FFmpeg——Windows下,视频播放器2:视频解码、转码

  FFmpeg解码的流程图如下所示

这里写图片描述


#include "czyplayer.h"
#include <QtWidgets/QApplication>

//封装格式
#pragma comment(lib, "avformat.lib")
//工具(如错误信息)
#pragma comment(lib, "avutil.lib")
//解码
#pragma comment(lib, "avcodec.lib")
//缩放
#pragma comment(lib, "swscale.lib")
extern "C"
{
    #include <libavformat/avformat.h>
    #include <libswscale/swscale.h>
}

static double r2d(AVRational r)
{
    return r.num == 0 || r.den == 0 ? 0. : (double)r.num / (double)r.den;
}

  按照ffmpeg的解码流程进行注册

int main(int argc, char *argv[])
{
    //1.注册组件
    av_register_all();
    char *path = "video.mp4";
    //封装格式上下文 (存放视频、或者流信息)
    AVFormatContext *ic = NULL; 

    //2.打开输入视频文件 (0 成功)
    int re = avformat_open_input(&ic, path, 0, 0);  // lib: avformat
    if (re != 0)
    {
        char buf[1024] = { 0 };             //存放错误信息  (文件不存在,格式不支持)
        av_strerror(re, buf, sizeof(buf));  // lib: avutil
        printf("open %s failed: %s\n",path, buf);
        return -1;
    }

  将视频时间戳换算为毫秒

    int totalSec = ic->duration / AV_TIME_BASE;
    printf("file total Sec is %d : %d \n", totalSec / 60 , totalSec % 60);

  遍历AVStream找到对应的数据索引

  这里写图片描述

    //视频解码,需要找到对应的AVStream所在的pFormatCtx->streams的索引位置
    //nb_streams: 包含流的数量,视频流、音频流、字幕流
    int videoStream = 0;
    AVCodecContext *videoCtx = NULL;    
    for (int i = 0; i < ic->nb_streams; i++)
    {
        AVCodecContext *enc = ic->streams[i]->codec;
        //3.根据类型判断是否是视频流
        if (enc->codec_type == AVMEDIA_TYPE_VIDEO)
        {

            videoCtx = enc; //根据索引拿到对应的流,根据流拿到解码器上下文
            videoStream = i;    

  FFmpeg解码的数据结构如下所示:根据AVStream获取对应的解码器

这里写图片描述

            //4.获取解码器. 根据上下文拿到编解码id, 通过该id拿到解码器
            AVCodec *codec = avcodec_find_decoder(enc->codec_id);   
            if (!codec)
            {
                printf("video code not find! \n");  
            }

            //5.打开解码器
            int err = avcodec_open2(enc, codec, NULL);
            if (err != 0)
            {
                char buf[1024] = { 0 };
                av_strerror(err, buf, sizeof(buf));
                printf(buf);
                return -2;
            }
            printf("open codec success!\n");
        }
    }

    //像素数据(解码数据)
    AVFrame *yuv = av_frame_alloc();

    int outwidth = 640;
    int outheight = 480;
    SwsContext *cCtx = NULL;

    char *rgb = new char[outwidth * outheight * 4]; 


    for (;;)                                
    {
        AVPacket pkt;   
        //6.一帧一帧读取压缩的视频数据AVPacket
        re = av_read_frame(ic, &pkt);
        if (re != 0)    break;
        if (pkt.stream_index != videoStream)
        {
            av_packet_unref(&pkt);
            continue;
        }
        int pts = pkt.pts * r2d(ic->streams[pkt.stream_index]->time_base) * 1000;

        //7.1ffmpeg新版本方法: 解码
        int re = avcodec_send_packet(videoCtx, &pkt);
        if (re != 0)
        {
            av_packet_unref(&pkt);
            continue;
        }
        //7.2
        re = avcodec_receive_frame(videoCtx, yuv);
        if (re != 0)
        {
            av_packet_unref(&pkt);
            continue;
        }

        printf("[decode success!] ");           //解码成功

  转码 yuv->rgb


        //解码出来的是yuv, 但是显示需要rgb    yuv->rgb              
        //解码出来的视频大小, 不一定是显示的大小, 窗口会放大、缩小(这个缩放虽然可以在客户端转换, 但消耗资源, 可以一举两得, 在这里就做缩放处理)

        //8.获取转码器           lib: swscale
        cCtx = sws_getCachedContext(cCtx, videoCtx->width,
            videoCtx->height,
            videoCtx->pix_fmt,  
            outwidth,
            outheight,
            AV_PIX_FMT_BGRA,
            SWS_BICUBIC,            //转码用什么算法
            NULL, NULL, NULL
            );
        if (!cCtx)
        {
            printf("sws_gegCachedContext failed!\n");
            break;
        }

        uint8_t *data[AV_NUM_DATA_POINTERS] = { 0 };
        data[0] = (uint8_t *)rgb;
        int linesize[AV_NUM_DATA_POINTERS] = { 0 };
        linesize[0] = outwidth * 4;     //一行的大小, 宽 * 4
        //9.转码YUV->RGB8888 (h: 转码后的高度)
        int h = sws_scale(cCtx, yuv->data, yuv->linesize, 0, videoCtx->height,  
            data,
            linesize);

        if (h > 0)
        {
            printf("(h:%d) ", h);
        }

        printf("pts = %d \n", pts);         //利用pts来控制进度    (pts如果不断往上加, 表示读取视频正常了)

  释放资源

        av_packet_unref(&pkt);              //清理空间      lib: avcodec

    }

    if (cCtx)   //释放转码器
    {
        sws_freeContext(cCtx);
        cCtx = NULL;
    }

    //写ffmpeg的时候,结对编程,有申请空间,就要释放空间
    avformat_close_input(&ic);              //在读视频后,关闭
    ic = NULL;

    QApplication a(argc, argv);
    CzyPlayer w;
    w.show();
    return a.exec();
}

  以上代码牵扯到的数据结构解释

这里写图片描述

  程序运行结果

  这里写图片描述


  源码下载  密码:mpyb

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值