博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
最简单的基于FFmpeg的编码器-纯净版(不包含libavformat)
阅读量:4165 次
发布时间:2019-05-26

本文共 10143 字,大约阅读时间需要 33 分钟。

=====================================================

最简单的基于FFmpeg的视频编码器文章列表:

=====================================================

本文记录一个更加“纯净”的基于FFmpeg的视频编码器。此前记录过一个基于FFmpeg的视频编码器:

这个视频编码器调用了FFmpeg中的libavformat和libavcodec两个库完成了视频编码工作。但是这不是一个“纯净”的编码器。上述两个库中libavformat完成封装格式处理,而libavcodec完成编码工作。一个“纯净”的编码器,理论上说只需要使用libavcodec就足够了,并不需要使用libavformat。本文记录的编码器就是这样的一个“纯净”的编码器,它仅仅通过调用libavcodec将YUV数据编码为H.264/HEVC等格式的压缩视频码流。

流程图

仅使用libavcodec(不使用libavformat)编码视频的流程如下图所示。

流程图中关键函数的作用如下所列:

avcodec_register_all():注册所有的编解码器。
avcodec_find_encoder():查找编码器。
avcodec_alloc_context3():为AVCodecContext分配内存。
avcodec_open2():打开编码器。
avcodec_encode_video2():编码一帧数据。
两个存储数据的结构体如下所列:
AVFrame:存储一帧未编码的像素数据。
AVPacket:存储一帧压缩编码数据。

对比

简单记录一下这个只使用libavcodec的“纯净版”视频编码器和使用libavcodec+libavformat的视频编码器的不同。

PS:使用libavcodec+libavformat的编码器参考文章

(1)
下列与libavformat相关的函数在“纯净版”视频编码器中都不存在。
av_register_all():注册所有的编解码器,复用/解复用器等等组件。其中调用了avcodec_register_all()注册所有编解码器相关的组件。
avformat_alloc_context():创建AVFormatContext结构体。
avformat_alloc_output_context2():初始化一个输出流。
avio_open():打开输出文件。
avformat_new_stream():创建AVStream结构体。avformat_new_stream()中会调用avcodec_alloc_context3()创建AVCodecContext结构体。
avformat_write_header():写文件头。
av_write_frame():写编码后的文件帧。
av_write_trailer():写文件尾。
(2)
新增了如下几个函数
avcodec_register_all():只注册编解码器有关的组件。

avcodec_alloc_context3():创建AVCodecContext结构体。

可以看出,相比于“完整”的编码器,这个纯净的编码器函数调用更加简单,功能相对少一些,相对来说更加的“轻量”。

源代码

