最简单的基于FFmpeg的视频编码器-更新版(YUV编码为HEVC(H.265))

前一阵子做过一个基于FFmpeg的视频编码器的例子:

最简单的基于FFMPEG的视频编码器(YUV编码为H.264)
在该例子中,可以将YUV像素数据(YUV420P)编码为H.264码流。因为如今FFmpeg已经实现了对libx265的支持,因此对上述编码H.264的例子进行了升级,使之变成编码H.265(HEVC)的例子。
比较早的FFmpeg的类库(大约几个月以前的版本,我这里编译时间是2014.05.06)对H.265的编码支持有问题。开始调试的时候,以为是自己的代码有问题,几经修改也没有找到解决方法。最终发现是类库本身的问题,更换新版本的类库(我这里编译时间是2014.09.16)后问题解决。

流程

下面附上一张FFmpeg编码视频的流程图。通过该流程,不仅可以编码H.264/H.265的码流,而且可以编码MPEG4/MPEG2/VP9/VP8等多种码流。实际上使用FFmpeg编码视频的方式都是一样的。图中蓝色背景的函数是实际输出数据的函数。浅绿色的函数是视频编码的函数。


 


简单介绍一下流程中各个函数的意义(上一篇YUV编码为H.264的文章中已经写过一遍,这里复制粘贴一下):
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)。


代码

