解决ffmpeg中的时间戳同步问题_day95

解决ffmpeg中的时间戳同步问题_day95,第1张

本文主要介绍在用FFmpeg进行视频相关开发时涉及到的一些视频基本概念。

在H264协议里,图像以组(GOP,也就是一个序列)为单位进行组织,一个组是一段图像编码后的数据流,以I帧开始,到下一个I帧结束。一个组就是一段内容差异不太大的图像编码后生成的一串数据流。当运动变化比较少时,一个组可以很长,因为运动变化少就代表图像画面的内容变动很小,所以就可以编一个I帧,然后一直P帧、B帧了。当运动变化多时,可能一个序列就比较短了,比如就包含一个I帧和3、4个P帧。

I帧是帧组的第一帧,在一组中只有一个I帧。I帧是帧内编码帧,是一种自带全部信息的独立帧,无需参考其他图像便可独立进行解码,可以简单理解为一张静态画面。如果传输过程中I真丢失,画面最直接的影响就是会卡顿,因为后面的帧都无法正确解码,只能等待下一个GOP。

P帧是帧间预测编码帧,需要参考其前面的I帧或P帧才能进行编码。P帧没有完整的画面数据,只有与其前一参考帧的画面差别的数据。与I帧相比,P帧通常占用更少的数据位,但不足是,由于P帧对前面的P和I参考帧有着复杂的依耐性,因此对传输错误非常敏感,所以如果P帧丢失,画面会出现马赛克现象,因为前向参考帧错误,补齐的并不是真正运动变化后的数据。

B帧是双向预测编码帧,也就是B帧记录的是本帧与前后帧的差别。也就是说要解码B帧,不仅要取得之前的缓存画面,还要解码之后的画面,通过前后画面的与本帧数据的叠加取得最终的画面。B帧压缩率高,但解码时很耗CPU资源。(B帧以前面的I或P帧和后面的P帧为参考帧,B帧不是参考帧,不会造成解码错误的扩散。)

一般来说,I帧的压缩率是7(跟JPG差不多),P帧是20,B帧可以达到50。虽然B帧压缩率高,但是在直播系统中很少使用B帧,一是因为解码很耗CPU,再就是B帧解码需要等待下一个P帧数据,这就会造成解码延时,而直播系统对延时要求很高,所以一般不用B帧。但对于点播系统就不会有这个问题。

PTS(Presentation TimeStamp)是渲染用的时间戳,也就是说,我们的视频帧是按照 PTS 的时间戳来展示的。DTS(Decoding TimeStamp)解码时间戳,是用于视频解码的。

为什么会有2个不同的时间戳呢?这和I帧、P帧和B帧有关,我们分2中情况来看:

如下所示,第1帧是I帧,2-8帧是P帧,展示的顺序是12345678。解码时先解码第1帧,第2帧参考第1帧解码,第3帧参考第2帧解码……也就是解码顺序也是12345678。这种情况下PTS和DTS是一样的,所以此时是没有必要区分2个不同的时间戳的。

如下所示,第1帧是I帧,第4、8帧是P帧,其余是B帧,展示顺序是12345678。解码时先解第1帧,第2帧是B帧,由于它后面的帧还没解码,所以它也没法解码,第3帧也是一样,所以第二解码是第4号的P帧,然后2号B帧参考其前面的第1帧和其后面的第4帧进行解码,然后再是3号B帧解码,然后567号B帧要等到8号的P帧解码后才能解码。所以解码的顺序是14238567,这种情况下展示顺序和解码顺序就不一样,所以就有了PTS和DTS的区分。

FFmpeg中用AVPacket结构体来描述解码前或编码后的压缩包,用AVFrame结构体来描述解码后或编码前的信号帧。对于视频来说,AVFrame就是视频的一帧图像。这帧图像什么时候显示给用户,就取决于它的PTS。DTS是AVPacket里的一个成员,表示这个压缩包应该什么时候被解码。那么PTS和DTS的单位是什么呢?

要回答这个问题,我们先引入FFmpeg中时间基的概念,也就是 time_base ,它是一个AVRational结构体,其定义如下:

