本文为作者原创,转载请注明出处:https://www.cnblogs.com/leisure_chn/p/10584901.html

FFmpeg编解码处理系列笔记:

[0]. FFmpeg时间戳详解

[1]. FFmpeg编解码处理1-转码全流程简介

[2]. FFmpeg编解码处理2-编解码API详解

[3]. FFmpeg编解码处理3-视频编码

[4]. FFmpeg编解码处理4-音频编码

基于 FFmpeg 4.1 版本。

1. 转码全流程简介

看一下 FFmpeg 常规处理流程:

大流程可以划分为输入、输出、转码、播放四大块。其中转码涉及比较多的处理环节,从图中可以看出,转码功能在整个功能图中占比很大。转码的核心功能在解码和编码两个部分,但在一个可用的示例程序中,编码解码与输入输出是难以分割的。解复用器为解码器提供输入,解码器会输出原始帧,对原始帧可进行各种复杂的滤镜处理,滤镜处理后的帧经编码器生成编码帧,多路流的编码帧经复用器输出到输出文件。

1.1 解复用

从输入文件中读取编码帧,判断流类型,根据流类型将编码帧送入视频解码器或音频解码器。

av_read_frame(ictx.fmt_ctx, &ipacket);
if (codec_type == AVMEDIA_TYPE_VIDEO) {
transcode_video(&stream, &ipacket);
} else if (codec_type == AVMEDIA_TYPE_AUDIO) {
transcode_audio(&stream, &ipacket);
}
else {
av_interleaved_write_frame(octx.fmt_ctx, &ipacket);
}

1.2 解码

将视音频编码帧解码生成原始帧。后文详述。

1.3 滤镜

FFmpeg 提供多种多样的滤镜,用来处理原始帧数据。

本例中,为每个音频流/视频流使用空滤镜,即滤镜图中将 buffer 滤镜和 buffersink 滤镜直接相连。目的是:通过视频 buffersink 滤镜将视频流输出像素格式转换为编码器采用的像素格式;通过音频 abuffersink 滤镜将音频流输出声道布局转换为编码器采用的声道布局。为下一步的编码操作作好准备。如果不使用这种方法,则需要处理图像格式转换和音频重采样,从而确保进入编码器的帧是编码器支持的格式。

当然,例程可扩展,可以很容易的在 buffer 滤镜和 buffersink 滤镜中间插入其他功能滤镜,实现丰富的视音频处理功能。

滤镜的使用方法不是本实验关注的重点。详细用法可参考 "FFmpeg原始帧处理-滤镜API用法

1.4 编码

将原始视音频帧编码生成编码帧。后文详述。

1.5 复用

将编码帧按不同流类型交织写入输出文件。

av_interleaved_write_frame(octx.fmt_ctx, &ipacket);

2. 转码例程简介

转码功能复杂,示例程序很难写得简短,这几篇笔记共用同一份示例代码。在 shell 中运行如下命令下载例程源码:

svn checkout https://github.com/leichn/exercises/trunk/source/ffmpeg/ffmpeg_transcode

例程支持在命令行中指定视音频编码格式以及输出文件封装格式。如果编码格式指定为 "copy",则输出流使用与输入流相同的编码格式。与 FFmpeg 命令不同的是,FFmpeg 命令指定编码器参数为 "copy" 时,将不会启动编解码过程,而仅启用转封装过程,整个过程很快执行完毕;本例程指定编码格式为 "copy" 时,则会使用相同的编码格式进行解码与编码,整个过程比较耗时。

例程验证方法:

./transcode -i input.flv -c:v mpeg2video -c:a mp2 output.ts

和如下命令效果大致一样:

ffmpeg -i input.flv -c:v mpeg2video -c:a mp2 output.ts

源代码文件说明:

Makefile
main.c 转复用转码功能
av_codec.c 编码解码功能
av_filter.c 滤镜处理
open_file.c 打开输入输出文件

转码的主流程主要在 main. c中 transcode_video()、transcode_audio() 和 transcode_audio_with_afifo() 三个函数中。当输入音频帧尺寸能被音频编码器接受时,使用 transcode_audio() 函数;否则,引入音频 FIFO,使每次从 FIFO 中取出的音频帧尺寸能被音频编码器接受,使用 transcode_audio_with_afifo() 函数实现此功能。这几个函数仅提供示意功能,演示音视频转码功能的实现方法,源码纠结、可读性差,暂无时间优化。

2.1 视频转码流程

视频转码函数 transcode_video(),其主要处理流程如下(已删除大量细节代码):

