FFmpeg小试牛刀
FFmpeg代码结构
设置log
多媒体文件
多媒体文件其实是个容器
在容器里有很多流(Stream/Track)
每种流是由不同的编码器编码的
从流中读出的数据称为包
在一个包中包含着一个或多个帧
重要结构体
AVFormatContext
AVStream
AVPacket
FFMPEG中结构体很多。最关键的结构体可以分成以下几类:FFMPEG中最关键的结构体之间的关系_ffmpeg 结构体关系-CSDN博客
a) 解协议(http,rtsp,rtmp,mms)
AVIOContext,URLProtocol,URLContext主要存储视音频使用的协议的类型以及状态。URLProtocol存储输入视音频使用的封装格式。每种协议都对应一个URLProtocol结构。(注意:FFMPEG中文件也被当做一种协议“file”)
b) 解封装(flv,avi,rmvb,mp4)
AVFormatContext主要存储视音频封装格式中包含的信息;AVInputFormat存储输入视音频使用的封装格式。每种视音频封装格式都对应一个AVInputFormat 结构。
c) 解码(h264,mpeg2,aac,mp3)
每个AVStream存储一个视频/音频流的相关数据;每个AVStream对应一个AVCodecContext,存储该视频/音频流使用解码方式的相关数据;每个AVCodecContext中对应一个AVCodec,包含该视频/音频对应的解码器。每种解码器都对应一个AVCodec结构。
d) 存数据
视频的话,每个结构一般是存一帧;音频可能有好几帧
解码前数据:AVPacket
解码后数据:AVFrame
结构体之间的关系:
解码前数据:AVPacket
解码后数据:AVFrame
FFmpeg操作步骤:
具体来讲:
简单实操:
一:提取音频
涉及结构体:
AVFormatContext
AVOutputFormat
AVStream
AVPacket
关键步骤:
//1.打开多媒体文件
avformat_open_input(&pFmtCtx, src, NULL, NULL);
//2.从多媒体文件中找到音频流
av_find_best_stream(pFmtCtx, AVMEDIA_TYPE_AUDIO, -1, -1, NULL, 0);
//3.打开目的文件上下文
oFmtCtx = avformat_alloc_context();
outFmt = av_guess_format(NULL, dst, NULL);
//4.为目的文件创建一个新的音频流
avformat_new_stream(oFmtCtx, NULL);
//5.设置音频参数
avcodec_parameters_copy(outStream->codecpar, inStream->codecpar);
//6.绑定
avio_open2(&oFmtCtx->pb, dst, AVIO_FLAG_WRITE, NULL, NULL);
//7.写多媒体文件头到目的文件
avformat_write_header(oFmtCtx, NULL);
//8.从源多媒体文件中读到音频数据到目的文件中
while (av_read_frame(pFmtCtx, &pkt) >= 0)
//设置pts,dts,时间,相关信息
//最后将包写入
av_interleaved_write_frame(oFmtCtx, &pkt);
av_packet_unref(&pkt);
//9.写多媒体文件尾到输出文件中
av_write_trailer(oFmtCtx);
//10.将申请的资源释放掉
avformat_close_input(&pFmtCtx);
avio_close(oFmtCtx->pb);
avformat_free_context(oFmtCtx);
二:提取视频(与提取音频相同)
不同点:
在打开文件中找到视频流
av_find_best_stream(pFmtCtx, AVMEDIA_TYPE_VIDEO, -1, -1, NULL, 0);
出现的问题:
花屏,马赛克
//对视频来讲dts不一定等于pts
pkt.dts = pkt.pts;
//DTS就是Decode Time Stamp,就是说这个帧什么时候被放在编码器去解。 在没有B帧的情况下,DTS和PTS的输出顺序是一样的。
pkt.dts = av_rescale_q_rnd(pkt.dts, inStream->time_base, outStream->time_base, (AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX));
三:转换视频格式
步骤与上基本相同,不同点在于,读取数据后,筛选出音频流,视频流,字幕等,其他流舍掉
以下函数直接复制时间相关信息:
av_packet_rescale_ts(&pkt, inStream->time_base, outStream->time_base);
四:音视频裁剪
不同点:
主要流程与前面相同,不同点在于找到裁剪开始时间与结束时间
av_seek_frame(pFmtCtx, -1, starttime*AV_TIME_BASE, AVSEEK_FLAG_BACKWARD);
AVSEEK_FLAG_BACKWARD:表示如果开始时间点不是I帧,则在开始时间点向后找到下一个I帧
然后在读取pkt的过程中,改变开始时间点为0.
pkt.pts -= pts_start_time[pkt.stream_index];
pkt.dts -= dts_start_time[pkt.stream_index];
出现的问题:
时间戳不对
视频的pts和bts不一定对应,应该让他们相等
if (pkt.dts > pkt.pts)
pkt.pts = pkt.dts;
五:实战 小咖秀
将两个媒体文件中分别抽取音频与视频轨
将音频与视频轨合并成一个新文件
对音频与视频轨进行裁剪
注意点:提取每个视频的流有个对应的index,需要保证与输出文件流的对应
可以判断每个流的长度,超过的就删除packet数据,保证对齐
音频编码:
aac和fdk-aac
首先去ffmpeg文档上找找相关信息,ffmpeg内置的aac编码器是个什么水平需要了解。
这是ffmpeg wiki上关于aac编码方法的wiki地址 https://trac.ffmpeg.org/wiki/Encode/AAC
20200209,目前版本的ffmpeg支持两种aac的编码器,libfdk_aac和内置的aac编码器。
libfdk_aac编码器由于不是GPL证书和ffmpeg的GPL证书有冲突,所以不能编译成二进制文件分发,需要我们自己编译,所以说我们下载到的ffmpeg编译完成的版本通常只有一个内置的aac编码器。
内置aac编码器只支持aac-lc编码。
libfdk_aac则支持aac-lc和aac-he编码。
根据官方的说明,libfdk_aac是优于官方内置的编码器的。就我个人体验来说,ffmpeg内置aac压制的aac文件听同人音声和源文件,反正我是感觉不出来。因为给的320k的码率,已经是很高的一个码率了,估计码率给的够高以后,无论哪个编码器来都一样听不出来。
The Fraunhofer FDK AAC codec library. This is currently the highest-quality AAC encoder available with ffmpeg
ffmpeg以前还支持libfaac,似乎是自己的内置的编码音频质量已经优于faac的质量了,所以后来的版本移除了,所以说ffmpeg在使用aac-lc压制的时候,音频质量已经比较好,跟libfdk_aac比较接近了。
aac-he主要用在一些码率比较低的场合,所以内置编码器其实在我的使用场景下和libfaac差距不大。
下面是wiki百科的描述,自从2016年的更新后,ffmpeg的内置aac编码器已经变得可用,可以和其他主流编码器同台竞技了。
The native AAC encoder created in FFmpeg, and forked with Libav, was considered experimental and poor. A significant amount of work was done for the 3.0 release of FFmpeg (February 2016) to make its version usable and competitive with the rest of the AAC encoders. Libav has not merged this work and continues to use the older version of the AAC encoder. These encoders are LGPL-licensed open-source and can be built for any platform that the FFmpeg or Libav frameworks can be built.
Both FFmpeg and Libav can use the Fraunhofer FDK AAC library via libfdk-aac, and while the FFmpeg native encoder has become stable and good enough for common use, FDK is still considered the highest quality encoder available for use with FFmpeg. [2] Libav also recommends using FDK AAC if it is available. [3]
关于使用libfdk_aac有一个注意点会影响音质,所以说还是内置aac方便,就不用操心那么多了。
Note: libfdk_aac defaults to a low-pass filter of around 14kHz (details). If you want to preserve higher frequencies, use -cutoff 18000. Adjust the number to the upper frequency limit only if you need to; keeping in mind that a higher limit may audibly reduce the overall quality.