time_base是用来度量时间的,比如time_base = {1,25},它的意思是将1秒分成25段,那么每段就是1/25秒,在FFmpeg中函数av_q2d(time_base)就是用来计算一段的时间的,计算结果就是1/25秒。PTS和DTS的单位就是段,比如一个视频中某一帧的pts是800,也就是说有800段,那么它表示多少秒呢,pts av_q2d(time_base)=800 (1/25)=32s,也就是说要在第32秒的时候播放这一帧。

为什么要有时间基转换呢?首先,不同的封装格式时间基是不一样的。其次,在编码前和编码后的时间基也不一致。拿mpegts封装格式25fps来说,非压缩时候的数据(YUV),在ffmpeg中对应的结构体为AVFrame,它的时间基为AVCodecContext 的time_base ,AVRational{1,25}。 压缩后的数据(对应的结构体为AVPacket)对应的时间基为AVStream的time_base,AVRational{1,90000}。

因此数据状态不同,时间基不一样,所以我们必须转换,这就是pts的转换。FFmpeg中有个函数用于时间基的转换,比如time_base1 = {1,25}的时间基下pts1 = 100,那么在time_base2 = {1,90000}的时间基下pts2是多少呢? pts2 = av_rescale_q(pts1,time_base1,time_base2);

在FFmpeg内部还有一个比较常见的时间基,其定义如下:

另外还有个和pts类似的参数duration,它的单位和pts一样,表示两帧直接的间隔,也就是说一帧持续多少个时间刻度。

以AAC音频为例,一个AAC原始帧包含一段时间内1024个采样及相关数据,也就是说一帧有1024个样本,如果采样率为441kHz(1秒采集44100个样本),所以aac音频1秒有44100/1024帧,每一帧的持续时间是1024/44100秒,由此可以计算出每一帧的pts。(不同的封装格式一帧的样本数是不一样的。)

我们平时的一帧数据就是一个NALU,它包含一个字节的头信息和帧的视频数据内容。

MALU头信息中第1个bit是forbidden_zero_bit,在 H264 规范中规定了这一位必须为 0。

第2、3位是nal_ref_idc,指示了这个NALU的重要性,值越大表示越重要,比如为0时NALU解码器可以丢弃它而不影响图像的回放。如果是参考帧的话这个值必须大于0。

第4-8位是nal_unit_type,表这个NALU单元类型,2表示B帧或P帧,5表示I帧,7表示SPS帧,8表示PPS帧。

每个NALU前会添加一个4字节的起始码,每当解码器遇到起始码就表示当前帧结束下一帧开始。

SPS(Sequence Paramater Set),也就是序列参数集,里面保存了一组编码视频序列(也叫组)的全局参数(比如分辨率、码流等参数)。

PPS(Picture Paramater Set),也就是图像参数集,每一帧的编码后数据所依赖的参数保存于图像参数集中。

H264中SPS和PPS分别保存在2个NALU中,SPS的nal_unit_type值是7,PPS的nal_unit_type值是。在封装格式中,PPS通常与SPS一起,保存在视频文件的文件头中,后面的解码都需要用到SPS和PPS中的参数,SPS和PPS丢失会导致无法解码。如果在播放途中切换分辨率,SPS和PPS信息都会发生改变,所以每次切换时都需要把新的SPS和PPS传过来。对于需要从中间开始解码的情况,中间也需要传SPS和PPS过来,否则后面无法解码。

1分组:把几帧图像分为一组(GOP,也就是一个序列),每组的帧数不是固定的,运动变化少时一组的帧数就较多,运动变化多时一组帧数就少。

2定义帧:将每组内各帧图像定义为三种类型,即I帧、B帧和P帧;

3预测帧:以I帧做为基础帧,以I帧预测P帧,再由I帧和P帧预测B帧;

4数据传输:最后将I帧数据与预测的差值信息进行存储和传输。

背景:为测试听歌识曲的sdk是否准确,获取一批测试音频(MP3格式) 的pcm 数据

首先去ffmpeg 官网上获取相应工具 

>

FFmpeg名称中的mpeg来自视频编码标准MPEG,而前缀FF是Fast Forward的首字母缩写。

目录

默认的编译会生成 4 个可执行文件和 8 个静态库。可执行文件包括用于 转码 、 推流 、Dump媒体文件的 ffmpeg 、用于播放媒体文件的 ffplay 、 用于获取媒体文件信息的 ffprobe ,以及作为简单流媒体服务器的 ffserver 。