static int transcode_video(const stream_ctx_t *sctx, AVPacket *ipacket)
{
AVFrame *frame_dec = av_frame_alloc();
AVFrame *frame_flt = av_frame_alloc();
AVPacket opacket; // 一个视频packet只包含一个视频frame,但冲洗解码器时一个flush packet会取出
// 多个frame出来,每次循环取处理一个frame
while (1)
{
// 1. 时间基转换,解码
av_packet_rescale_ts(ipacket, sctx->i_stream->time_base, sctx->o_codec_ctx->time_base);
ret = av_decode_frame(sctx->i_codec_ctx, ipacket, &new_packet, frame_dec); // 2. 滤镜处理
ret = filtering_frame(sctx->flt_ctx, frame_dec, frame_flt); // 3. 编码
// 3.1 设置帧类型
frame_flt->pict_type = AV_PICTURE_TYPE_NONE;
// 3.2 编码
ret = av_encode_frame(sctx->o_codec_ctx, frame_flt, &opacket);
// 3.3 更新编码帧中流序号
opacket.stream_index = sctx->stream_idx;
// 3.4 时间基转换,AVPacket.pts和AVPacket.dts的单位是AVStream.time_base,不同的封装格式其
// AVStream.time_base不同所以输出文件中,每个packet需要根据输出封装格式重新计算pts和dts
av_packet_rescale_ts(&opacket, sctx->o_codec_ctx->time_base, sctx->o_stream->time_base); // 4. 将编码后的packet写入输出媒体文件
ret = av_interleaved_write_frame(sctx->o_fmt_ctx, &opacket);
av_packet_unref(&opacket);
} return ret;
}

2.2 音频转码流程

音频转码函数 transcode_audio(),其主要处理流程如下(已删除大量细节代码):

static int transcode_audio_with_afifo(const stream_ctx_t *sctx, AVPacket *ipacket)
{
AVFrame *frame_dec = av_frame_alloc();
AVFrame *frame_flt = av_frame_alloc();
AVFrame *frame_enc = NULL;
AVPacket opacket;
int enc_frame_size = sctx->o_codec_ctx->frame_size;
AVAudioFifo* p_fifo = sctx->aud_fifo;
static int s_pts = 0; while (1) // 处理一个packet,一个音频packet可能包含多个音频frame,循环每次处理一个frame
{
// 1. 时间基转换,解码
av_packet_rescale_ts(ipacket, sctx->i_stream->time_base, sctx->o_codec_ctx->time_base);
ret = av_decode_frame(sctx->i_codec_ctx, ipacket, &new_packet, frame_dec); // 2. 滤镜处理
ret = filtering_frame(sctx->flt_ctx, frame_dec, frame_flt); // 3. 使用音频fifo,从而保证每次送入编码器的音频帧尺寸满足编码器要求
// 3.1 将音频帧写入fifo,音频帧尺寸是解码格式中音频帧尺寸
if (!dec_finished)
{
uint8_t** new_data = frame_flt->extended_data; // 本帧中多个声道音频数据
int new_size = frame_flt->nb_samples; // 本帧中单个声道的采样点数 // FIFO中可读数据小于编码器帧尺寸,则继续往FIFO中写数据
ret = write_frame_to_audio_fifo(p_fifo, new_data, new_size);
} // 3.2 从fifo中取出音频帧,音频帧尺寸是编码格式中音频帧尺寸
// FIFO中可读数据大于编码器帧尺寸,则从FIFO中读走数据进行处理
while ((av_audio_fifo_size(p_fifo) >= enc_frame_size) || dec_finished)
{
// 从FIFO中读取数据,编码,写入输出文件
ret = read_frame_from_audio_fifo(p_fifo, sctx->o_codec_ctx, &frame_enc); // 4. fifo中读取的音频帧没有时间戳信息,重新生成pts
frame_enc->pts = s_pts;
s_pts += ret; flush_encoder:
// 5. 编码
ret = av_encode_frame(sctx->o_codec_ctx, frame_enc, &opacket); // 5.1 更新编码帧中流序号,并进行时间基转换
// AVPacket.pts和AVPacket.dts的单位是AVStream.time_base,不同的封装格式其AVStream.time_base不同
// 所以输出文件中,每个packet需要根据输出封装格式重新计算pts和dts
opacket.stream_index = sctx->stream_idx;
av_packet_rescale_ts(&opacket, sctx->o_codec_ctx->time_base, sctx->o_stream->time_base); // 6. 将编码后的packet写入输出媒体文件
ret = av_interleaved_write_frame(sctx->o_fmt_ctx, &opacket);
} if (finished)
{
break;
}
} return ret;
}

