美文网首页
实现最小YUV视频解码器

实现最小YUV视频解码器

作者: 北极星的笔记 | 来源:发表于2021-05-29 23:25 被阅读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;
    }
    

    相关文章

      网友评论

          本文标题:实现最小YUV视频解码器

          本文链接:https://www.haomeiwen.com/subject/hvmusltx.html