YUV420P的像素数据编码为H.264的压缩编码数据

本文介绍一个最简单的基于FFMPEG的视频编码器。该编码器实现了YUV420P的像素数据编码为H.264的压缩编码数据。编码器代码十分简单,但是每一行代码都很重要,适合好好研究一下。弄清楚了本代码也就基本弄清楚了FFMPEG的编码流程。


下面附一张使用FFmpeg编码视频的流程图。使用该流程,不仅可以编码H.264的视频,而且可以编码MPEG4/MPEG2/VP8等等各种FFmpeg支持的视频。图中蓝色背景的函数是实际输出数据的函数。浅绿色的函数是视频编码的函数。


简单介绍一下流程中各个函数的意义:

av_register_all():注册FFmpeg所有编解码器。

avformat_alloc_output_context2():初始化输出码流的AVFormatContext。

avio_open():打开输出文件。

av_new_stream():创建输出码流的AVStream。

avcodec_find_encoder():查找编码器。

avcodec_open2():打开编码器。

avformat_write_header():写文件头(对于某些没有文件头的封装格式,不需要此函数。比如说MPEG2TS)。

avcodec_encode_video2():编码一帧视频。即将AVFrame(存储YUV像素数据)编码为AVPacket(存储H.264等格式的码流数据)。

av_write_frame():将编码后的视频码流写入文件。

flush_encoder():输入的像素数据读取完成后调用此函数。用于输出编码器中剩余的AVPacket。

