ffmpeg architecture(下)
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程序,现在我们将添加对文件进行编码的功能,尤其是使它能够将视频从转换h264
为h265
。
在准备好解码器之后,但在安排输出媒体文件之前,我们将设置编码器。
AVStream
在编码器中创建视频,avformat_new_stream
- 使用
AVCodec
所谓的libx265
,avcodec_find_encoder_by_name
AVCodecContext
在创建的编解码器中创建基础,avcodec_alloc_context3
- 设置转码会话的基本属性,并
- 打开编解码器,然后将参数从上下文复制到流中。
avcodec_open2
和avcodec_parameters_from_context
- 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);
我们需要扩展解码循环以进行视频流转码:
- 将空发送
AVPacket
给解码器,avcodec_send_packet
- 接收未压缩的
AVFrame
,avcodec_receive_frame
- 开始对该原始帧进行转码,
- 发送原始帧,
avcodec_send_frame
- 根据我们的编解码器接收压缩的文件
AVPacket
,avcodec_receive_packet
- 设置时间戳,并
av_packet_rescale_ts
- 将其写入输出文件。
av_interleaved_write_frame
- 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 ;
- }
我们将媒体流从转换h264
为h265
,正如预期的那样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-cfr
的h264
工作而且我仍然看到一些警告消息,例如warning messages (forced frame type (5) at 80 was changed to frame type (3))
。
ffmpeg architecture(下)的更多相关文章
- ffmpeg architecture(中)
ffmpeg architecture(中) 艰苦学习FFmpeg libav 您是否不奇怪有时会发出声音和视觉? 由于FFmpeg作为命令行工具非常有用,可以对媒体文件执行基本任务,因此如何在程序中 ...
- ffmpeg architecture(上)
ffmpeg architecture(上) 目录 介绍 视频-您看到的是什么! 音频-您在听什么! 编解码器-缩小数据 容器-音频和视频的舒适场所 FFmpeg-命令行 FFmpeg命令行工具101 ...
- ffmpeg windows下编译ffmpeg
windows下编译ffmpeg 今天由于工作需求需重新编译ffmpeg,百度,goole了一大堆,看眼花缭乱的,但几乎都是三种方案,大部分都是直接转发,一字不漏,错误的缺文件的还是照转,可是问题都大 ...
- FFmpeg Windows下安装与测试
FFmpeg 简介 FFmpeg的名称来自MPEG视频编码标准,前面的"FF"代表"Fast Forward",FFmpeg是一套可以用来记录.转换数字音频.视 ...
- FFmpeg: mac下手动编译android上使用的FFmpeg(支持x86、armeabi-v7a、arm64-v8a)
之前一直在linux下编译FFmpeg,最近换电脑了,尝试了下在mac下编译ffmpeg,特记录之. 一. 准备工作 1. 下载FFmpeg.(http://ffmpeg.org/download.h ...
- ffmpeg windows下编译安装
安装msys2 更新源使下载速度更快 进入msys64/etc/pacman.d/目录中,分别在三个文件中增加mirrorlist.mingw32Server = http://mirrors.ust ...
- ffmpeg Windows下采集摄像头一帧数据,并保存为bmp图片
这里请注意,在编译ffmpeg时,不要使用--disable-devices选项. 使用 --enable-encoder=rawvideo --enable-decoder=rawvideo 启用r ...
- [转载] ffmpeg Windows下采集摄像头一帧数据,并保存为bmp图片
这里请注意,在编译ffmpeg时,不要使用--disable-devices选项. 使用 --enable-encoder=rawvideo --enable-decoder=rawvideo 启用r ...
- Linux下使用NDK编译FFMPEG(libstagefright)
这个月要负责一个项目,使用FFMPEG渲染视频,主要是Android端的,由于性能要求,要使用硬解码,但网上大多数教程都是没有libstagefright的,所以个人觉得,生成的so库文件也是没有开启 ...
随机推荐
- Android系统自带的android.util.Base64的实现源码
由于Android逆向还原的时候,经常需要用到android.util.Base64的代码,因此从Android 4.4.4的 系统里抠出来进行备份,懒得用其他的代码进行修改替换了. /* * Cop ...
- Windows核心编程 第六章 线程基础知识 (下)
6.6 线程的一些性质 到现在为止,讲述了如何实现线程函数和如何让系统创建线程以便执行该函数.本节将要介绍系统如何使这些操作获得成功. 图6 - 1显示了系统在创建线程和对线程进行初始化时必须做些什么 ...
- NumPy中文文档搬砖(划掉)学习笔记(1)
原文地址 前言 况下加速Python中的操作运行时.适用于快速数值运算的一个选项是NumPy,它当之无愧地将自己称为使用Python进行科学计算的基本软件包. 当然,很少有人将50微秒(百万分之五十秒 ...
- 大事件,Java被超越了,2021年5月TIOBE编程语言排行榜出炉
TIOBE 头条 TIOBE 5月编程语言排行榜新鲜出炉.前十榜单中,C.Python.Java三大鳌头仍占据前三榜单.去年11月,Python短时间的挤掉Java跃居至榜单第二名:今年5月,Pyth ...
- 【JavaScript】Leetcode每日一题-组合总和4
[JavaScript]Leetcode每日一题-组合总和4 [题目描述] 给你一个由 不同 整数组成的数组 nums ,和一个目标整数 target .请你从 nums 中找出并返回总和为 targ ...
- 启动spring boot项目时报错:java.lang.ClassNotFoundException: javax.servlet.Filter
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring- ...
- python三元(三目)运算
传统的if,else写法 三元运算 name="alex" if 1==1 else "SB"
- 用 shell 脚本制造连接频繁中断的场景
问题的提出 最近在准备客户端的新版本,在内部灰度过程中,发现一类奇怪的 dump,通过查看日志和堆栈,可以确定是因为每次连上后台就被后台断开了.导致多次重连后随机发生的崩溃.dump 和日志都无法提供 ...
- QFNU 10-30 training
7-9 特立独行的幸福 题意:见PTA 思路:其实就是遍历进行查找,利用递归函数,为了解决是特立独行,还要用一个全局数组进行存储所有满足条件的数进行去重标记,最后在输出的时候进行判断是否是只读取过一次 ...
- ZOHO的下一个25年:用心为企业服务
来源:中国软件网 作者:海策 在25周年会上,ZOHO大中华区总裁侯康宁先生豪情壮志,"25岁的ZOHO,已经成长为非典型一线大厂." 1996年,ZOHO成立.截止2021年,Z ...