FFmpeg零基础学习(三)——视频分辨率更改

这篇具有很好参考价值的文章主要介绍了FFmpeg零基础学习(三)——视频分辨率更改。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

前言

本篇文章的需求为:
1、输入视频文件的路径。
2、输入要输出的视频的分辨率。
3、将视频文件转为指定分辨率的视频。

当前进展:目前仅将数据解码出,且使用sws_scale 更改为其他分辨率的AVFrame,但将该帧编码成视频的方式,目前还未成功。后续完善。

正文

一、将视频的每一帧重编辑为指定的格式及宽高

code

void CFFmpegDemoTest::on_btnChangePix_clicked()
{
    qDebug() << "--> CFFmpegDemoTest::on_btnChangePix_clicked Start";
    int ret = 0; int err;
    int read_end = 0;
    int frame_num = 0;

    AVPacket *pkt = av_packet_alloc();
    AVFrame *frame = av_frame_alloc();

    //打开输入文件
    char filename[] = "juren-30s.mp4";
    struct SwsContext* img_convert_ctx = NULL;
    AVFormatContext *fmt_ctx = avformat_alloc_context();
    if (!fmt_ctx) {
        qDebug() << "fmt_ctx error code %d " << AVERROR(ENOMEM);
        return;
    }
    if((err = avformat_open_input(&fmt_ctx, filename,NULL,NULL)) < 0){
        qDebug() << "can not open file:"<<err;
        return;
    }

    //打开解码器
    AVCodecContext *avctx = avcodec_alloc_context3(NULL);
    ret = avcodec_parameters_to_context(avctx, fmt_ctx->streams[0]->codecpar);

    if (ret < 0)
    {
        qDebug() << " avcodec_parameters_to_contexterror code:" << ret;
        return;
    }
    AVCodec *codec = avcodec_find_decoder(avctx->codec_id);
    if ((ret = avcodec_open2(avctx, codec, NULL)) < 0) {
        qDebug() << "open codec faile :"<<ret;
        return;
    }

    int sws_flags = SWS_BICUBIC;
    int result_format = AV_PIX_FMT_BGRA;
    int result_width = 300;
    int result_height = 300;

    //确定内存的大小
    int buf_size = av_image_get_buffer_size(static_cast<AVPixelFormat>(result_format), result_width, result_height, 1);

    for(;;)
    {
        if( 1 == read_end ){
            break;
        }

        ret = av_read_frame(fmt_ctx, pkt);
        //跳过不处理音频包
        if( 1 == pkt->stream_index)
        {
            av_packet_unref(pkt);
            continue;
        }

        if ( AVERROR_EOF == ret)
        {
            //读取完文件,这时候 pkt 的 data 跟 size 应该是 null
            avcodec_send_packet(avctx, NULL);
        }
        else
        {
            if( 0 != ret)
            {
                qDebug() << "read error code:" << ret;
                return;
            }else{
                retry:
                if (avcodec_send_packet(avctx, pkt) == AVERROR(EAGAIN)) {
                    qDebug() << "Receive_frame and send_packet both returned EAGAIN, which is an API violation";
                    //这里可以考虑休眠 0.1 秒,返回 EAGAIN 通常是 ffmpeg 的内部 api 有bug
                    goto retry;
                }
                //释放 pkt 里面的编码数据
                av_packet_unref(pkt);
            }

        }

        //循环不断从解码器读数据,直到没有数据可读。
        for(;;){
            //读取 AVFrame
            ret = avcodec_receive_frame(avctx, frame);
            /* 释放 frame 里面的YUV数据,
             * 由于 avcodec_receive_frame 函数里面会调用 av_frame_unref,所以下面的代码可以注释。
             * 所以我们不需要 手动 unref 这个 AVFrame
             * */
            //av_frame_unref(frame);

            if( AVERROR(EAGAIN) == ret )
            {
                //提示 EAGAIN 代表 解码器 需要 更多的 AVPacket
                //跳出 第一层 for,让 解码器拿到更多的 AVPacket
                break;
            }
            else if( AVERROR_EOF == ret )
            {
                /* 提示 AVERROR_EOF 代表之前已经往 解码器发送了一个 data 跟 size 都是 NULL 的 AVPacket
                 * 发送 NULL 的 AVPacket 是提示解码器把所有的缓存帧全都刷出来。
                 * 通常只有在 读完输入文件才会发送 NULL 的 AVPacket,或者需要用现有的解码器解码另一个的视频流才会这么干。
                 * */

                //跳出 第二层 for,文件已经解码完毕。
                read_end = 1;
                break;
            }
            else if( ret >= 0 )
            {
                if( NULL == img_convert_ctx )
                {
                    img_convert_ctx = sws_getCachedContext(img_convert_ctx,
                                                           frame->width, frame->height, static_cast<AVPixelFormat>(frame->format),
                                                           result_width, result_height, static_cast<AVPixelFormat>(result_format),
                                                           sws_flags, NULL, NULL, NULL);
                    if (NULL == img_convert_ctx) {
                        av_log(NULL, AV_LOG_FATAL, "no memory 1\n");
                        return;
                    }
                }

                //申请内存
                uint8_t* buffer = (uint8_t *)av_malloc(buf_size);
                if( !buffer ){
                    av_log(NULL, AV_LOG_FATAL, "no memory 2\n");
                    return;
                }
                uint8_t *pixels[4];
                int pitch[4];
                //把内存映射到数组里面
                av_image_fill_arrays(pixels, pitch, buffer, static_cast<AVPixelFormat>(result_format), result_width, result_height, 1);

                sws_scale(img_convert_ctx,
                          (const uint8_t * const *)frame->data, frame->linesize, 0, frame->height,
                          pixels,pitch);

                //处理%20 ==0 的帧
                if (frame_num % 20 == 0)
                {
                    Save_rgb_to_file(pixels, pitch, result_height, frame_num);
                }
                //减少引用,释放内存。
                av_frame_unref(frame);
                av_freep(&buffer);

                frame_num++;

            }
            else
            {
                av_log(NULL, AV_LOG_FATAL, "other fail \n");
                qDebug() << "other fail";
                return;
            }
        }
    }

    av_frame_free(&frame);
    av_packet_free(&pkt);

    sws_freeContext(img_convert_ctx);
    img_convert_ctx = NULL;

    //关闭编码器,解码器。
    avcodec_close(avctx);

    //释放容器内存。
    avformat_free_context(fmt_ctx);
    qDebug() <<"done \n";
    return;
}