[cpp]
  1. /** 
  2.  * 最简单的基于FFmpeg的视频编码器(纯净版) 
  3.  * Simplest FFmpeg Video Encoder Pure 
  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像素数据编码为视频码流(H264,MPEG2,VP8等等)。 
  12.  * 它仅仅使用了libavcodec(而没有使用libavformat)。 
  13.  * 是最简单的FFmpeg视频编码方面的教程。 
  14.  * 通过学习本例子可以了解FFmpeg的编码流程。 
  15.  * This software encode YUV420P data to video bitstream 
  16.  * (Such as H.264, H.265, VP8, MPEG2 etc). 
  17.  * It only uses libavcodec to encode video (without libavformat) 
  18.  * It's the simplest video encoding software based on FFmpeg.  
  19.  * Suitable for beginner of FFmpeg  
  20.  */  
  21.   
  22.   
  23. #include <stdio.h>   
  24.   
  25. extern "C"  
  26. {  
  27. #include "libavutil\opt.h"   
  28. #include "libavcodec\avcodec.h"   
  29. #include "libavutil\imgutils.h"   
  30. };  
  31.   
  32. //test different codec   
  33. #define TEST_H264  0   
  34. #define TEST_HEVC  1   
  35.   
  36.   
  37. int main(int argc, char* argv[])  
  38. {  
  39.     AVCodec *pCodec;  
  40.     AVCodecContext *pCodecCtx= NULL;  
  41.     int i, ret, x, y, got_output;  
  42.     FILE *fp_in;  
  43.     FILE *fp_out;  
  44.     AVFrame *pFrame;  
  45.     AVPacket pkt;  
  46.     int y_size;  
  47.     int framecnt=0;  
  48.   
  49.     char filename_in[]="../ds_480x272.yuv";  
  50.   
  51. #if TEST_HEVC   
  52.     AVCodecID codec_id=AV_CODEC_ID_HEVC;  
  53.     char filename_out[]="ds.hevc";  
  54. #else   
  55.     AVCodecID codec_id=AV_CODEC_ID_H264;  
  56.     char filename_out[]="ds.h264";  
  57. #endif   
  58.   
  59.   
  60.     int in_w=480,in_h=272;    
  61.     int framenum=100;     
  62.   
  63.     avcodec_register_all();  
  64.   
  65.     pCodec = avcodec_find_encoder(codec_id);  
  66.     if (!pCodec) {  
  67.         printf("Codec not found\n");  
  68.         return -1;  
  69.     }  
  70.     pCodecCtx = avcodec_alloc_context3(pCodec);  
  71.     if (!pCodecCtx) {  
  72.         printf("Could not allocate video codec context\n");  
  73.         return -1;  
  74.     }  
  75.     pCodecCtx->bit_rate = 400000;  
  76.     pCodecCtx->width = in_w;  
  77.     pCodecCtx->height = in_h;  
  78.     pCodecCtx->time_base.num=1;  
  79.     pCodecCtx->time_base.den=25;  
  80.     pCodecCtx->gop_size = 10;  
  81.     pCodecCtx->max_b_frames = 1;  
  82.     pCodecCtx->pix_fmt = AV_PIX_FMT_YUV420P;  
  83.   
  84.     if (codec_id == AV_CODEC_ID_H264)  
  85.         av_opt_set(pCodecCtx->priv_data, "preset""slow", 0);  
  86.    
  87.     if (avcodec_open2(pCodecCtx, pCodec, NULL) < 0) {  
  88.         printf("Could not open codec\n");  
  89.         return -1;  
  90.     }  
  91.       
  92.     pFrame = av_frame_alloc();  
  93.     if (!pFrame) {  
  94.         printf("Could not allocate video frame\n");  
  95.         return -1;  
  96.     }  
  97.     pFrame->format = pCodecCtx->pix_fmt;  
  98.     pFrame->width  = pCodecCtx->width;  
  99.     pFrame->height = pCodecCtx->height;  
  100.   
  101.     ret = av_image_alloc(pFrame->data, pFrame->linesize, pCodecCtx->width, pCodecCtx->height,  
  102.                          pCodecCtx->pix_fmt, 16);  
  103.     if (ret < 0) {  
  104.         printf("Could not allocate raw picture buffer\n");  
  105.         return -1;  
  106.     }  
  107.     //Input raw data   
  108.     fp_in = fopen(filename_in, "rb");  
  109.     if (!fp_in) {  
  110.         printf("Could not open %s\n", filename_in);  
  111.         return -1;  
  112.     }  
  113.     //Output bitstream   
  114.     fp_out = fopen(filename_out, "wb");  
  115.     if (!fp_out) {  
  116.         printf("Could not open %s\n", filename_out);  
  117.         return -1;  
  118.     }  
  119.   
  120.     y_size = pCodecCtx->width * pCodecCtx->height;  
  121.     //Encode   
  122.     for (i = 0; i < framenum; i++) {  
  123.         av_init_packet(&pkt);  
  124.         pkt.data = NULL;    // packet data will be allocated by the encoder   
  125.         pkt.size = 0;  
  126.         //Read raw YUV data   
  127.         if (fread(pFrame->data[0],1,y_size,fp_in)< 0||        // Y   
  128.             fread(pFrame->data[1],1,y_size/4,fp_in)< 0||  // U    
  129.             fread(pFrame->data[2],1,y_size/4,fp_in)< 0){  // V   
  130.             return -1;  
  131.         }else if(feof(fp_in)){  
  132.             break;  
  133.         }  
  134.   
  135.         pFrame->pts = i;  
  136.         /* encode the image */  
  137.         ret = avcodec_encode_video2(pCodecCtx, &pkt, pFrame, &got_output);  
  138.         if (ret < 0) {  
  139.             printf("Error encoding frame\n");  
  140.             return -1;  
  141.         }  
  142.         if (got_output) {  
  143.             printf("Succeed to encode frame: %5d\tsize:%5d\n",framecnt,pkt.size);  
  144.             framecnt++;  
  145.             fwrite(pkt.data, 1, pkt.size, fp_out);  
  146.             av_free_packet(&pkt);  
  147.         }  
  148.     }  
  149.     //Flush Encoder   
  150.     for (got_output = 1; got_output; i++) {  
  151.         ret = avcodec_encode_video2(pCodecCtx, &pkt, NULL, &got_output);  
  152.         if (ret < 0) {  
  153.             printf("Error encoding frame\n");  
  154.             return -1;  
  155.         }  
  156.         if (got_output) {  
  157.             printf("Flush Encoder: Succeed to encode 1 frame!\tsize:%5d\n",pkt.size);  
  158.             fwrite(pkt.data, 1, pkt.size, fp_out);  
  159.             av_free_packet(&pkt);  
  160.         }  
  161.     }  
  162.   
  163.     fclose(fp_out);  
  164.     avcodec_close(pCodecCtx);  
  165.     av_free(pCodecCtx);  
  166.     av_freep(&pFrame->data[0]);  
  167.     av_frame_free(&pFrame);  
  168.   
  169.     return 0;  
  170. }  