av_write_trailer():写文件尾(对于某些没有文件头的封装格式,不需要此函数。比如说MPEG2TS)。

 

  1.  
  2.  * 本程序实现了YUV像素数据编码为视频码流(H264,MPEG2,VP8等等)。 
  3.  * 是最简单的FFmpeg视频编码方面的教程。 
  4.  * 通过学习本例子可以了解FFmpeg的编码流程。 
  5.  * This software encode YUV420P data to H.264 bitstream. 
  6.  * It's the simplest video encoding software based on FFmpeg.  
  7.  * Suitable for beginner of FFmpeg  
  8.  */  
  9.   
  10. #include <stdio.h>  
  11.   
  12. #define __STDC_CONSTANT_MACROS  
  13.   
  14. #ifdef _WIN32  
  15. //Windows  
  16. extern "C"  
  17. {  
  18. #include "libavutil/opt.h"  
  19. #include "libavcodec/avcodec.h"  
  20. #include "libavformat/avformat.h"  
  21. };  
  22. #else  
  23. //Linux...  
  24. #ifdef __cplusplus  
  25. extern "C"  
  26. {  
  27. #endif  
  28. #include <libavutil/opt.h>  
  29. #include <libavcodec/avcodec.h>  
  30. #include <libavformat/avformat.h>  
  31. #ifdef __cplusplus  
  32. };  
  33. #endif  
  34. #endif  
  35.   
  36.   
  37. int flush_encoder(AVFormatContext *fmt_ctx,unsigned int stream_index){  
  38.     int ret;  
  39.     int got_frame;  
  40.     AVPacket enc_pkt;  
  41.     if (!(fmt_ctx->streams[stream_index]->codec->codec->capabilities &  
  42.         CODEC_CAP_DELAY))  
  43.         return 0;  
  44.     while (1) {  
  45.         enc_pkt.data = NULL;  
  46.         enc_pkt.size = 0;  
  47.         av_init_packet(&enc_pkt);  
  48.         ret = avcodec_encode_video2 (fmt_ctx->streams[stream_index]->codec, &enc_pkt,  
  49.             NULL, &got_frame);  
  50.         av_frame_free(NULL);  
  51.         if (ret < 0)  
  52.             break;  
  53.         if (!got_frame){  
  54.             ret=0;  
  55.             break;  
  56.         }  
  57.         printf("Flush Encoder: Succeed to encode 1 frame!\tsize:%5d\n",enc_pkt.size);  
  58.         /* mux encoded frame */  
  59.         ret = av_write_frame(fmt_ctx, &enc_pkt);  
  60.         if (ret < 0)  
  61.             break;  
  62.     }  
  63.     return ret;  
  64. }  
  65.   
  66. int main(int argc, char* argv[])  
  67. {  
  68.     AVFormatContext* pFormatCtx;  
  69.     AVOutputFormat* fmt;  
  70.     AVStream* video_st;  
  71.     AVCodecContext* pCodecCtx;  
  72.     AVCodec* pCodec;  
  73.     AVPacket pkt;  
  74.     uint8_t* picture_buf;  
  75.     AVFrame* pFrame;  
  76.     int picture_size;  
  77.     int y_size;  
  78.     int framecnt=0;  
  79.     //FILE *in_file = fopen("src01_480x272.yuv", "rb"); //Input raw YUV data   
  80.     FILE *in_file = fopen("../ds_480x272.yuv""rb");   //Input raw YUV data  
  81.     int in_w=480,in_h=272;                              //Input data's width and height  
  82.     int framenum=100;                                   //Frames to encode  
  83.     //const char* out_file = "src01.h264";              //Output Filepath   
  84.     //const char* out_file = "src01.ts";  
  85.     //const char* out_file = "src01.hevc";  
  86.     const char* out_file = "ds.h264";  
  87.   
  88.     av_register_all();  
  89.     //Method1.  
  90.     pFormatCtx = avformat_alloc_context();  
  91.     //Guess Format  
  92.     fmt = av_guess_format(NULL, out_file, NULL);  
  93.     pFormatCtx->oformat = fmt;  
  94.       
  95.     //Method 2.  
  96.     //avformat_alloc_output_context2(&pFormatCtx, NULL, NULL, out_file);  
  97.     //fmt = pFormatCtx->oformat;  
  98.   
  99.   
  100.     //Open output URL  
  101.     if (avio_open(&pFormatCtx->pb,out_file, AVIO_FLAG_READ_WRITE) < 0){  
  102.         printf("Failed to open output file! \n");  
  103.         return -1;  
  104.     }  
  105.   
  106.     video_st = avformat_new_stream(pFormatCtx, 0);  
  107.     //video_st->time_base.num = 1;   
  108.     //video_st->time_base.den = 25;    
  109.   
  110.     if (video_st==NULL){  
  111.         return -1;  
  112.     }  
  113.     //Param that must set  
  114.     pCodecCtx = video_st->codec;  
  115.     //pCodecCtx->codec_id =AV_CODEC_ID_HEVC;  
  116.     pCodecCtx->codec_id = fmt->video_codec;  
  117.     pCodecCtx->codec_type = AVMEDIA_TYPE_VIDEO;  
  118.     pCodecCtx->pix_fmt = AV_PIX_FMT_YUV420P;  
  119.     pCodecCtx->width = in_w;    
  120.     pCodecCtx->height = in_h;  
  121.     pCodecCtx->bit_rate = 400000;    
  122.     pCodecCtx->gop_size=250;  
  123.   
  124.     pCodecCtx->time_base.num = 1;    
  125.     pCodecCtx->time_base.den = 25;    
  126.   
  127.     //H264  
  128.     //pCodecCtx->me_range = 16;  
  129.     //pCodecCtx->max_qdiff = 4;  
  130.     //pCodecCtx->qcompress = 0.6;  
  131.     pCodecCtx->qmin = 10;  
  132.     pCodecCtx->qmax = 51;  
  133.   
  134.     //Optional Param  
  135.     pCodecCtx->max_b_frames=3;  
  136.   
  137.     // Set Option  
  138.     AVDictionary *param = 0;  
  139.     //H.264  
  140.     if(pCodecCtx->codec_id == AV_CODEC_ID_H264) {  
  141.         av_dict_set(¶m, "preset""slow", 0);  
  142.         av_dict_set(¶m, "tune""zerolatency", 0);  
  143.         //av_dict_set(¶m, "profile", "main", 0);  
  144.     }  
  145.     //H.265  
  146.     if(pCodecCtx->codec_id == AV_CODEC_ID_H265){  
  147.         av_dict_set(¶m, "preset""ultrafast", 0);  
  148.         av_dict_set(¶m, "tune""zero-latency", 0);  
  149.     }  
  150.   
  151.     //Show some Information  
  152.     av_dump_format(pFormatCtx, 0, out_file, 1);  
  153.   
  154.     pCodec = avcodec_find_encoder(pCodecCtx->codec_id);  
  155.     if (!pCodec){  
  156.         printf("Can not find encoder! \n");  
  157.         return -1;  
  158.     }  
  159.     if (avcodec_open2(pCodecCtx, pCodec,¶m) < 0){  
  160.         printf("Failed to open encoder! \n");  
  161.         return -1;  
  162.     }  
  163.   
  164.   
  165.     pFrame = av_frame_alloc();  
  166.     picture_size = avpicture_get_size(pCodecCtx->pix_fmt, pCodecCtx->width, pCodecCtx->height);  
  167.     picture_buf = (uint8_t *)av_malloc(picture_size);  
  168.     avpicture_fill((AVPicture *)pFrame, picture_buf, pCodecCtx->pix_fmt, pCodecCtx->width, pCodecCtx->height);  
  169.   
  170.     //Write File Header  
  171.     avformat_write_header(pFormatCtx,NULL);  
  172.   
  173.     av_new_packet(&pkt,picture_size);  
  174.   
  175.     y_size = pCodecCtx->width * pCodecCtx->height;  
  176.   
  177.     for (int i=0; i<framenum; i++){  
  178.         //Read raw YUV data  
  179.         if (fread(picture_buf, 1, y_size*3/2, in_file) <= 0){  
  180.             printf("Failed to read raw data! \n");  
  181.             return -1;  
  182.         }else if(feof(in_file)){  
  183.             break;  
  184.         }  
  185.         pFrame->data[0] = picture_buf;              // Y  
  186.         pFrame->data[1] = picture_buf+ y_size;      // U   
  187.         pFrame->data[2] = picture_buf+ y_size*5/4;  // V  
  188.         //PTS  
  189.         //pFrame->pts=i;  
  190.         pFrame->pts=i*(video_st->time_base.den)/((video_st->time_base.num)*25);  
  191.         int got_picture=0;  
  192.         //Encode  
  193.         int ret = avcodec_encode_video2(pCodecCtx, &pkt,pFrame, &got_picture);  
  194.         if(ret < 0){  
  195.             printf("Failed to encode! \n");  
  196.             return -1;  
  197.         }  
  198.         if (got_picture==1){  
  199.             printf("Succeed to encode frame: %5d\tsize:%5d\n",framecnt,pkt.size);  
  200.             framecnt++;  
  201.             pkt.stream_index = video_st->index;  
  202.             ret = av_write_frame(pFormatCtx, &pkt);  
  203.             av_free_packet(&pkt);  
  204.         }  
  205.     }  
  206.     //Flush Encoder  
  207.     int ret = flush_encoder(pFormatCtx,0);  
  208.     if (ret < 0) {  
  209.         printf("Flushing encoder failed\n");  
  210.         return -1;  
  211.     }  
  212.   
  213.     //Write file trailer  
  214.     av_write_trailer(pFormatCtx);  
  215.   
  216.     //Clean  
  217.     if (video_st){  
  218.         avcodec_close(video_st->codec);  
  219.         av_free(pFrame);  
  220.         av_free(picture_buf);  
  221.     }  
  222.     avio_close(pFormatCtx->pb);  
  223.     avformat_free_context(pFormatCtx);  
  224.   
  225.     fclose(in_file);  
  226.   
  227.     return 0;  
  • 4
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
提供的源码资源涵盖了安卓应用、小程序、Python应用和Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值