2.3 转码过程中的时间戳处理

在封装格式处理例程中,不深入理解时间戳也没有关系。但在编解码处理例程中,时间戳处理是很重要的一个细节,必须要搞清楚。

容器(文件层)中的时间基(AVStream.time_base)与编解码器上下文(视频层)里的时间基(AVCodecContex.time_base)不一样,解码编码过程中需要进行时间基转换。

视频按帧进行播放,所以原始视频帧时间基为 1/framerate。视频解码前需要处理输入 AVPacket 中各时间参数,将输入容器中的时间基转换为 1/framerate 时间基;视频编码后再处理输出 AVPacket 中各时间参数,将 1/framerate 时间基转换为输出容器中的时间基。

音频按采样点进行播放,所以原始音频帧时间为 1/sample_rate。音频解码前需要处理输入 AVPacket 中各时间参数,将输入容器中的时间基转换为 1/sample_rate 时间基;音频编码后再处理输出 AVPacket 中各时间参数,将 1/sample_rate 时间基转换为输出容器中的时间基。如果引入音频 FIFO,从 FIFO 从读出的音频帧时间戳信息会丢失,需要使用 1/sample_rate 时间基重新为每一个音频帧生成 pts,然后再送入编码器。

解码前的时间基转换:

av_packet_rescale_ts(ipacket, sctx->i_stream->time_base, sctx->o_codec_ctx->time_base);

编码后的时间基转换:

av_packet_rescale_ts(&opacket, sctx->o_codec_ctx->time_base, sctx->o_stream->time_base);

关于时间基与时间戳的详细内容可参考 "FFmpeg时间戳详解"。编解码过程主要关注音视频帧的 pts,用户可不关注 dts,详细说明可参考 "FFmpeg编解码处理3-编解码API详解"。

3. 编译与验证

在 shell 中运行如下命令下载例程源码:

svn checkout https://github.com/leichn/exercises/trunk/source/ffmpeg/ffmpeg_transcode

在源码目录执行 make 命令,生成 transcode 可执行文件

下载测试文件(右键另存为):tnmil2.flv

使用 ffprobe 看一下文件格式:

think@opensuse> ffprobe tnmil2.flv
ffprobe version 4.1 Copyright (c) 2007-2018 the FFmpeg developers
Input #0, flv, from 'tnmil2.flv':
Metadata:
encoder : Lavf58.20.100
Duration: 00:00:13.68, start: 0.057000, bitrate: 474 kb/s
Stream #0:0: Video: h264 (High), yuv420p(progressive), 784x480, 25 fps, 25 tbr, 1k tbn, 50 tbc
Stream #0:1: Audio: aac (LC), 44100 Hz, stereo, fltp, 128 kb/s

使用输入文件中的编码格式和封装格式生成输出文件

./transcode -i tnmil2.flv -c:v copy -c:a copy tnmil2o.flv

指定编码格式和封装格式生成输出文件

./transcode -i tnmil2.flv -c:v mpeg2video -c:a mp2 tnmil2.ts

7. 参考资料

[1]. FFmpeg关于nb_smples,frame_size以及profile的解释https://blog.csdn.net/zhuweigangzwg/article/details/53335941

[2]. What does the output of ffmpeg mean? tbr tbn tbc etc?

[3]. 视频编解码基础概念, https://www.cnblogs.com/leisure_chn/p/10285829.html

[4]. 对ffmpeg的时间戳的理解笔记, https://blog.csdn.net/topsluo/article/details/76239136

[6]. ffmpeg中的时间戳与时间基, http://www.imooc.com/article/91381

[6]. ffmpeg编解码中涉及到的pts详解, http://www.52ffmpeg.com/article/353.html

[7]. 音视频录入的pts和dts问题, https://blog.csdn.net/zhouyongku/article/details/38510747

8. 修改记录

2019-03-23 V1.0 初稿

