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. PAT 乙级 -- 1002 -- 写出这个数

    题目: 读入一个自然数n,计算其各位数字之和,用汉语拼音写出和的每一位数字. 输入格式:每个测试输入包含1个测试用例,即给出自然数n的值.这里保证n小于10100. 输出格式:在一行内输出n的各位数字 ...

  2. Python脚本破解Linux口令(crypt模块)

    环境 Kali Linux ,python版本2.7.13 . 我们利用Linux系统中的 crypt 模块模拟了Linux系统中用户密码的加密,在Windows中是不存在这个库的. 在Linux系统 ...

  3. 源码分析SpringCloud Gateway如何加载断言(predicates)与过滤器(filters)

    我们今天的主角是Gateway网关,一听名字就知道它基本的任务就是去分发路由.根据不同的指定名称去请求各个服务,下面是Gateway官方的解释: https://spring.io/projects/ ...

  4. Android LayoutInflater.inflate源码解析

    一年多以前看过源码,感觉了解比较透彻了,长时间不经大脑思考,靠曾经总结的经验使用inflate方法,突然发现不知道什么时候忘记其中的原理了,上网查了一些资料,还各有不同,反而把我搞糊涂了,还是自己看源 ...

  5. xxl-job源码阅读一(客户端)

    1.源码入口 使用xxl-job的时候,需要引入一个jar,然后还需要往Spring容器注入XxlJobSpringExecutor <dependency> <groupId> ...

  6. lua编译为二进制方式

    当不想使用户看到lua源码,文本文件可以通过luac,把lua文本文件"编译"成二进制的文件. lc@lc-virtual-machine:~/lua$ luac -o redis ...

  7. [刷题] 104 Maximum Depth of Binary Tree

    要求 求一棵二叉树的最高深度 思路 递归地求左右子树的最高深度 实现 1 Definition for a binary tree node. 2 struct TreeNode { 3 int va ...

  8. SPECCPU2006 Spec2006 使用说明

    http://www.vimlinux.com/lipeng/author/penglee5.html Spec2006使用说明 五 10 十月 2014 By penglee 工具介绍 SPEC C ...

  9. 详解Linux中的cat文本输出命令用法

    作系统 > LINUX >   详解Linux中的cat文本输出命令用法 Linux命令手册   发布时间:2016-01-14 14:14:35   作者:张映    我要评论   这篇 ...

  10. ft2000安装tigervnc

    apt update apt install tigervnc*vncserver :88 history >>history