8个静态库其实就是FFmpeg的8个模块,具体包括如下内容。

比如AAC编码,常见的有两种封装格式

AAC 的 bit stream filter 常常应用在 编码 的过程中。

与音频的AAC编码格式相对应的是视频中的 H264编码 ,它也有两种封装格式

FFmpeg中也提供了对应的 bit stream filter ,称 H264_mp4toannexb ,可以将MP4封装格式的H264数据包转换为annexb封装格式的H264数据 (其实就是裸的H264的数据)包。

H264 的 bit stream filter 常常应用于视频解码过程中。

ffmpeg 是进行媒体文件转码的命令行工具

ffprobe 是用于查看媒体 文件头信息的工具

ffplay 则是用于播放媒体文件的工具

1首先用ffprobe查看一个音频的文件

2输出格式信息format_name、时间长度duration、文件 大小size、比特率bit_rate、流的数目nb_streams等。

3以JSON格式的形式输出具体每一个流 最详细 的信息

4显示帧信息的命令如下:

5查看包信息的命令如下:

ffplay是以FFmpeg框架为基础,外加渲染音视频 的库libSDL来构建的媒体文件播放器。

业界内开源的 ijkPlayer 其实就是基于 ffplay 进行改造的播放器,当然其做了硬件解码以及很多兼容性的工作。

在 ffplay中音画同步的实现方式其实有三种。分别是

并且在 ffplay 中默认的对齐方式也是以 音频 为基准进行对齐的。

首先要声明的是,播放器接收到的视频帧或者音频帧,内部都会有 时间戳(PTS时钟) 来标识它实际应该在什么时刻进行展示。

实际的对齐策略如下:比较视频当前的播放时间和音频当前的播放时间

关键就在于音视频时间的比较以及延迟的计算,当然在比较的过程中会设 置一个 阈值(Threshold) ,若超过预设的阈值就应该做调整(丢帧渲染 或者重复渲染),这就是整个对齐策略。

ffmpeg 就是强大的媒体文件转换工具。它可以转换任何格式的媒体文件,并且还可以用自己的 AudioFilter 以及 VideoFilter 进行处理和编辑。

接下来介绍一个解码的实例,该实例实现的功能非常单一,就是把一个视频文件解码成单独的音频PCM文件和视频YUV文件。

AVFormatContext是API层直接接触到的结构体,它会进行格式的封 装与解封装。

该结构体包含的就是与实际的 编解码 有关的部分。

331 av_register_all

所以该函数的内部实现会先调用 avcodec_register_all 来注册所有configh里面开放的编解码器,然后会注册所有的 Muxer 和 Demuxer (也就是封装格式),最后注册所有的 Protocol (即协议层的东西)。

332 av_find_codec

这里面其实包含了两部分的内容:一部分是寻找 解码器 ,一部分是寻找 编码器 。

333 avcodec_open2

该函数是打开编解码器(Codec)的函数,无论是编码过程还是解码过程,都会用到该函数。

avformat_open_input

根据所提供的文件路径判断文件的格 式,其实就是通过这一步来决定使用的到底是哪一个 Demuxer 。

avformat_find_stream_info

该方法的作用就是把所有 Stream 的 MetaData 信息填充好。

av_read_frame

使用该方法读取出来的数据是 AVPacket 。

对于 音频流 ,一个 AVPacket 可能包含 多 个 AVFrame ,但是对于 视频流 ,一个 AVPacket 只包含 一 个 AVFrame ,该函数最终只会返回一个 AVPacket 结构体。

avcodec_decode

该方法包含了两部分内容:一部分是 解码视频 ,一部分是 解码音频 , 解码 是会委托给对应的解码器来实施的。

avformat_close_input

该函数负责释放对应的资源。

avformat_alloc_output_context2

该函数内部需要调用方法avformat_alloc_context来分配一个 AVFormatContext 结构体。

avio_open2

编码的阶段了,开发者需要将手动封装好的 AVFrame 结构体,作为 avcodec_encode_video 方法的输入,将其编码成为 AVPacket ,然后调用 av_write_frame 方法输出到媒体文件中。

本文参考 音视频开发进阶指南