FFmpeg编解码处理1-转码全流程简介的更多相关文章

  1. FFmpeg编解码处理4-音频编码

    本文为作者原创,转载请注明出处:https://www.cnblogs.com/leisure_chn/p/10584948.html FFmpeg编解码处理系列笔记: [0]. FFmpeg时间戳详 ...

  2. FFmpeg编解码处理3-视频编码

    本文为作者原创,转载请注明出处:https://www.cnblogs.com/leisure_chn/p/10584937.html FFmpeg编解码处理系列笔记: [0]. FFmpeg时间戳详 ...

  3. FFmpeg编解码处理2-编解码API详解

    本文为作者原创,转载请注明出处:https://www.cnblogs.com/leisure_chn/p/10584925.html FFmpeg编解码处理系列笔记: [0]. FFmpeg时间戳详 ...

  4. ffmpeg 编解码详细过程

    ffmpeg编解码详细过程     bobbypollo 转:ffmpeg编解码详细过程 原文地址:ffmpeg编解码详细过程(转)作者:心在飞翔原文出处: http://www.360doc.com ...

  5. 最近在研究FFmpeg编解码

    好几年没上CNBLOGS了, 最近在研究FFmpeg编解码,一个人研究感到很寂寞,所以想通过博客来和大家分享和交流,呵呵. 最近研究的主题是: ANDROID手机同屏技术: 需要用到ANDROID截屏 ...

  6. android MediaCodec 音频编解码的实现——转码

    原文地址:http://blog.csdn.net/tinsanmr/article/details/51049179 从今天开始 每周不定期更新博客,把这一周在工作与学习中遇到的问题做个总结.俗话说 ...

  7. ffmpeg编解码音频AAC

    本次项目的需求:手机端和PC端共享同一个音视频网络源. 所以编解码需要满足手机上编码和解码原来PC端的音视频流. 这里先封装安卓手机端音频的编解码. 编译工作依然是在linux下 ubuntu 12. ...

  8. ffmpeg编解码视频导致噪声增大的一种解决方法

    一.前言 ffmpeg在视音频编解码领域算是一个比较成熟的解决方案了.公司的一款视频编辑软件正是基于ffmpeg做了二次封装,并在此基础上进行音视频的编解码处理.然而,在观察编码后的视频质量时,发现图 ...

  9. FFmpeg 学习(五):FFmpeg 编解码 API 分析

    在上一篇文章 FFmpeg学习(四):FFmpeg API 介绍与通用 API 分析 中,我们简单的讲解了一下FFmpeg 的API基本概念,并分析了一下通用API,本文我们将分析 FFmpeg 在编 ...

随机推荐

  1. 快速解决MariaDB无密码就可以登录的问题

    mysql  Ver 15.1 Distrib 10.1.37-MariaDB, for Linux (x86_64) using readline 5.1 #mysql -uroot -p #del ...

  2. Linux学习笔记:Tomcat安装与使用

    Tomcat是一个Servlet容器服务器,用java实现的. 目录结构 Tomcat的安装也很简单,从官网下载安装包.解压后的tomcat目录 apache-tomcat-7.0.92 下的子目录为 ...

  3. 20175316盛茂淞 2018-2019-2 《Java程序设计》第3周学习总结

    20175316盛茂淞 2018-2019-2 <Java程序设计>第3周学习总结 教材学习内容总结 1.1.编程语言的几个发展阶段: 面向机器语言 面向过程语言 面向对象语言 2.1.类 ...

  4. base operand of '->' has non-pointer type 'const Comple

    base operand of '->' has non-pointer type 'const Comple ->操作符前面的操作数类型不是指针类型 错误原因 函数(&对象) { ...

  5. Chrome书签添加到百度网盘

    一:Chrome是最干净的浏览器了,但是无奈国内的环境导致书签不方便保存到云端,如果保存到本地那么就要经常自己备份之类的: 二:由以上的需求背景终于找到了可以将chrome打开的网页保存到百度网盘里[ ...

  6. handoop安装部署集群

    hdfs 1.0版本 一个namenode (secordary namenode 2.0冷备份) 和多个datanode构成分布式文件系统 mapreduce 一个 jobtracker 协调 ta ...

  7. Python获取当前类的所有成员属性

    # -*- coding: utf-8 -*- class Market(object): def __init__(self): self.title = 'apple' self.count = ...

  8. Swift学习目录

    本学习基于苹果官方Swift学习材料,保留了原版90%左右的内容(一些项目开发中基本不用的知识点没有整理),并根据理解进行整理.如对原版感兴趣,可以直接单击链接阅读和学习. 第一部分 基础篇 1.基本 ...

  9. VS从数据库表生成Model代码

    1.工具——扩展和更新——安装下列插件 2.如图所示,在项目或者MODEL文件夹下添加 3.如图所示,生成了一个datanase.11 4.打开该文件后,将数据库连接字符串改为你自己项目中WebCof ...

  10. Javascript高级编程学习笔记(13)—— 引用类型(2)Array类型

    除了Object类型之外ECMA中最常用的引用类型可能就是Array类型了 并且ECMA中的数组类型和其他大多数编程语言的数组类型存在着很大的区别 今天就介绍一下JS中的Array的特别之处 区别 1 ...