音视频同步介绍
音频和视频是各自线程独立播放的,需要同步行为来保证声画的时间节点是一致的或者时间偏差值在一定的范围内。一般来说是根据音频时间来做同步,也就是将视频同步到音频。从ijkplayer中的代码可以看出来,默认是音频除非音频通道不存在才会是视频。引起音视频不同步的原因主要有两种:一种是音频和视频的数据量不一致而且编码算法不同所引起的解码时间差导致的不同步。并且发送端没有统一的同步时钟;另一种是网络传输延时,网络传输是受到网络的实时传输带宽、传输距离和网络节点的处理速度等因素的影响,在网络阻塞时,媒体信息不能保证以连续的“流”数据方式传输,特别是不能保证数据量大的视频信息的连续传输,从而引起媒体流内和流间的失步。
ijkplayer中的结构体介绍
IjkMediaPlayer
ijkplayer 的结构体,提供播放控制和播放的状态的一些处理,结构体指针再初始化后会保存在java层,提供复用。基本每个jni的方法都会获取java 层对应对象的一个long 型变量,然后强转成此结构体。
FFPlayer
主要与java层交互的结构体,音视频的输出,软硬解码器的设置。
VideoState
FFPlay中的结构体。ijkplayer 直接拿过来包含在FFPlayer中。
Frame_Queue
保存解码后数据的环形数组,不同通道的大小不一致。
Packet_Queue
保存从文件或者流读取出来的解码前数据的队列。
Clock
一个用于音视频同步的结构体
方法介绍
stream_open
对一些结构体进行初始化,然后创建文件读取线程和视频渲染线程
ffplay_video_thread
视频解码线程执行的方法
audio_thread
音频解码线程执行的方法
video_refresh_thread
视频渲染,音视频同步
调用流程
在 stream_open 方法中,会对 Frame_Queue、Packet_Queue 和 Clock 进行初始化,如下所示。
if (frame_queue_init(&is->pictq, &is->videoq, ffp->pictq_size, 1) < 0)
goto fail;
if (frame_queue_init(&is->subpq, &is->subtitleq, SUBPICTURE_QUEUE_SIZE, 0) < 0)
goto fail;
if (frame_queue_init(&is->sampq, &is->audioq, SAMPLE_QUEUE_SIZE, 1) < 0)
goto fail;
if (packet_queue_init(&is->videoq) < 0 ||
packet_queue_init(&is->audioq) < 0 ||
packet_queue_init(&is->subtitleq) < 0)
goto fail;
init_clock(&is->vidclk, &is->videoq.serial);
init_clock(&is->audclk, &is->audioq.serial);
init_clock(&is->extclk, &is->extclk.serial);
同时, stream_open 方法中会启动一个 read_thread 线程。在线程中会根据读取的文件或者流的信息去判断是否存在音频流和视频流,然后通过 stream_component_open 方法找到对应的解码器,启动解码线程。
if (st_index[AVMEDIA_TYPE_AUDIO] >= 0) {
stream_component_open(ffp, st_index[AVMEDIA_TYPE_AUDIO]);
} else {
// 如果音频流不存在,那就没办法通过音频去同步,所以把同步方式改为视频
ffp->av_sync_type = AV_SYNC_VIDEO_MASTER;
is->av_sync_type = ffp->av_sync_type;
}
if (st_index[AVMEDIA_