项目源码地址 - FFmpegDecoder

前言

如此强大的FFmpeg,能够实现视频采集、视频格式转化、视频截图、视频添加水印、视频切片、视频录制、视频推流、更改音视频参数功能等。通过终端命令如何实现这些功能,Richy在本文做一记录,以备之后查阅。

注意:下面一一列举的命令,未归类整理,命令参数供参考。

如果参数有误,大家可对照文章- FFmpeg参数命令 ,进行修改。

第一组

1分离视频音频流

ffmpeg -i input_file -vcodec copy -an output_file_video//分离视频流ffmpeg -i input_file -acodec copy -vn output_file_audio//分离音频流

2视频解复用

ffmpeg –i testmp4 –vcodec copy –an –f m4v test264

ffmpeg –i testavi –vcodec copy –an –f m4v test264

3视频转码

ffmpeg –i testmp4 –vcodec h264 –s 352278 –an –f m4v test264

//转码为码流原始文件

ffmpeg –i testmp4 –vcodec h264 –bf 0 –g 25 –s 352278 –an –f m4v test264 //转码为码流原始文件

ffmpeg –i testavi -vcodec mpeg4 –vtag xvid –qsame test_xvidavi //转码为封装文件

说明: -bf B帧数目控制,-g 关键帧间隔控制,-s 分辨率控制

4视频封装

ffmpeg –i video_file –i audio_file –vcodec copy –acodec copy output_file

5视频剪切

ffmpeg –i testavi –r 1 –f image2 image-%3djpeg //提取

ffmpeg -ss 0:1:30 -t 0:0:20 -i inputavi -vcodec copy -acodec copy outputavi //剪切视频//-r 提取图像的频率,-ss 开始时间,-t 持续时间

6视频录制

ffmpeg –i rtsp://1921683205:5555/test –vcodec copy outavi

7、利用ffmpeg视频切片

主要把视频源切成若干个ts格式的视频片段然后生成一个m3u8的切片文件索引提供给html5的video做hls直播源

命令如下:

ffmpeg -i 视频源地址 -strict -2 -c:v libx264 -c:a aac -f hls m3u8文件输出地址

8、ffmpeg缩放视频

假设原始视频尺寸是 1080p(即 1920×1080 px,16:9),使用下面命令可以缩小到 480p:

命令如下:

ffmpeg -i 视频源地址 -vf scale=853:480 -acodec aac -vcodec h264 视频输出地址(如:outmp4)

各个参数的含义:-i amov 指定待处理视频的文件名-vf scale=853:480 vf 参数用于指定视频滤镜,其中 scale 表示缩放,后面的数字表示缩放至 853×480 px,其中的 853px 是计算而得,因为原始视频的宽高比为 16:9,所以为了让目标视频的高度为 480px,则宽度 = 480 x 9 / 16 = 853-acodec aac 指定音频使用 aac 编码。注:因为 ffmpeg 的内置 aac 编码目前(写这篇文章时)还是试验阶段,故会提示添加参数 “-strict -2” 才能继续,尽管添加即可。又或者使用外部的 libfaac(需要重新编译 ffmpeg)。-vcodec h264 指定视频使用 h264 编码。注:目前手机一般视频拍摄的格式(封装格式、文件格式)为 mov 或者 mp4,这两者的音频编码都是 aac,视频都是 h264。outmp4 指定输出文件名上面的参数 scale=853:480 当中的宽度和高度实际应用场景中通常只需指定一个,比如指定高度为 480 或者 720,至于宽度则可以传入 “-1” 表示由原始视频的宽高比自动计算而得。即参数可以写为:scale=-1:480,当然也可以 scale=480:-1

9、ffmpeg裁剪

有时可能只需要视频的正中一块,而两头的内容不需要,这时可以对视频进行裁剪(crop),比如有一个竖向的视频 1080 x 1920,如果指向保留中间 1080×1080 部分命令如下:ffmpeg -i 视频源地址 -strict -2 -vf crop=1080:1080:0:420 视频输出地址(如:outmp4)

其中的 crop=1080:1080:0:420 才裁剪参数,具体含义是 crop=width:height:x:y,其中 width 和 height 表示裁剪后的尺寸,x:y 表示裁剪区域的左上角坐标。比如当前这个示例,我们只需要保留竖向视频的中间部分,所以 x 不用偏移,故传入0,而 y 则需要向下偏移:(1920 – 1080) / 2 = 420