END、总结的知识与问题

1、avcodec_send_frame和avcodec_receive_packet的关系

1、编码器和解码器都维护了一个缓冲区,在刚开始输入数据时,需要多输入几帧,等缓冲区被填充满后,才会在receive端接收到编码或解码后的数据。

2、存在AVPacket中的数据不一定是一帧(比如音频的数据可能1个AVPacket包含1s的数据,帧率为25的话,就包含25帧),但存在AVFrame中的是一帧数据,所以avcodec_send_packet和avcodec_receive_frame不一定一一对应,调用一次avcodec_send_packet后,可能需要多次调用avcodec_receive_frame。

3、frame只需要分配对象本身空间就好,frame->data的空间并不需要分配。函数会判断是否已经给frame->data分配空间,如果没有分配会在函数内部为frame->data分配合适的空间。

4、avcodec_receive_frame:send端只把数据放入缓冲区,recive端才是解码并获得数据的函数,如果receive发现已有解码后的数据则直接获取,如果没有则开始解码。

5、数据遗留:在最后应该向avcodec_send_frame(enc_ctx, NULL)传入NULL数据,这样编码器知道后面不会再有数据,就把放在缓冲区中的数据,全部编码并通过avcodec_receive_packet输出出来。

6、返回值:EAGAIN,意思是需要输入更多的数据,才会有新的数据编码后的数据返回。AVERROR_EOF在从文件中读取数据推流时,出现这个错误是因为文件内的数据读完了。

7、参数内存:newpkt->data并不需要分配数据空间,只需要给newpkt本身结构体分配空间就好。
参考:avcodec_send_frame和avcodec_receive_packet

2、Input picture width (640) is greater than stride (200)

问题: 在调用sws_scale 时,一定传入的目的图像的宽高小于输入图像的宽高时,则出现该问题。

参考

1、avcodec_send_frame和avcodec_receive_packet
2、FFmpeg Basics 中文文档-命令行教程文章来源地址https://www.toymoban.com/news/detail-812539.html

