ffmpeg architecture(下)

第3章-转码

TLDR;给我看代码和执行。

$ make run_transcoding

我们将跳过一些细节,但是请放心:源代码可在github上找到

在本章中,我们将创建一个用C编写的极简代码转换器,可以使用FFmpeg / libav库(尤其是libavcodec,libavformat和libavutil)将H264编码的视频转换为H265。

只是快速回顾一下:AVFormatContext是媒体文件格式的抽象,又名容器(例如:MKV,MP4,WEBM,TS)。的AV表示每种类型的用于给定格式(例如:音频,视频,字幕,元数据)的数据。所述AVPacket是从所获得的压缩数据的切片AVStream可以由一个解码avcodec:产生称为原始数据(例如AV1,H264,VP9,HEVC)AVFrame

转码

让我们从简单的转换操作开始,然后我们可以基于此代码构建,第一步是加载输入文件

//分配一个AVFormatContext
avfc = avformat_alloc_context();
//打开输入流并读取标题。
avformat_open_input(avfc,in_filename, NULL, NULL);
//读取媒体文件的数据包以获取流信息。
avformat_find_stream_info(avfc, NULL);

现在,我们要设置解码器,它AVFormatContext将使我们能够访问所有AVStream组件,并且对于每个组件,我们都可以获取它们AVCodec并创建特定组件AVCodecContext,最后我们可以打开给定的编解码器,以便继续进行解码处理。

所述AVCodecContext持有如比特率,帧速率,采样率,信道,高度,和许多其它有关的媒体数据的配置等。

对于(int i = 0 ; i <avfc-> nb_streams; i ++)
{
  AVStream * avs = avfc-> 流 [i];
  AVCodec * avc = avcodec_find_decoder(avs-> codecpar- > codec_id);
  AVCodecContext * avcc = avcodec_alloc_context3(* avc);
  avcodec_parameters_to_context(* avcc,avs-> codecpar);
  avcodec_open2(* avcc,* avc,NULL);
}

我们还需要准备输出媒体文件以进行多路传输,我们首先为输出分配内存AVFormatContext。我们以输出格式创建每个流。为了正确打包流,我们从解码器复制编解码器参数

我们设置标志 AV_CODEC_FLAG_GLOBAL_HEADER告诉编码器它可以使用全局头文件,最后打开输出文件进行写入并保留头文件。

avformat_alloc_output_context2(&encoder_avfc,NULL,NULL,out_filename);
AVStream * avs = avformat_new_stream(encoder_avfc,NULL);
avcodec_parameters_copy(avs-> codecpar,解码器_avs-> codecpar);
如果(encoder_avfc-> oformat-> flags和AVFMT_GLOBALHEADER)
  encoder_avfc-> flags | = AV_CODEC_FLAG_GLOBAL_HEADER;
avio_open(&encoder_avfc-> pb,编码器->文件名,AVIO_FLAG_WRITE);
avformat_write_header(encoder- > avfc,&muxer_opts);

我们AVPacket从解码器获取,调整时间戳,并将数据包正确写入输出文件。即使函数av_interleaved_write_frame说“写帧”,我们仍在存储数据包。通过将流预告片写入文件中,我们完成了转换过程。

AVFrame * input_frame = av_frame_alloc();
AVPacket * input_packet = av_packet_alloc();
而(av_read_frame(decoder_avfc,input_packet)> = 0)
{
  av_packet_rescale_ts(input_packet,解码器_视频_avs-> time_base,编码器 _视频_avs- > time_base);
  av_interleaved_write_frame(* avfc,input_packet)< 0));
}
av_write_trailer(encoder_avfc);

转码

上一节展示了一个简单的transmuxer程序,现在我们将添加对文件进行编码的功能,尤其是使它能够将视频从转换h264h265

在准备好解码器之后,但在安排输出媒体文件之前,我们将设置编码器。