10 转视频格式

ffmpeng -i sourcemp4 -c:v libx264 -crf 24 destinationflv

其中 -crf 很重要,是控制转码后视频的质量,质量越高,文件也就越大。

此值的范围是 0 到 51:0 表示高清无损;23 是默认值(如果没有指定此参数);51 虽然文件最小,但效果是最差的。

值越小,质量越高,但文件也越大,建议的值范围是 18 到 28。而值 18 是视觉上看起来无损或接近无损的,当然不代表是数据(技术上)的转码无损。

第二组

1ffmpeg 把文件当做直播推送至服务器 (RTMP + FLV)

ffmpeg - re -i demomp4 -c copy - f flv rtmp://wgslbletv/live/streamid

2将直播的媒体保存到本地

ffmpeg -i rtmp://rglsbletv/live/streamid -c copy streamfileflv

3将一个直播流,视频改用h264压缩,音频改用faac压缩,送至另一个直播服务器

ffmpeg -i rtmp://rglsbletv/live/streamidA -c:a libfaac -ar 44100 -ab 48k -c:v libx264 -vpre slow -vpre baseline -f flv rtmp://wglsbletv/live/streamb

4提取视频中的音频,并保存为mp3 然后输出

ffmpeg -i inputavi -b:a 128k outputmp3

第三组

1获取视频的信息

ffmpeg -i videoavi

2将序列合成视频

ffmpeg -f image2 -i image%djpg videompg

上面的命令会把当前目录下的(名字如:image1jpg image2jpg 等)合并成videompg

3将视频分解成序列

ffmpeg -i videompg image%djpg

上面的命令会生成image1jpg image2jpg

支持的格式有:PGM PPM PAM PGMYUV JPEG GIF PNG TIFF SGI

4为视频重新编码以适合在iPod/iPhone上播放

ffmpeg -i source_videoavi input -acodec aac -ab 128kb -vcodec mpeg4 -b 1200kb -mbd 2 -flags +4mv+trell -aic 2 -cmp 2 -subcmp 2 -s 320x180 -title X final_videomp4

5为视频重新编码以适合在PSP上播放

ffmpeg -i source_videoavi -b 300 -s 320x240 -vcodec xvid -ab 32 -ar 24000 -acodec aac final_videomp4

6从视频抽出声音并存为Mp3

ffmpeg -i source_videoavi -vn -ar 44100 -ac 2 -ab 192 -f mp3 soundmp3

7将wav文件转成Mp3

ffmpeg -i son_origineavi -vn -ar 44100 -ac 2 -ab 192 -f mp3 son_finalmp3

8将avi视频转成mpg

ffmpeg -i video_origineavi video_finalempg

9将mpg转成avi

ffmpeg -i video_originempg video_finaleavi

10将avi转成gif动画(未压缩)

ffmpeg -i video_origineavi gif_animegif

11合成视频和音频

ffmpeg -i sonwav -i video_origineavi video_finalempg

12将avi转成flv

ffmpeg -i video_origineavi -ab 56 -ar 44100 -b 200 -r 15 -s 320x240 -f flv video_finaleflv

13将avi转成dv

ffmpeg -i video_origineavi -s pal -r pal -aspect 4:3 -ar 48000 -ac 2 video_finaledv

或者:

ffmpeg -i video_origineavi -target pal-dv video_finaledv

14将avi压缩成divx

ffmpeg -i video_origineavi -s 320x240 -vcodec msmpeg4v2 video_finaleavi

15将Ogg Theora压缩成Mpeg dvd

ffmpeg -i film_sortie_cinelerraogm -s 720x576 -vcodec mpeg2video -acodec mp3 film_terminatempg

16将avi压缩成SVCD mpeg2

NTSC格式:

ffmpeg -i video_origineavi -target ntsc-svcd video_finalempg

PAL格式:

ffmpeg -i video_origineavi -target pal-dvcd video_finalempg

17将avi压缩成VCD mpeg2

NTSC格式:

ffmpeg -i video_origineavi -target ntsc-vcd video_finalempg

PAL格式:

ffmpeg -i video_origineavi -target pal-vcd video_finalempg