到了这里,关于FFmpeg零基础学习(三)——视频分辨率更改的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • opencv和ffmpeg调整视频分辨率两种方法介绍

     咳咳,为了调整学习资料的分辨率,我花了很多时间,嗯,效果一般。就是图个乐子。   opencv确实是个不错的软件,但可惜我不太懂调整颜色色差,对比度这些东西,但是还是贴上代码吧。     ffmpeg有python版本和exe版本,为了节省时间,我就使用exe版本了。ffmpeg使用前需要

    2024年02月09日
    浏览(35)
  • 使用ffmpeg调整视频分辨率/帧率并保持高清晰度

    通过ffmpeg -i命令查看视频基本信息 通过命令查看,原始视频信息 分辨率为4096x2160,码率214092k, 帧率50 -qscale value:使用固定的视频量化标度(VBR),以value质量为基础的VBR,取值0.01-255,越小质量越好 -q:v:表示存储jpeg的图像质量 -b:v:设置输出文件的视频比特率(码率) -c:a:

    2024年02月02日
    浏览(59)
  • 【FFmpeg】ffmpeg 命令行参数 ⑧ ( 使用 ffmpeg 转换封装格式 | 音视频编解码器参数设置 | 视频 帧率 / 码率 / 分辨率 设置 | 音频 码率 / 采样率 设置 )

    音视频 文件 从 采样 - 处理 - 得到原始数据帧队列 - 音视频编码 - 音视频包队列 - 格式封装 的过程如下 : 封装格式 参考 【音视频原理】音视频 “ 采样 - 编码 - 封装 过程 “ 和 “ 解封装 - 解码 - 播放 过程 “ 分析 ( 视频采集处理流程 | 音频采集处理流程 | 音视频文件解封装

    2024年04月17日
    浏览(63)
  • iphone更改照片分辨率?手机怎样修改图片分辨率?

    现在手机越来越智能,已经能替代电脑完成很多平时的工作,而且手机携带也更加方便,例如现在很多的考试网上报名就可以通过手机来完成,但最近有很多小伙伴来咨询提交证件照时为什么总是显示上传失败无法识别?其实这种问题一般是由于提交的证件照的分辨率不符合

    2024年02月11日
    浏览(51)
  • 视频基础知识——编码时,帧率、码率、分辨率之间的联系

    本文主要补充基础概念,在设置编码器的时候,参数设置不对,录制的屏幕就会出现花屏。所以梳理了基础知识,就知道哪里设置不对了。 如下是ffmpeg自带的sample,encode_video的例子,选取H264的编码器,进行编码。 其中,c-bit_rate = 400 000; 一直不知道怎么来的,导致录制屏幕花

    2024年02月10日
    浏览(43)
  • VMware虚拟机更改Mac系统的屏幕分辨率

    安装VMware Tools 在虚拟机设置中将CD/DVD指定为 darwin.iso 链接:https://pan.baidu.com/s/112-SeQ1YNUcRK_RsrP-6ZQ  提取码:o8iw  --来自百度网盘超级会员V5的分享 开机安装  VMware Tools 2. 终端输入命令 Mac系统重启后找到启动台--其他--终端 输入指令 sudo /Library/Application Support/VMware Tools/vmware

    2024年02月13日
    浏览(47)
  • 【视频超分辨率】视频超分辨率的介绍(定义,评价指标,分类)

    视频超分率起源于图像超分率,旨在根据已有的低分辨率视频序列生成具有真实细节和内容连续的高分辨率视频序列。视频超分辨率技术可以将 低分辨率(低清晰度)视频转换为高分辨率(高清晰度)视频 ,以提供更多的细节和清晰度。 视频超分辨率技术主要分为 传统方法

    2024年02月04日
    浏览(37)
  • Python获取视频分辨率大小

    可以使用 Python 的 cv2 库来获取视频的分辨率大小。具体来说,需要执行以下步骤: 1、首先,你需要安装 cv2 库。你可以使用 pip 安装: 2、然后,需要导入 cv2 库并打开视频文件。就可以使用 cv2.VideoCapture 函数来打开视频文件: 3、接着,可以使用 cv2.VideoCapture.get 函数获取视

    2024年02月15日
    浏览(48)
  • 视频分辨率/帧率/码率选择参考

    1. 视频码率与分辨率的参考表     1080*720的分辨率,用5000K左右;     720*576的分辨率,用3500K左右;     640*480的分辨率,用1500K左右。 2. 计算公式 基本算法:码率(kbps)=文件大小(KB)*8/时间(秒) 举例:视频文件的容量为3.446G,视频长度100分钟(6000秒), 计

    2024年02月06日
    浏览(29)
  • java获取视频时长、分辨率、帧率、码率

    1、先导包 2、获取时长

    2024年02月15日
    浏览(45)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包