基于RTMP拉流的实现
- 时钟与同步
- AVSync类:采用Audio Master方式
- 初始化时钟
- 获取主时钟
- 更新音视频时钟
- 视频进行同步:
视频慢了则丢掉部分视频帧(视觉->画⾯跳帧)
视频快了则继续渲染上⼀帧
- Clock类:
double pts_; // 时钟基础, 当前帧(待播放)显示时间戳
double pts_drift_; // 当前pts与当前系统时钟的差值, audio、video对于该值是独⽴的
double last_updated; // 最后更新的系统时钟
double speed; // 时钟速度控制,控制播放速度
int serial; // 播放序列,所谓播放序列就是⼀段连续的播放动作,⼀个seek操作会启动⼀段新的播放序列
int paused; // = 1 说明是暂停状态
int *queue_serial_; //指向当前数据包队列序列的指针
char name_[MAX_NAME_SIZE+1]; //标注时钟类型
-
主要函数:
- 设置当前时钟:set_clock(double pts, int serial), set_clock_at(double pts, int serial, double time)
- 获取当前时钟:get_clock()
-
时钟的工作原理:
- 需要不断“对时”。对时的⽅法 set_clock_at(Clock *c, double pts, int serial, double time) ,需要⽤pts、serial、time(系统时间)进⾏对时。
- 获取的时间是⼀个估算值。估算是通过对时时记录的pts_drift估算的。
- 1)set_clock 进⾏一次对时,假设这时的 pts 是落后时间 time 的,那么计算 pts_drift = pts - time,计算出pts和time的相对差值。
2)经过一段时间,且在下次对时前,通过 get_clock 来查询时间,因为set_clock时的 pts 已经过时,不能直接拿set_clock时的pts当做这个时钟的时间。
3)可以通过相对差值 pts_drift,并根据当前时刻的时间来估算当前时刻的pts: pts = time + pts_drift。
- 音视频的解码
解码线程独⽴于数据获取线程,其中PacketQueue用于存放从数据获取线程取到的播放时间内的AVPacket。FrameQueue用于存放各自解码后的AVFrame。Clock用于同步⾳视频。解码线程负责将PacketQueue数据解码为AVFrame,并存⼊FrameQueue。
-
AudioDecodeLoop类:用于音频解码。
AACDecoder类:封装了AAC解码器
Init: 初始化解码器,设置参数。
Loop: 循环:1.等待音频包队列足够数据;2.循环:解码,设置帧的pts,获取可写帧,放入FrameQueue。3.获取packet,向解码器发送packet。 -
VideoDecodeLoop类:用于视频解码。
H264Decoder类:封装H264解码器,初始化解码器,解码packet。
Init: 创建h264解码器;初始化解码器;初始化packet_queue_和frame_queue_的成员变量。
Loop: 循环从包队列获取packet解码后放入图像队列。
Post:用于相packet队列添加元素