AVRational input_framerate = av_guess_frame_rate(decoder_avfc,decoder_video_avs,NULL);
AVStream * video_avs = avformat_new_stream(encoder_avfc,NULL);
char * codec_name = “ libx265 ” ;
char * codec_priv_key = “ x265-params ” ;
//我们将为x265使用内部选项
// //禁用场景更改检测,然后修复
// 60帧的GOP。
char * codec_priv_value = “ keyint = 60:min-keyint = 60:scenecut = 0 ” ;
AVCodec * video_avc = avcodec_find_encoder_by_name(codec_name);
AVCodecContext * video_avcc = avcodec_alloc_context3(video_avc);
//编码器编解码器参数
av_opt_set(sc-> video_avcc-> priv_data,codec_priv_key,codec_priv_value, 0);
video_avcc-> height =解码器_ctx-> height;
video_avcc-> width =解码器_ctx-> width;
video_avcc-> pix_fmt = video_avc-> pix_fmts [ 0 ];
//控制率 
video_avcc-> bit_rate = 2 * 1000 * 1000 ;
video_avcc-> rc_buffer_size = 4 * 1000 * 1000 ;
video_avcc-> rc_max_rate = 2 * 1000 * 1000 ;
video_avcc-> rc_min_rate = 2.5 * 1000 * 1000 ;
//时基
video_avcc-> time_base = av_inv_q(input_framerate);
video_avs-> time_base = sc-> video_avcc-> time_base;
avcodec_open2(sc-> video_avcc,sc-> video_avc,NULL);
avcodec_parameters_from_context(sc-> video_avs-> codecpar,sc-> video_avcc);

我们需要扩展解码循环以进行视频流转码:

AVFrame * input_frame = av_frame_alloc();
AVPacket * input_packet = av_packet_alloc();
而(av_read_frame(decoder_avfc,input_packet)> = 0)
{
  int响应= avcodec_send_packet(decoder_video_avcc,input_packet);
  while(响应> = 0){
    响应= avcodec_receive_frame(decoder_video_avcc,input_frame);
    if(响应== AVERROR(EAGAIN)|| response == AVERROR_EOF){
       中断 ;
    } else  if(response < 0){
       返回响应;
    }
    if(响应> = 0){
       编码(encoder_avfc,decoder_video_avs,encoder_video_avs,decoder_video_avcc,input_packet-> stream_index);
    }
    av_frame_unref(input_frame);
  }
  av_packet_unref(input_packet);
}
av_write_trailer(encoder_avfc);
//使用的函数
int  编码(AVFormatContext * avfc,AVStream * dec_video_avs,AVStream * enc_video_avs,AVCodecContext video_avcc int索引){
  AVPacket * output_packet = av_packet_alloc();
  int response = avcodec_send_frame(video_avcc,input_frame);
  while(响应> = 0){
    响应= avcodec_receive_packet(video_avcc,output_packet);
    if(响应== AVERROR(EAGAIN)|| response == AVERROR_EOF){
       中断 ;
    } else  if(响应< 0){
       return - 1 ;
    }
    output_packet-> stream_index = index ;
    output_packet-> 持续时间 = enc_video_avs-> 那么time_base。den / enc_video_avs-> time_base。num / dec_video_avs-> avg_frame_rate。num * dec_video_avs-> avg_frame_rate。巢穴 ;
    av_packet_rescale_ts(output_packet,dec_video_avs-> time_base,enc_video_avs-> time_base);
    响应= av_interleaved_write_frame(avfc,output_packet);
  }
  av_packet_unref(output_packet);
  av_packet_free(&output_packet);
  返回 0 ;
}

我们将媒体流从转换h264h265,正如预期的那样h265,媒体文件的版本小于,h264创建的程序能够:

  / *
    * H264-> H265 
   *音频->重混合(未触摸)
   * MP4-MP4 
