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);
}
好了,音视频同步介绍完毕。如果各位有什么问题或建议,欢迎交流。
源码地址: https://github.com/xufuji456/FFmpegAndroid

发布了63 篇原创文章 · 获赞 179 · 访问量 18万+

猜你喜欢

转载自blog.csdn.net/u011686167/article/details/79364407