/** * 最简单的基于FFmpeg的视频编码器(纯净版) * Simplest FFmpeg Video Encoder Pure *  * 雷霄骅 Lei Xiaohua * leixiaohua1020@126.com * 中国传媒大学/数字电视技术 * Communication University of China / Digital TV Technology * http://blog.csdn.net/leixiaohua1020 *  * 本程序实现了YUV像素数据编码为视频码流(H264,MPEG2,VP8等等)。 * 它仅仅使用了libavcodec(而没有使用libavformat)。 * 是最简单的FFmpeg视频编码方面的教程。 * 通过学习本例子可以了解FFmpeg的编码流程。 * This software encode YUV420P data to video bitstream * (Such as H.264, H.265, VP8, MPEG2 etc). * It only uses libavcodec to encode video (without libavformat) * It's the simplest video encoding software based on FFmpeg.  * Suitable for beginner of FFmpeg  */#include 
extern "C"{#include "libavutil\opt.h"#include "libavcodec\avcodec.h"#include "libavutil\imgutils.h"};//test different codec#define TEST_H264 0#define TEST_HEVC 1int main(int argc, char* argv[]){ AVCodec *pCodec; AVCodecContext *pCodecCtx= NULL; int i, ret, x, y, got_output; FILE *fp_in; FILE *fp_out; AVFrame *pFrame; AVPacket pkt; int y_size; int framecnt=0; char filename_in[]="../ds_480x272.yuv";#if TEST_HEVC AVCodecID codec_id=AV_CODEC_ID_HEVC; char filename_out[]="ds.hevc";#else AVCodecID codec_id=AV_CODEC_ID_H264; char filename_out[]="ds.h264";#endif int in_w=480,in_h=272; int framenum=100; avcodec_register_all(); pCodec = avcodec_find_encoder(codec_id); if (!pCodec) { printf("Codec not found\n"); return -1; } pCodecCtx = avcodec_alloc_context3(pCodec); if (!pCodecCtx) { printf("Could not allocate video codec context\n"); return -1; } pCodecCtx->bit_rate = 400000; pCodecCtx->width = in_w; pCodecCtx->height = in_h; pCodecCtx->time_base.num=1; pCodecCtx->time_base.den=25; pCodecCtx->gop_size = 10; pCodecCtx->max_b_frames = 1; pCodecCtx->pix_fmt = AV_PIX_FMT_YUV420P; if (codec_id == AV_CODEC_ID_H264) av_opt_set(pCodecCtx->priv_data, "preset", "slow", 0); if (avcodec_open2(pCodecCtx, pCodec, NULL) < 0) { printf("Could not open codec\n"); return -1; } pFrame = av_frame_alloc(); if (!pFrame) { printf("Could not allocate video frame\n"); return -1; } pFrame->format = pCodecCtx->pix_fmt; pFrame->width = pCodecCtx->width; pFrame->height = pCodecCtx->height; ret = av_image_alloc(pFrame->data, pFrame->linesize, pCodecCtx->width, pCodecCtx->height, pCodecCtx->pix_fmt, 16); if (ret < 0) { printf("Could not allocate raw picture buffer\n"); return -1; } //Input raw data fp_in = fopen(filename_in, "rb"); if (!fp_in) { printf("Could not open %s\n", filename_in); return -1; } //Output bitstream fp_out = fopen(filename_out, "wb"); if (!fp_out) { printf("Could not open %s\n", filename_out); return -1; } y_size = pCodecCtx->width * pCodecCtx->height; //Encode for (i = 0; i < framenum; i++) { av_init_packet(&pkt); pkt.data = NULL; // packet data will be allocated by the encoder pkt.size = 0; //Read raw YUV data if (fread(pFrame->data[0],1,y_size,fp_in)< 0|| // Y fread(pFrame->data[1],1,y_size/4,fp_in)< 0|| // U fread(pFrame->data[2],1,y_size/4,fp_in)< 0){ // V return -1; }else if(feof(fp_in)){ break; } pFrame->pts = i; /* encode the image */ ret = avcodec_encode_video2(pCodecCtx, &pkt, pFrame, &got_output); if (ret < 0) { printf("Error encoding frame\n"); return -1; } if (got_output) { printf("Succeed to encode frame: %5d\tsize:%5d\n",framecnt,pkt.size); framecnt++; fwrite(pkt.data, 1, pkt.size, fp_out); av_free_packet(&pkt); } } //Flush Encoder for (got_output = 1; got_output; i++) { ret = avcodec_encode_video2(pCodecCtx, &pkt, NULL, &got_output); if (ret < 0) { printf("Error encoding frame\n"); return -1; } if (got_output) { printf("Flush Encoder: Succeed to encode 1 frame!\tsize:%5d\n",pkt.size); fwrite(pkt.data, 1, pkt.size, fp_out); av_free_packet(&pkt); } } fclose(fp_out); avcodec_close(pCodecCtx); av_free(pCodecCtx); av_freep(&pFrame->data[0]); av_frame_free(&pFrame); return 0;}

运行结果

通过设定定义在程序开始的宏,确定需要使用的编码器。

[cpp]
  1. //test different codec   
  2. #define TEST_H264  0   
  3. #define TEST_HEVC  1  
//test different codec#define TEST_H264  0#define TEST_HEVC  1
当TEST_H264设置为1的时候,编码H.264文件“ds.h264”。
当TEST_HEVC设置为1的时候,解码HEVC文件“ds.hevc”。
输入文件是“ds_480x272.yuv”。

程序运行的截图如下所示。

输入的YUV文件如下图所示。

输出的HEVC文件如下图所示。

下载

Simplest ffmpeg encoder pure工程被作为子工程添加到了simplest ffmpeg video encoder工程中。新版的simplest ffmpeg video encoder的信息如下。

Simplest ffmpeg video encoder
SourceForge主页:
CSDN下载地址:
本程序实现了YUV像素数据编码为视频码流(H.265,H264,MPEG2,VP8等等)。

是最简单的FFmpeg视频编码方面的教程。

它包含以下两个子项目:

simplest_ffmpeg_video_encoder:最简单的基于FFmpeg的视频编码器。使用libavcodec和libavformat编码并且封装视频。

simplest_ffmpeg_video_encoder_pure:最简单的基于FFmpeg的视频编码器-纯净版。仅使用libavcodec编码视频,不使用libavformat。

转载:

你可能感兴趣的文章
Fiesta:配置、运行及其结果
查看>>
三维点云处理:7最邻近问题之二叉树
查看>>
三维点云处理: k-nn、radius邻近查找作业
查看>>
三维点云处理:8 Kd-tree
查看>>
三维点云处理:9 octree
查看>>
python记录
查看>>
C++:冒泡排序的实现算法代码
查看>>
PCL是否安装成功测试(手把手超详细版)
查看>>
3、PCL 加载自己的点云数据集并显示出来
查看>>
PCL 三维点云轮廓提取
查看>>
C++:插入排序算法实现代码
查看>>
C++:快速排序法的代码实现
查看>>
4、ROS下播放激光点云bag数据并可视化
查看>>
5、激光雷达bag文件和转PCD文件
查看>>
2、关于点云格式的碎碎念
查看>>
ROS初始化时候sudo rosdep init rosdep update出现的各种问题亲测成功的解决办法(2020.12.29已解决!)
查看>>
6、ubuntu16.04 下 pcl的vogelgrid降采样处理 过程
查看>>
8、在ubuntu16.04 、ROS下使用 rviz 显示octomap_sever 构建的三维栅格地图
查看>>
如何在ubuntu16.04下运行第一个C++和python程序,以helloworld为例
查看>>
古月居 PyTorch入门:一起从零搭建神经网络一、PyTorch环境搭建
查看>>