ffmpeg转码MPEG2-TS的音视频同步机制分析
http://blog.chinaunix.net/uid-26000296-id-3483782.html
一、FFmpeg忽略了adaptation_field()数据
FFmpeg忽略了包含PCR值的adaptation_filed数据;
代码(libavformat/mpegts.c)分析如下:
/* 解析TS包 */
int handle_packet(MpegTSContext *ts, const uint8_t *packet)
{
...
pid = AV_RB16(packet + 1) & 0x1fff; //SYNTAX: PID
is_start = packet[1] & 0x40; //SYNTAX: payload_unit_start_indicator
...
/* continuity check (currently not used) */
cc = (packet[3] & 0xf); //SYNTAX: continuity_counter
expected_cc = (packet[3] & 0x10) ? (tss->last_cc + 1) & 0x0f : tss->last_cc;
cc_ok = (tss->last_cc < 0) || (expected_cc == cc);
tss->last_cc = cc;
/* skip adaptation field */
afc = (packet[3] >> 4) & 3; //SYNTAX: adaptation_field_control
p = packet + 4;
if (afc == 0) /* reserved value */
return 0;
if (afc == 2) /* adaptation field only */
return 0;
if (afc == 3)
{
/* skip adapation field */
p += p[0] + 1;
}
...
}
二、解码初始时间戳的计算
原理如下:
a. 分析阶段: 分析多个TS包,并找到第一个PES包的PTS,做为初始偏移量;
b. PTS置零: 分析与初始化阶段完成后,
解码TS的第一个PES包,得到其PTS值,
减去初始偏移量,使得第一个编码后帧的PTS为零;
c. DTS/PTS增量累加;
1. PTS置零代码分析
main(){
|-- ...
|-- parse_options(){
|-- …
|-- opt_input_file(){
|-- …
av_find_stream_info(ic);
timestamp = start_time;
timestamp += ic->start_time;
…
input_files_ts_offset[nb_input_files] =
input_ts_offset - (copy_ts ? 0 : timestamp);
…
}
…
}
|-- transcode(){
|-- …
for( ; received_sigterm == 0; ) {
AVPacket pkt;
…
ret = av_read_frame(is, &pkt);
…
pkt.dts += av_rescale_q(input_files_ts_offset[nb_input_files],
AV_TIME_BASE_Q, ist->st->time_base);
}
}
三、编码音视频帧的DTS/PTS计算
音频帧的DTS/PTS计算:
一个音频帧(对于AAC来说, 是1024个采样点),
相对于音频采样率(如 44100个采样点/second = 44.1KHz)来说,
累加上每帧的增量(1024*1000/44100 = 23ms/frame)
st->time_base.den = 1000 //时钟基, 1 second = 1000 ms
frame_size = 1024 //一帧 = 1024个采样点
st->pts = {val=0,
num=22050,
den=44100}; // 音频采样率
av_frac_add(&st->pts, (int64_t)st->time_base.den * frame_size);
/* f.val = f.val + ((f.num + incr) / f->den) */
static void av_frac_add(AVFrac *f, int64_t incr)
{
int64_t num, den;
num = f->num + incr;
den = f->den;
if (num < 0)
{
f->val += num / den;
num = num % den;
if (num < 0)
{
num += den;
f->val--;
}
}
else if (num >= den)
{
f->val += num / den;
num = num % den;
}
f->num = num;
}
st->pts = {val=23, // 计算后的时间戳
num=31750, // 上一帧未播放完的余值
den=44100}
视频帧的DTS/PTS计算:
一个视频帧,
相对于视频帧率来说(如 25 frames/second),
累加上每帧的增量(1000ms/25frames = 40ms/frame)
time_base.den = 1000
time_base.num = 1
st->pts = {val=0, num=12, den=25},
av_frac_add(&st->pts, (int64_t)st->time_base.den * st->codec->time_base.num);
st->pts = {val=40, num=12, den=25}
四、解码时间戳与编码时间戳的同步机制
正常的转码流程
(ffmpeg version 0.8.10 在ffmpeg.c的transcode函数
for(; received_sigterm == 0;){}
循环中):
step1. 解析PES包,得到时间戳、流索引、PES包长度等数据,并将这个PES包压入到PES包队列;
见libavformat/mpegts.c函数
int mpegts_push_data();
step2. 从PES包队列中取出一个PES包;
见libavformat/utils.c函数
int av_read_frame();
step3. 将这个PES包的PTS和/或DTS减去初始时间戳,
见ffmpeg.c
pkt.dts += av_rescale_q(input_files_ts_offset[ist->file_index], AV_TIME_BASE_Q, ist->st->time_base);
pkt.pts += av_rescale_q(input_files_ts_offset[ist->file_index], AV_TIME_BASE_Q, ist->st->time_base);
并根据音频/视频流的采样率得到下一帧的PTS和/或DTS;
见ffmpeg.c函数
int output_packet();
ist->next_pts = ist->pts = av_rescale_q(pkt->dts, ist->st->time_base, AV_TIME_BASE_Q);
pkt_pts = av_rescale_q(pkt->pts, ist->st->time_base, AV_TIME_BASE_Q);
如果本帧解码得到的时间戳和上一帧解码得到的时间戳的差值超过了设定的阈值,
为了使输出的时间戳连续或同步,
则需要调整, 如,
视频帧时间戳不连续,则丢弃音频帧以同步
音频帧时间戳不连续,则插件静音帧;
或是其它的策略。
step4. 解码这个PES包中的音/视频帧, 并压入到相应的已解码音频/视频帧队列;
见ffmpeg.c函数
int output_packet();
ret = avcodec_decode_audio3(ist->st->codec, samples, &decoded_data_size,&avpkt);
ret = avcodec_decode_video2(ist->st->codec,&picture, &got_output, &avpkt);
step5. 以已解码音频/视频帧队列做为输入, 交错编码音频/视频帧,并将已编码数据压入到输出队列;
见ffmpeg.c函数
void do_video_out();
void do_audio_out();
step6. 根据要编码输出的音频/视频帧号及相应的采样率/帧率计算输出帧的时间戳;
见libavformat/utils.c函数
int compute_pkt_fields2();
step7. 将这个已编码音频/视频帧的数据和时间戳信息一起输出;
见libavformat/flvenc.c函数
int flv_write_packet()
step8. 没有到结束时,跳回到step1.
转码中的时间戳流程:
1. 解码TS包,
libavformat/mpegts.c的函数
int mpegts_push_data(MpegTSFilter *filter,
const uint8_t *buf, int buf_size, int is_start,
int64_t pos);
功能:
解析PES包, 获得时间戳等信息, 并取出负载数据组成ES流。
分析:
int mpegts_push_data(MpegTSFilter *filter,
const uint8_t *buf, int buf_size, int is_start,
int64_t pos)
{
if (pes->header[0] == 0x00 && //SYNTAX: packet_start_code_prefix
pes->header[1] == 0x00 &&
pes->header[2] == 0x01)
{
code = pes->header[3] | 0x100; //SYNTAX: stream_id
pes->total_size = AV_RB16(pes->header + 4); //SYNTAX: PES_packet_length
/* 分配ES的空间 */
pes->buffer = av_malloc(pes->total_size+FF_INPUT_BUFFER_PADDING_SIZE);
if (code != 0x1bc && code != 0x1bf && /* program_stream_map, private_stream_2 */
code != 0x1f0 && code != 0x1f1 && /* ECM, EMM */
code != 0x1ff && code != 0x1f2 && /* program_stream_directory, DSMCC_stream */
code != 0x1f8) /* ITU-T Rec.H.222.1 type E stream
{
flags = pes->header[7]; //SYNTAX: PTS_DTS_flags
if((flags & 0xc0) == ...)
{
pes->pts = ff_parse_pes_pts(r); //SYNTAX: PTS[32...0]
r += 5;
pes->dts = ff_parse_pes_pts(r); //SYNTAX: DTS[32...0]
r += 5;
}
/* 取出PES的负载数据组成TS流 */
memcpy(pes->buffer+pes->data_index, p, buf_size);
}
}
}
五、输入时间戳不边续时的处理机制
目的: 输入时间戳不连续,必须保证输出时间戳的连续。
1. 当视频时间戳连续,而音频时间戳不连续时
不强行修改时间戳,
用插入静音帧来实现重同步
ffmpeg转码MPEG2-TS的音视频同步机制分析的更多相关文章
- ffmpeg 2.3版本号, 关于ffplay音视频同步的分析
近期学习播放器的一些东西.所以接触了ffmpeg,看源代码的过程中.就想了解一下ffplay是怎么处理音视频同步的,之前仅仅大概知道通过pts来进行同步,但对于怎样实现却不甚了解,所以想借助这个机会, ...
- Android 音视频同步机制
一.概述 音视频同步(avsync),是影响多媒体应用体验质量的一个重要因素.而我们在看到音视频同步的时候,最先想到的就是对齐两者的pts,但是实际使用中的各类播放器,其音视频同步机制都比这些复杂的多 ...
- FFmpeg简易播放器的实现-音视频同步
本文为作者原创,转载请注明出处:https://www.cnblogs.com/leisure_chn/p/10284653.html 基于FFmpeg和SDL实现的简易视频播放器,主要分为读取视频文 ...
- 如何理解直播APP源码开发中的音视频同步
视频 直播APP源码的视频的播放过程可以简单理解为一帧一帧的画面按照时间顺序呈现出来的过程,就像在一个本子的每一页画上画,然后快速翻动的感觉. 但是在实际应用中,并不是每一帧都是完整的画面,因为如果直 ...
- 直播APP源码是如何实现音视频同步的
1. 音视频同步原理 1)时间戳 直播APP源码音视频同步主要用于在音视频流的播放过程中,让同一时刻录制的声音和图像在播放的时候尽可能的在同一个时间输出. 解决直播APP源码音视频同步问题的最佳方案 ...
- vlc源码分析(五) 流媒体的音视频同步
vlc播放流媒体时实现音视频同步,简单来说就是发送方发送的RTP包带有时间戳,接收方根据此时间戳不断校正本地时钟,播放音视频时根据本地时钟进行同步播放.首先了解两个概念:stream clock和sy ...
- ffplay的音视频同步分析
以前工作中参与了一些音视频程序的开发,不过使用的都是芯片公司的SDK,没有研究到更深入一层,比如说音视频同步是怎么回事.只好自己抽点时间出来分析开源代码了,做音视频编解码的人都知道ffmpeg,他在各 ...
- ffplay(2.0.1)中的音视频同步
最近在看ffmpeg相关的一些东西,以及一些播放器相关资料和代码. 然后对于ffmpeg-2.0.1版本下的ffplay进行了大概的代码阅读,其中这里把里面的音视频同步,按个人的理解,暂时在这里作个笔 ...
- (转)ffplay的音视频同步分析之视频同步到音频
以前工作中参与了一些音视频程序的开发,不过使用的都是芯片公司的SDK,没有研究到更深入一层,比如说音视频同步是怎么回事.只好自己抽点时间出来分析开源代码了,做音视频编解码的人都知道ffmp ...
随机推荐
- SQL Server数据库学习笔记-三大范式
第一范式(First Normal Form,简称1NF):数据库表中的字段都是单一属性的,不可再分.这个单一属性由基本类型构成,包括整型.实数.字符型.逻辑型.日期型等.要求一个属性只包含一个值,多 ...
- Thread系列——Thread.Sleep(0)
转载自:http://www.cnblogs.com/ATually/archive/2010/10/21/1857261.html 线程这一概念,可以理解成进程中的一个小单元.这个单元是一个独立的执 ...
- Android实现Button事件的处理
Android实现Button事件的处理 开发工具:Andorid Studio 1.3 运行环境:Android 4.4 KitKat 代码实现 首先是最基本的线性布局,给每个控件设立id值,以供代 ...
- 一、JPEG文件格式-----压缩框架
JPEG文件格式 http://wenku.baidu.com/view/4856d31dc281e53a5802ff0d.html 标记名 FF E0 ...
- C++ STL中Map的按Key排序和按Value排序
map是用来存放<key, value>键值对的数据结构,可以很方便快速的根据key查到相应的value.假如存储学生和其成绩(假定不存在重名,当然可以对重名加以区 分),我们用map来进 ...
- 如何用pdfbox-app-1.8.10.jar批处理将pdf文档转换成text文档
1.首先下载pdfbox-app-1.8.10.jar(下载地址:http://pdfbox.apache.org/download.html) 2.将pdfbox-app-1.8.10.jar加载到 ...
- Careercup - Google面试题 - 6407924087783424
2014-05-07 15:17 题目链接 原题: Given an array of n elements (a1,a2,..ai,...,an). You are allow to chose a ...
- 【转】oracle查询不到表的问题
ORACLE的问题解决:Ora-00942:表或视图不存在 分类: 数据库2006-07-05 00:15 10793人阅读 评论(4) 收藏 举报 oraclesqlmanager 由powerde ...
- Analyzer使用第二Y轴,以及同一分析图不同量值使用不同的图形样式
Analyzer的建立分析图后,图中有两个量值,希望能显示成不同的图形样式,如一个是柱图.一个是线图. 1.设置显示多个量值: 3.设置显示出图例,即表明图中量值内容的说明: 2.右键图例中要修改为不 ...
- python 关键字参数
原文地址:http://docs.pythontab.com/python/python3.4/controlflow.html#tut-functions 函数可以通过 关键字参数 的形式来调用,形 ...