下面直接贴上代码
[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. /** 
  2.  * 最简单的基于FFmpeg的视频编码器 
  3.  * Simplest FFmpeg Video Encoder 
  4.  *  
  5.  * 雷霄骅 Lei Xiaohua 
  6.  * leixiaohua1020@126.com 
  7.  * 中国传媒大学/数字电视技术 
  8.  * Communication University of China / Digital TV Technology 
  9.  * http://blog.csdn.net/leixiaohua1020 
  10.  *  
  11.  * 本程序实现了YUV像素数据编码为视频码流(HEVC(H.265),H264,MPEG2,VP8等等)。 
  12.  * 是最简单的FFmpeg视频编码方面的教程。 
  13.  * 通过学习本例子可以了解FFmpeg的编码流程。 
  14.  * This software encode YUV420P data to HEVC(H.265) bitstream (or 
  15.  * H.264, MPEG2, VP8 etc.). 
  16.  * It's the simplest video encoding software based on FFmpeg.  
  17.  * Suitable for beginner of FFmpeg  
  18.  */  
  19.   
  20. #include <stdio.h>  
  21.   
  22. extern "C"  
  23. {  
  24. #include "libavutil\opt.h"  
  25. #include "libavcodec\avcodec.h"  
  26. #include "libavformat\avformat.h"  
  27. #include "libswscale\swscale.h"  
  28. };  
  29.   
  30.   
  31. int flush_encoder(AVFormatContext *fmt_ctx,unsigned int stream_index)  
  32. {  
  33.     int ret;  
  34.     int got_frame;  
  35.     AVPacket enc_pkt;  
  36.     if (!(fmt_ctx->streams[stream_index]->codec->codec->capabilities &  
  37.         CODEC_CAP_DELAY))  
  38.         return 0;  
  39.     while (1) {  
  40.         printf("Flushing stream #%u encoder\n", stream_index);  
  41.         //ret = encode_write_frame(NULL, stream_index, &got_frame);  
  42.         enc_pkt.data = NULL;  
  43.         enc_pkt.size = 0;  
  44.         av_init_packet(&enc_pkt);  
  45.         ret = avcodec_encode_video2 (fmt_ctx->streams[stream_index]->codec, &enc_pkt,  
  46.             NULL, &got_frame);  
  47.         av_frame_free(NULL);  
  48.         if (ret < 0)  
  49.             break;  
  50.         if (!got_frame){  
  51.             ret=0;  
  52.             break;  
  53.         }  
  54.         printf("Succeed to encode 1 frame! 编码成功1帧!\n");  
  55.         /* mux encoded frame */  
  56.         ret = av_write_frame(fmt_ctx, &enc_pkt);  
  57.         if (ret < 0)  
  58.             break;  
  59.     }  
  60.     return ret;  
  61. }  
  62.   
  63. int main(int argc, char* argv[])  
  64. {  
  65.     AVFormatContext* pFormatCtx;  
  66.     AVOutputFormat* fmt;  
  67.     AVStream* video_st;  
  68.     AVCodecContext* pCodecCtx;  
  69.     AVCodec* pCodec;  
  70.   
  71.     uint8_t* picture_buf;  
  72.     AVFrame* picture;  
  73.     int size;  
  74.   
  75.     //FILE *in_file = fopen("src01_480x272.yuv", "rb"); //Input YUV data 视频YUV源文件   
  76.     FILE *in_file = fopen("ds_480x272.yuv""rb");  //Input YUV data 视频YUV源文件   
  77.     int in_w=480,in_h=272;//宽高    
  78.     //Frames to encode  
  79.     int framenum=100;  
  80.     //const char* out_file = "src01.h264";  //Output Filepath 输出文件路径  
  81.     //const char* out_file = "src01.ts";  
  82.     //const char* out_file = "src01.hevc";  
  83.     const char* out_file = "ds.hevc";  
  84.   
  85.     av_register_all();  
  86.     //Method1 方法1.组合使用几个函数  
  87.     pFormatCtx = avformat_alloc_context();  
  88.     //Guess Format 猜格式  
  89.     fmt = av_guess_format(NULL, out_file, NULL);  
  90.     pFormatCtx->oformat = fmt;  
  91.       
  92.     //Method 2 方法2.更加自动化一些  
  93.     //avformat_alloc_output_context2(&pFormatCtx, NULL, NULL, out_file);  
  94.     //fmt = pFormatCtx->oformat;  
  95.   
  96.   
  97.     //Output Format 注意输出路径  
  98.     if (avio_open(&pFormatCtx->pb,out_file, AVIO_FLAG_READ_WRITE) < 0)  
  99.     {  
  100.         printf("Failed to open output file! 输出文件打开失败");  
  101.         return -1;  
  102.     }  
  103.   
  104.     video_st = avformat_new_stream(pFormatCtx, 0);  
  105.     video_st->time_base.num = 1;   
  106.     video_st->time_base.den = 25;    
  107.   
  108.     if (video_st==NULL)  
  109.     {  
  110.         return -1;  
  111.     }  
  112.     //Param that must set  
  113.     pCodecCtx = video_st->codec;  
  114.     //pCodecCtx->codec_id =AV_CODEC_ID_HEVC;  
  115.     pCodecCtx->codec_id = fmt->video_codec;  
  116.     pCodecCtx->codec_type = AVMEDIA_TYPE_VIDEO;  
  117.     pCodecCtx->pix_fmt = PIX_FMT_YUV420P;  
  118.     pCodecCtx->width = in_w;    
  119.     pCodecCtx->height = in_h;  
  120.     pCodecCtx->time_base.num = 1;    
  121.     pCodecCtx->time_base.den = 25;    
  122.     pCodecCtx->bit_rate = 400000;    
  123.     pCodecCtx->gop_size=250;  
  124.     //H264  
  125.     //pCodecCtx->me_range = 16;  
  126.     //pCodecCtx->max_qdiff = 4;  
  127.     //pCodecCtx->qcompress = 0.6;  
  128.     pCodecCtx->qmin = 10;  
  129.     pCodecCtx->qmax = 51;  
  130.   
  131.     //Optional Param  
  132.     pCodecCtx->max_b_frames=3;  
  133.   
  134.     // Set Option  
  135.     AVDictionary *param = 0;  
  136.     //H.264  
  137.     if(pCodecCtx->codec_id == AV_CODEC_ID_H264) {  
  138.         av_dict_set(?m, "preset""slow", 0);  
  139.         av_dict_set(?m, "tune""zerolatency", 0);  
  140.     }  
  141.     //H.265  
  142.     if(pCodecCtx->codec_id == AV_CODEC_ID_H265){  
  143.         av_dict_set(?m, "x265-params""qp=20", 0);  
  144.         av_dict_set(?m, "preset""ultrafast", 0);  
  145.         av_dict_set(?m, "tune""zero-latency", 0);  
  146.     }  
  147.   
  148.     //Dump Information 输出格式信息  
  149.     av_dump_format(pFormatCtx, 0, out_file, 1);  
  150.   
  151.     pCodec = avcodec_find_encoder(pCodecCtx->codec_id);  
  152.     if (!pCodec){  
  153.         printf("Can not find encoder! 没有找到合适的编码器!\n");  
  154.         return -1;  
  155.     }  
  156.     if (avcodec_open2(pCodecCtx, pCodec,?m) < 0){  
  157.         printf("Failed to open encoder! 编码器打开失败!\n");  
  158.         return -1;  
  159.     }  
  160.       
  161.   
  162.   
  163.     picture = avcodec_alloc_frame();  
  164.     size = avpicture_get_size(pCodecCtx->pix_fmt, pCodecCtx->width, pCodecCtx->height);  
  165.     picture_buf = (uint8_t *)av_malloc(size);  
  166.     avpicture_fill((AVPicture *)picture, picture_buf, pCodecCtx->pix_fmt, pCodecCtx->width, pCodecCtx->height);  
  167.   
  168.     //Write File Header 写文件头  
  169.     avformat_write_header(pFormatCtx,NULL);  
  170.   
  171.     AVPacket pkt;  
  172.     int y_size = pCodecCtx->width * pCodecCtx->height;  
  173.     av_new_packet(&pkt,y_size*3);  
  174.   
  175.     for (int i=0; i<framenum; i++){  
  176.         //Read YUV 读入YUV  
  177.         if (fread(picture_buf, 1, y_size*3/2, in_file) < 0){  
  178.             printf("Failed to read YUV data! 文件读取错误\n");  
  179.             return -1;  
  180.         }else if(feof(in_file)){  
  181.             break;  
  182.         }  
  183.         picture->data[0] = picture_buf;  // 亮度Y  
  184.         picture->data[1] = picture_buf+ y_size;  // U   
  185.         picture->data[2] = picture_buf+ y_size*5/4; // V  
  186.         //PTS  
  187.         picture->pts=i;  
  188.         int got_picture=0;  
  189.         //Encode 编码  
  190.         int ret = avcodec_encode_video2(pCodecCtx, &pkt,picture, &got_picture);  
  191.         if(ret < 0){  
  192.             printf("Failed to encode! 编码错误!\n");  
  193.             return -1;  
  194.         }  
  195.         if (got_picture==1){  
  196.             printf("Succeed to encode 1 frame! 编码成功1帧!\n");  
  197.             pkt.stream_index = video_st->index;  
  198.             ret = av_write_frame(pFormatCtx, &pkt);  
  199.             av_free_packet(&pkt);  
  200.         }  
  201.     }  
  202.     //Flush Encoder  
  203.     int ret = flush_encoder(pFormatCtx,0);  
  204.     if (ret < 0) {  
  205.         printf("Flushing encoder failed\n");  
  206.         return -1;  
  207.     }  
  208.   
  209.     //Write file trailer 写文件尾  
  210.     av_write_trailer(pFormatCtx);  
  211.   
  212.     //Clean 清理  
  213.     if (video_st){  
  214.         avcodec_close(video_st->codec);  
  215.         av_free(picture);  
  216.         av_free(picture_buf);  
  217.     }  
  218.     avio_close(pFormatCtx->pb);  
  219.     avformat_free_context(pFormatCtx);  
  220.   
  221.     fclose(in_file);  
  222.   
  223.     return 0;  
  224. }  





结果

软件运行截图(受限于文件体积,原始YUV帧数只有100帧):


这次换了个有趣点的YUV序列。之前总是看YUV标准测试序列都已经看烦了,这次换个电视剧里的序列相对更加生动一些。YUV序列如下图所示。

 
编码后的HEVC(H.265)码流:
 

下载

SourceForge项目地址:

https://sourceforge.net/projects/simplestffmpegvideoencoder/

CSDN下载地址:

http://download.csdn.net/detail/leixiaohua1020/8001515


转载自http://blog.csdn.net/leixiaohua1020/article/details/39770947

### 回答1: FFmpeg是一款流媒体处理的工具,支持多种视频编码格式,其中也包括YUV编码。H.265是一种高效的视频编码格式,可以将视频文件的大小压缩至原来的一半。 要将YUV编码的视频文件转换为H.265编码,需要进行以下步骤: 1. 通过FFmpeg获取原始视频的YUV数据。 2. 将YUV数据进行处理,将其转换为H.265编码格式。此处需要使用x265编码,以实现高质量的视频编码。 3. 将处理后的H.265编码数据转化为保存为视频文件。 在使用FFmpeg进行YUV编码为H.265的操作时,需要注意以下几点: 1. YUV数据的格式应该符合编码的要求,否则将无法进行编码。 2. 编码参数的选择对于编码质量和压缩率都有很大影响,需要注意对应的参数设置。 3. H.265编码是比较耗时的操作,需要足够的计算机性能支持。 通过上述步骤,我们可以将YUV格式的视频文件转换为高效的H.265格式,实现更好的视频质量和更小的视频文件大小。 ### 回答2: FFmpeg是一个跨平台的音视频处理库,其中也包括了对YUV数据的处理和编码功能。而H.265是一种高效的视频编码标准,能够提供更好的视频质量和更小的文件大小。因此,将YUV数据编码为H.265对于提高视频编码的效率和质量非常重要。 在FFmpeg中,可以使用x265编码来将YUV数据编码为H.265。首先,需要将YUV数据加载到FFmpeg中,并设置相应的编码参数。然后,使用x265编码YUV数据进行压缩编码,并输出为H.265视频文件格式。 具体步骤如下: 1. 使用FFmpeg加载YUV数据,可以通过命令行输入以下命令: ffmpeg -s:v widthxheight -pix_fmt yuv420p -i input.yuv 其中,width和height分别表示YUV数据的宽度和高度,input.yuvYUV数据的文件名。 2. 设置编码参数,可以通过命令行指定编码的参数,例如: ffmpeg -c:v libx265 -preset medium -x265-params keyint=60 -b:v 2M output.mp4 其中,-c:v表示指定使用x265编码,-preset medium表示设置为中等压缩质量,-x265-params keyint=60表示设置关键帧间隔为60,-b:v 2M表示设置输出视频的比特率为2M,output.mp4表示输出为H.265视频文件。 3. 进行YUV编码,可以使用以下命令实现: ffmpeg -i input.yuv -c:v libx265 -preset medium -x265-params keyint=60 -b:v 2M output.mp4 其中,-i input.yuv表示输入YUV数据文件,-c:v libx265表示指定使用x265编码进行编码,后续参数同上。 通过以上步骤,就可以将YUV数据编码为H.265格式的视频文件,从而利用H.265标准的高效性能优势来提高视频编码的效率和质量。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值