* / 
  StreamingParams sp = { 0 };   
  sp.copy_audio = 1 ;
  sp.copy_video = 0 ;
  sp.video_codec = “ libx265 ”;
  sp.codec_priv_key = “ x265-params ” ;
  sp.codec_priv_value = “ keyint = 60:min-keyint = 60:scenecut = 0 ” ;
  / *
    * H264-> H264(固定gop)
   *音频->重混合(未触摸)
   * MP4-MP4 
* / 
  StreamingParams sp = { 0 };   
  sp.copy_audio = 1 ;
  sp.copy_video = 0 ;
  sp.video_codec = “ libx264 ”;
  sp.codec_priv_key = “ x264参数” ;
  sp.codec_priv_value = “ keyint = 60:min-keyint = 60:scenecut = 0:force-cfr = 1 ” ;
  / *
    * H264-> H264(固定gop)
   *音频->重混合(
未修改)* MP4-    片段MP4 
* / 
  StreamingParams sp = { 0 };   
  sp.copy_audio = 1 ;
  sp.copy_video = 0 ;
  sp.video_codec = “ libx264 ”;
  sp.codec_priv_key = “ x264参数” ;
  sp.codec_priv_value = “ keyint = 60:min-keyint = 60:scenecut = 0:force-cfr = 1 ” ;
  sp.muxer_opt_key = “ movflags ”;
  sp.muxer_opt_value = “ frag_keyframe + empty_moov + default_base_moof ” ;
  / *
    * H264-> H264(固定gop)
   *音频-> AAC 
   * MP4-MPEG-TS 
* / 
  StreamingParams sp = { 0 };   
  sp.copy_audio = 0 ;
  sp.copy_video = 0 ;
  sp.video_codec = “ libx264 ”;
  sp.codec_priv_key = “ x264参数” ;
  sp.codec_priv_value = “ keyint = 60:min-keyint = 60:scenecut = 0:force-cfr = 1 ” ;
  sp.audio_codec = “ aac ” ;
  sp.output_extension = “ .ts ” ;
  / * WIP:P->它不在VLC上播放,最终比特率很高
   * H264-> VP9 
   *音频-> Vorbis 
   * MP4-WebM 
* / // StreamingParams sp = {0}; // sp.copy_audio = 0; // sp.copy_video = 0; // sp.video_codec =“ libvpx-vp9”; // sp.audio_codec =“ libvorbis”; // sp.output_extension =“ .webm”;   
现在,说实话,这是难度比我想这会是我不得不挖掘到FFmpeg的命令行的源代码和测试了很多,我觉得我失去了一些东西,因为我不得不执行force-cfrh264工作而且我仍然看到一些警告消息,例如warning messages (forced frame type (5) at 80 was changed to frame type (3))

