android端音视频同步
目录
android端音视频同步
音视频流是相互独立的。其中音频解码后得到pcm裸流,通过扬声器播放;视频解码后得到yuv再转换为rgb像素格式,通过屏幕(SurfaceView)渲染显示。与音视频同步有关的概念: dts (decode timestamp 解码时间戳)和 pts (presentation timestamp 显示时间戳)。理论上音视频流的时间是呈线性的,为什么需要dts和pts呢?因为视频帧存在: I帧 (关键帧 根据该帧可以重现完整画面)、 P帧 (非关键帧 依赖上一个I帧)和 B帧 (非关键帧 依赖上一个与下一个I帧)。例如某个B帧解码顺序为:1 3 2,而播放顺序为1 2 3(其中1、3为I帧,2为B帧),这就需要引入dts和pts的概念了。
音视频同步的参考时钟有三种: 音频时钟、视频时钟和系统时钟 。以参考时钟为基准,如果pts时间小于当前时间,丢弃该帧数据;如果pts时间大于当前时间,则休眠等待。另外休眠过程中,加上mutex互斥锁。
/**
* 音视频同步方法
* 如果pts时间大于当前时间,则休眠等待
* 如果pts时间小于当前时间,丢弃该帧数据
*/
void player_wait_for_frame(MediaPlayer *player, int64_t stream_time) {
//互斥锁加锁
pthread_mutex_lock(&player->mutex);
for(;;){
int64_t current_video_time = get_play_time(player);
int64_t sleep_time = stream_time - current_video_time;
if (sleep_time < -300000ll) {
//pts时间落后,不用等待
int64_t new_value = player->start_time - sleep_time;
player->start_time = new_value;
pthread_cond_broadcast(&player->cond);
}
if (sleep_time <= MIN_SLEEP_TIME_US) {
//休眠时长小于最小阈值,不用处理
break;
}
if (sleep_time > 500000ll) {
//最大休眠时长
sleep_time = 500000ll;
}
//等待指定时长,超时则退出等待
pthread_cond_timeout_np(&player->cond, &player->mutex,
(unsigned int) (sleep_time / 1000ll));
}
//互斥锁解锁
pthread_mutex_unlock(&player->mutex);
}
获取当前播放时间:
//获取当前播放时间
int64_t get_play_time(MediaPlayer* player){
return (int64_t)(av_gettime() - player->start_time);
}
视频播放时调用音视频同步方法:
//获取最佳的播放时间戳作为pts
int64_t pts = av_frame_get_best_effort_timestamp(player->yuv_frame);
//获取视频的AVStream
AVStream *stream = player->format_context->streams[player->video_stream_index];
//时间转换(不同时间基时间转换)
int64_t time = av_rescale_q(pts, stream->time_base, AV_TIME_BASE_Q);
//音视频帧同步
player_wait_for_frame(player, time);
音频播放时调用音视频同步方法:
//获取pts
int64_t pts = packet->pts;
if (pts != AV_NOPTS_VALUE) {
//获取音频的AVStream
AVStream *stream = player->format_context->streams[player->audio_stream_index];
//pts时间转换后作为音频时钟
player->audio_clock = av_rescale_q(pts, stream->time_base, AV_TIME_BASE_Q);
//音视频帧同步
player_wait_for_frame(player, player->audio_clock + AUDIO_TIME_ADJUST_US);
}
好了,音视频同步介绍完毕。如果各位有什么问题或建议,欢迎交流。
源码地址: