[FFmpeg] 使用Sws_scale修改解码后分辨率,并使用Qt显示

概要

为了对齐CPU及优化等原因,导致frame的linesize[0]的实际宽大于理论宽,所以需要使用sws_scale函数进行转换。函数作用如下:

1.图像色彩空间转换,如:YUV420P->YUV422/RGB24

2.分辨率缩放/切换

3.前后图像滤波

整体架构流程


 

代码

av_register_all();
AVFormatContext *inpFormatCtx = NULL;
const char* inFilename  = "/home/16THDD/xieyingbo/xieyingbo/big_buck_bunny.mp4";
const char* outFilename = "/home/16THDD/xieyingbo/xieyingbo/out.yuv";
int destwidth = 0, heightwidth = 0;

//1.打开流媒体文件
int ret = avformat_open_input(&inpFormatCtx, inFilename, NULL, NULL);
if(ret != 0)
{
    printf("%s\n", "open input format error");
    return;
}

//2.过去输入流信息
ret = avformat_find_stream_info(inpFormatCtx, NULL);
if(ret < 0)
{
    printf("%s\n", "find stream error");
    return;
}

//3.获取视频流文件
int videoIndex = av_find_best_stream(inpFormatCtx, AVMEDIA_TYPE_VIDEO, -1,-1, NULL, 0);
if(videoIndex < 0)
{
    printf("%s\n", "find best stream error");
    return;
}

//4.创建解码器上下文
AVCodeContext *codecCtx = avcodec_alloc_context3(NULL);
if(codecCtx == NULL)
{
    printf("%s\n", "avcoec alloc codecCtx error");
    return;
}

//5.获取输入的解码器参数
ret = avcodec_parameters_to_context(codecCtx, inpFormatCtx->streams[videoIndex]->codecpar);
if(ret < 0)
{
    printf("%s\n", "avcoec parameter to codecCtx error");
    return;
}

//6.找到当前解码器
AVCodec *avcodec = avcodec_find_decoder(codecCtx->codec_id);
if(avcodec == NULL)
{
    printf("%s\n", "avcoec find decoder error");
    return;  
}

//7.打开当前解码器
ret = avcodec_open2(codecCtx, avcodec, NULL);
if(ret != 0)
{
    printf("%s\n", "avcoec open2 error");
    return;  
}

//8.获取输入文件分辨率
destwidth = 640, destheight = 360;

//9.初始化视频格式转换上下文
#if 1
    //修改分辨率为640 X 360的yuv420格式
    enum AVPixelFormat destFormat = codecCtx->pix_fmt;    //输出的颜色空间格式
    SwsContext *swsCtx = sws_getContext(destwidth, destheight, destFormat, 640, 360, destFormat, SWS_FAST_BILINEAR, NULL, NULL, NULL);
//输入宽,输入高,输入类型,输出宽,输出高,输出类型,使用图像拉伸缩放算法,输入滤波算法,输出滤波算法
#else
    //修改分辨率为640 X 360的RGB24格式
    enum AVPixelFormat destFormat = AV_PIX_FMT_RGB24;
    SwsContext *swsCtx = sws_getContext(destwidth, destheight, destFormat,             1920, 1080, destFormat, SWS_FAST_BILINEAR, NULL, NULL, NULL);
#endif


//10.创建出一块内存空间,用来保存转换后的数据
AVFrame *destframe = av_frame_alloc();

//11.获取转换后的空间大小
int size = av_image_get_buffer_size(codecCtx->pix_fmt, destwideth, destheight, 1);

//12.申请内存空间
uint8_t *outBuffer[4];
int linesize[4];
ret = av_image_alloc(outBuffer, linesize, destwidth, destheight, codecCtx->pix_fmt, 1);
if(ret < 0)
{
    printf("%s\n", "av_image_alloc error");
    return;  
}

//13.按照avframe的格式重新分配到创建的内存空间destframe中(格式化)
ret = av_image_copy_to_buffer(outBUffer[0], linesize[0], destframe->data, destframe->linesize,codecCtx->pix_fmt, destwidth, destheight, 1);
if(ret < 0)
{
    printf("%s\n", "av_image_copy_to_buffer error");
    return;
}

FILE *dst_fp = fopen(outFIlename, "wb+");
if(dst_fp == NULL)
{
    printf("%s\n", "open file outFIlename error");
    return;    
}

//14.循环读取源文件的帧数据
AVPacket packet;
av_init_packet(&packet);
AVFrame *frame = av_frame_alloc();
while(av_read_frame(inpFormatCtx >= 0))
{
    if(packet.stream_index == videoIndex)
    {
        //15.发送packet到解码器
        ret = avcodec_send_packet(codecCtx, &packet);
        if(ret != 0)
        {
            printf("%s\n", "avcodec_send_packet error");
            av_packet_unref(&packet);
            return;
        }
        av_packet_unref(&packet);

        //16.解码器回收解码后的数据
        while(avcodec_receive_frame(codecCtx, frame) == 0)
        {
            sws_scale(swsCtx, frame->data, frame->linesize, 0, codecCtx->height. destframe->data, destframe->linesize);

#if 1
    //17.存储数据(YUV420P)
    fwirte(destframe->data[0], 1, destwidth *destheight, dst_fp);    //存储Y   
    fwirte(destframe->data[1], 1, destwidth *destheight /4, dst_fp);    //存储U 
    fwirte(destframe->data[2], 1, destwidth *destheight /4, dst_fp);    //存储V  
#else
    //rgb24以打包的方式存在data[0]里面                
    fwrite(destframe->data[0], 1, destwidth * destheight * 3, dst_fp);
    //Qt上画出
    QImage image(destframe->data[0], destwidth, destheight, Format_RGB888);
    emit GetImage(image,head.type);    //发出信号
#endif
        }
        av_packet_unref(&packet);
    }
}

 

小结

在转为RGB24后,可以在qt中接收到信号,并画在ui界面中。
  • 9
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
你可以使用`ffmpeg`库来解码H.264视频并在Qt应用程序中显示。下面是一个基本的示例代码: ```cpp #include <QApplication> #include <QLabel> #include <QTimer> #include <QDebug> #include <QByteArray> extern "C" { #include <libavcodec/avcodec.h> #include <libavformat/avformat.h> #include <libswscale/swscale.h> } AVFormatContext *pFormatCtx = nullptr; AVCodecContext *pCodecCtx = nullptr; AVCodec *pCodec = nullptr; AVFrame *pFrame = nullptr; AVFrame *pFrameRGB = nullptr; uint8_t *buffer = nullptr; struct SwsContext *sws_ctx = nullptr; bool openVideo(const char *filename) { if (avformat_open_input(&pFormatCtx, filename, nullptr, nullptr) != 0) { qDebug() << "Could not open file"; return false; } if (avformat_find_stream_info(pFormatCtx, nullptr) < 0) { qDebug() << "Could not find stream information"; return false; } int videoStream = -1; for (unsigned int i = 0; i < pFormatCtx->nb_streams; i++) { if (pFormatCtx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) { videoStream = i; break; } } if (videoStream == -1) { qDebug() << "Could not find video stream"; return false; } pCodecCtx = avcodec_alloc_context3(nullptr); if (avcodec_parameters_to_context(pCodecCtx, pFormatCtx->streams[videoStream]->codecpar) != 0) { qDebug() << "Could not initialize codec context"; return false; } pCodec = avcodec_find_decoder(pCodecCtx->codec_id); if (pCodec == nullptr) { qDebug() << "Codec not found"; return false; } if (avcodec_open2(pCodecCtx, pCodec, nullptr) < 0) { qDebug() << "Could not open codec"; return false; } pFrame = av_frame_alloc(); pFrameRGB = av_frame_alloc(); int numBytes = av_image_get_buffer_size(AV_PIX_FMT_RGB24, pCodecCtx->width, pCodecCtx->height, 1); buffer = new uint8_t[numBytes]; av_image_fill_arrays(pFrameRGB->data, pFrameRGB->linesize, buffer, AV_PIX_FMT_RGB24, pCodecCtx->width, pCodecCtx->height, 1); sws_ctx = sws_getContext(pCodecCtx->width, pCodecCtx->height, pCodecCtx->pix_fmt, pCodecCtx->width, pCodecCtx->height, AV_PIX_FMT_RGB24, SWS_BILINEAR, nullptr, nullptr, nullptr); return true; } void closeVideo() { if (sws_ctx != nullptr) sws_freeContext(sws_ctx); if (pFrameRGB != nullptr) av_frame_free(&pFrameRGB); if (pFrame != nullptr) av_frame_free(&pFrame); if (pCodecCtx != nullptr) avcodec_free_context(&pCodecCtx); if (pFormatCtx != nullptr) avformat_close_input(&pFormatCtx); if (buffer != nullptr) delete[] buffer; } int main(int argc, char *argv[]) { QApplication a(argc, argv); QLabel label; label.show(); if (!openVideo("path/to/your/video.mp4")) { qDebug() << "Failed to open video"; return -1; } QTimer timer; QObject::connect(&timer, &QTimer::timeout, [&]() { AVPacket packet; while (av_read_frame(pFormatCtx, &packet) >= 0) { if (packet.stream_index == pCodecCtx->video_stream_index) { avcodec_send_packet(pCodecCtx, &packet); while (avcodec_receive_frame(pCodecCtx, pFrame) == 0) { sws_scale(sws_ctx, pFrame->data, pFrame->linesize, 0, pCodecCtx->height, pFrameRGB->data, pFrameRGB->linesize); QImage img(pFrameRGB->data[0], pCodecCtx->width, pCodecCtx->height, QImage::Format_RGB888); label.setPixmap(QPixmap::fromImage(img)); label.adjustSize(); } } av_packet_unref(&packet); } }); timer.start(33); // 30 FPS int result = a.exec(); closeVideo(); return result; } ``` 你需要将路径 `path/to/your/video.mp4` 替换为你的H.264视频文件路径。这个示例会在一个Qt窗口中显示视频帧。请注意,你需要通过Qt的一些方法来显示图像,例如使用`QLabel`来显示 `QImage`。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值