ffmpeg architecture(下)的更多相关文章

  1. ffmpeg architecture(中)

    ffmpeg architecture(中) 艰苦学习FFmpeg libav 您是否不奇怪有时会发出声音和视觉? 由于FFmpeg作为命令行工具非常有用,可以对媒体文件执行基本任务,因此如何在程序中 ...

  2. ffmpeg architecture(上)

    ffmpeg architecture(上) 目录 介绍 视频-您看到的是什么! 音频-您在听什么! 编解码器-缩小数据 容器-音频和视频的舒适场所 FFmpeg-命令行 FFmpeg命令行工具101 ...

  3. ffmpeg windows下编译ffmpeg

    windows下编译ffmpeg 今天由于工作需求需重新编译ffmpeg,百度,goole了一大堆,看眼花缭乱的,但几乎都是三种方案,大部分都是直接转发,一字不漏,错误的缺文件的还是照转,可是问题都大 ...

  4. FFmpeg Windows下安装与测试

    FFmpeg 简介 FFmpeg的名称来自MPEG视频编码标准,前面的"FF"代表"Fast Forward",FFmpeg是一套可以用来记录.转换数字音频.视 ...

  5. FFmpeg: mac下手动编译android上使用的FFmpeg(支持x86、armeabi-v7a、arm64-v8a)

    之前一直在linux下编译FFmpeg,最近换电脑了,尝试了下在mac下编译ffmpeg,特记录之. 一. 准备工作 1. 下载FFmpeg.(http://ffmpeg.org/download.h ...

  6. ffmpeg windows下编译安装

    安装msys2 更新源使下载速度更快 进入msys64/etc/pacman.d/目录中,分别在三个文件中增加mirrorlist.mingw32Server = http://mirrors.ust ...

  7. ffmpeg Windows下采集摄像头一帧数据,并保存为bmp图片

    这里请注意,在编译ffmpeg时,不要使用--disable-devices选项. 使用 --enable-encoder=rawvideo --enable-decoder=rawvideo 启用r ...

  8. [转载] ffmpeg Windows下采集摄像头一帧数据,并保存为bmp图片

    这里请注意,在编译ffmpeg时,不要使用--disable-devices选项. 使用 --enable-encoder=rawvideo --enable-decoder=rawvideo 启用r ...

  9. Linux下使用NDK编译FFMPEG(libstagefright)

    这个月要负责一个项目,使用FFMPEG渲染视频,主要是Android端的,由于性能要求,要使用硬解码,但网上大多数教程都是没有libstagefright的,所以个人觉得,生成的so库文件也是没有开启 ...

随机推荐

  1. hdu1960 最小路径覆盖

    题意:       给你明天的出租车订单,订单中包含每个人的起点和终点坐标,还有时间,如果一辆出租车想接一个乘客必须在每个订单前1分钟到达,也就是小于等于time-1,问你完成所有订单要最少多少量出租 ...

  2. Python脚本暴力破解FTP口令(ftplib)

    目录 判断FTP服务器是否允许匿名登录 暴力破解FTP口令 列出FTP目录内的网页文件 综合 环境:Windows python2.7.15 ftplib模块是python下用于ftp服务的模块 . ...

  3. Python脚本写端口扫描器(socket,python-nmap)

    目录 Socket模块编写 扫描给定主机是否开放了指定的端口 python-nmap模块编写 扫描给定ip或给定网段内指定端口是否开放 一个用python写的简单的端口扫描器,python环境为 3. ...

  4. 深入浅出带你玩转sqlilabs(一)

    一.MySQL数据库结构分层 1.1库名,表名,列名,数据库用户等 Mysql数据库结构示例: 数据库A zblog = www.zblog.com 表名 列名(字段) 数据 数据库B dede = ...

  5. 【vue-07】vue-router

    Vue-router官网 安装 vue-router是一个插件包,所以我们还是需要用npm 来进行安装.打开命令行工具,进入你的项目目录,输入下面命令. npm install vue-router ...

  6. yum makecache: error: argument timer: invalid choice: ‘fast’ (choose from ‘timer’)

    这是因为版本问题,centos8没有该参数,解决办法为:去掉fast参数,就可以了 sudo yum makecache

  7. 制作 PPT 的新方式出现了——GitHub 热点速览 v.21.19

    作者:HelloGitHub-小鱼干 想当初 Markdown 的出现,拯救了多少死在 Word 样式调整上的人,现在,slidev 出现了,它让你 Focus 在本该专注的 PPT 内容制作上而不需 ...

  8. ImageIo.read 返回null

    一.问题描述 今天收到一个bug就是imageio读取图片会返回null,具体如下 但是其他的图片就没有问题 二.问题分析 结合百度发现这张图片原本的后缀并非是jpg,使用notpard++打开就可以 ...

  9. 测试的V模型和W模型

    V模型 :后测试 优点: 1.每一阶段都清晰明了,便于把控开发的每一个过程. 2.既包含了单元测试又包含了系统测试 缺点: 1.测试介入的比较晚,所以开发前期的缺陷无从修改. 2.开发和测试串行. W ...

  10. 『居善地』接口测试 — 4、Requests库发送GET请求

    目录 1.使用Requests库发送带参数的GET请求 2.查看GET请求的内容 3.带请求头.参数的Get请求 Requests库GET请求是使用HTTP协议中的GET请求方式对目标网站发起请求. ...