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

来源:互联网 发布:淘宝店铺导航全屏代码 编辑:程序博客网 时间:2024/06/14 09:36

  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 0
原创粉丝点击