实现最小YUV视频解码器

2021-05-29  本文已影响0人  北极星的笔记

本文记录了使用ffmpeg进行视频解码的最小解码器代码,通过这个小程序可以理解ffmpeg的解码过程及用到的api。这里将解码码后得到的YUV格式的视频保存到文件中,可以使用YUV Playe播放。代码已上传git,
https://github.com/beijixing/ffmpegStudy

流程图.jpg

extern "C"
{
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libavutil/opt.h>
#include <libavutil/imgutils.h>
#include <libswscale/swscale.h>
}

void devoder()
{
    //注册编解码器
    av_register_all();

    AVFormatContext *pAVFormatCtx = NULL;
    int ret = avformat_open_input(&pAVFormatCtx,"Titanic.mkv",NULL, NULL);
    if(ret != 0)
    {
        printf("avformat_open_input failed!");
        return;
    }

    //查找流信息
    ret = avformat_find_stream_info(pAVFormatCtx, NULL);
    if(ret != 0)
    {
        printf("avformat_find_stream_info failed!");
        return;
    }

    //查找视频流索引
   int videoIndex = -1;
   videoIndex = av_find_best_stream(pAVFormatCtx, AVMEDIA_TYPE_VIDEO, -1, -1, NULL, NULL);
   if(videoIndex < 0)
   {
       printf("av_find_best_stream failed!");
       return;
   }

   //查找解码器上下文
   AVCodecContext *pAVCodecCtx = pAVFormatCtx->streams[videoIndex]->codec;
   //查找解码器
   AVCodec *pAVCodec = avcodec_find_decoder(pAVCodecCtx->codec_id);

   if(pAVCodec == NULL)
   {
       printf("Codec not found.\n");
       return;
   }
    //打开解码器
   ret = avcodec_open2(pAVCodecCtx, pAVCodec, NULL);
   if(ret < 0)
   {
       printf("avcodec_open2 failed !\n");
       return;
   }

   //解码视频
   AVFrame *pAVFrame = av_frame_alloc();
   AVFrame *pAVFrameYUV = av_frame_alloc();

   //创建缓存
   int nBuffSize = av_image_get_buffer_size(AV_PIX_FMT_YUV420P, pAVCodecCtx->width, pAVCodecCtx->height, 1);
   unsigned char* buffer = (unsigned char *)av_malloc(nBuffSize);
   //设置缓存
   av_image_fill_arrays(pAVFrameYUV->data, pAVFrameYUV->linesize, buffer,
                        AV_PIX_FMT_YUV420P, pAVCodecCtx->width,
                        pAVCodecCtx->height, 1);
   //创建AVPacket
   AVPacket *pAVPacket = (AVPacket *)av_malloc(sizeof (AVPacket));
   //创建图像转换上下文
   SwsContext *pSwsCtx = sws_getContext(pAVCodecCtx->width, pAVCodecCtx->height,
                                       pAVCodecCtx->pix_fmt, pAVCodecCtx->width, pAVCodecCtx->height,
                                        AV_PIX_FMT_YUV420P, SWS_BICUBIC, NULL, NULL, NULL);


   int gotPicture = 0;
   FILE* pYuvFile = fopen("test.yuv", "wb");
   if(!pYuvFile)
   {
       printf("fopen test.yuv failed !\n");
       return;
   }

   int frameCnt = 0;
   while (av_read_frame(pAVFormatCtx, pAVPacket) >= 0)
   {
       //只解码视频
       if(pAVPacket->stream_index == videoIndex)
       {
            ret = avcodec_decode_video2(pAVCodecCtx, pAVFrame, &gotPicture, pAVPacket);
            if(ret < 0)
            {
                printf("avcodec_decode_video2 failed !\n");
                break;
            }
            if(gotPicture)
            {
                //格式转换
                sws_scale(pSwsCtx, pAVFrame->data,
                          pAVFrame->linesize,
                          0,
                          pAVCodecCtx->height,
                          pAVFrameYUV->data,
                          pAVFrameYUV->linesize);
                fwrite(pAVFrameYUV->data[0],1,pAVCodecCtx->width*pAVCodecCtx->height, pYuvFile);
                fwrite(pAVFrameYUV->data[1],1,pAVCodecCtx->width*pAVCodecCtx->height/4, pYuvFile);
                fwrite(pAVFrameYUV->data[2],1,pAVCodecCtx->width*pAVCodecCtx->height/4, pYuvFile);
                frameCnt++;
                printf("frameCnt = %d !\n", frameCnt);
            }

       }
   }

   //释放指针
   fclose(pYuvFile);
   sws_freeContext(pSwsCtx);
   av_frame_free(&pAVFrame);
   av_frame_free(&pAVFrameYUV);
   av_packet_free(&pAVPacket);
   avformat_close_input(&pAVFormatCtx);
}


int main(int argc, char *argv[])
{
    devoder();
    return 0;
}
上一篇下一篇

猜你喜欢

热点阅读