18多通道编码

ffmpeg -i fichierentree -pass 2 -passlogfile ffmpeg2pass fichiersortie-2

19从flv提取mp3

ffmpeg -i sourceflv -ab 128k destmp3

第四组

1、将文件当做直播送至live

ffmpeg -re -i localFilemp4 -c copy -f flv rtmp://server/live/streamName

2、将直播媒体保存至本地文件

ffmpeg -i rtmp://server/live/streamName -c copy dumpflv

3、将其中一个直播流,视频改用h264压缩,音频不变,送至另外一个直播服务流

ffmpeg -i rtmp://server/live/originalStream -c:a copy -c:v libx264 -vpre slow -f flv rtmp://server/live/h264Stream

4、将其中一个直播流,视频改用h264压缩,音频改用faac压缩,送至另外一个直播服务流

ffmpeg -i rtmp://server/live/originalStream -c:a libfaac -ar 44100 -ab 48k -c:v libx264 -vpre slow -vpre baseline -f flv rtmp://server/live/h264Stream

5、将其中一个直播流,视频不变,音频改用faac压缩,送至另外一个直播服务流

ffmpeg -i rtmp://server/live/originalStream -acodec libfaac -ar 44100 -ab 48k -vcodec copy -f flv rtmp://server/live/h264_AAC_Stream

6、将一个高清流,复制为几个不同视频清晰度的流重新发布,其中音频不变

ffmpeg -re -i rtmp://server/live/high_FMLE_stream -acodec copy -vcodec x264lib -s 640×360 -b 500k -vpre medium -vpre baseline rtmp://server/live/baseline_500k -acodec copy -vcodec x264lib -s 480×272 -b 300k -vpre medium -vpre baseline rtmp://server/live/baseline_300k -acodec copy -vcodec x264lib -s 320×200 -b 150k -vpre medium -vpre baseline rtmp://server/live/baseline_150k -acodec libfaac -vn -ab 48k rtmp://server/live/audio_only_AAC_48k

7、功能一样,只是采用-x264opts选项

ffmpeg -re -i rtmp://server/live/high_FMLE_stream -c:a copy -c:v x264lib -s 640×360 -x264opts bitrate=500:profile=baseline:preset=slow rtmp://server/live/baseline_500k -c:a copy -c:v x264lib -s 480×272 -x264opts bitrate=300:profile=baseline:preset=slow rtmp://server/live/baseline_300k -c:a copy -c:v x264lib -s 320×200 -x264opts bitrate=150:profile=baseline:preset=slow rtmp://server/live/baseline_150k -c:a libfaac -vn -b:a 48k rtmp://server/live/audio_only_AAC_48k

8、将当前摄像头及音频通过DSSHOW采集,视频h264、音频faac压缩后发布

ffmpeg -r 25 -f dshow -s 640×480 -i video=”video source name”:audio=”audio source name” -vcodec libx264 -b 600k -vpre slow -acodec libfaac -ab 128k -f flv rtmp://server/application/stream_name

9、将一个JPG经过h264压缩循环输出为mp4视频

ffmpegexe -i INPUTjpg -an -vcodec libx264 -coder 1 -flags +loop -cmp +chroma -subq 10 -qcomp 06 -qmin 10 -qmax 51 -qdiff 4 -flags2 +dct8x8 -trellis 2 -partitions +parti8x8+parti4x4 -crf 24 -threads 0 -r 25 -g 25 -y OUTPUTmp4

10、将普通流视频改用h264压缩,音频不变,送至高清流服务(新版本FMS live=1)

ffmpeg -i rtmp://server/live/originalStream -c:a copy -c:v libx264 -vpre slow -f flv “rtmp://server/live/h264Stream live=1〃

文/骚之哈塞给(作者)

以上就是关于解决ffmpeg中的时间戳同步问题_day95全部的内容,包括:解决ffmpeg中的时间戳同步问题_day95、ffmpeg # 视频切割 # copy模式的一点理解、附加: FFmpeg概念理解等相关内容解答,如果想了解更多相关内容,可以关注我们,你们的支持是我们更新的动力!

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/web/9507523.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2023-04-29
下一篇 2023-04-29

发表评论

登录后才能评论

评论列表(0条)

保存