美文网首页
android 音视频同步

android 音视频同步

作者: 小明叔叔_乐 | 来源:发表于2020-04-25 11:26 被阅读0次

    1、思路:

    采用视频跟随音频的策略(人对音频敏感,对视频相对不敏感导致):音频通道正常播放,在每次获取音频frame对象放入opensl播放队列后,记录当前音频播放的时间戳;视频通道,获取当前视频frame对象,并在显示一帧画面后,获取视频frame对象的显示时间戳,从而可以得到两者的时间差,根据这个时间差调整视频的播放策略(如丢帧,减少帧间时间,延长帧间时间等)

    2、上代码:

    AudioChannel.cpp
    /**
     * 获取PCM数据
     * @return PCM数据的有效长度
     */
    int AudioChannel::getPcm() {
        ...
        while (isPlaying) {
            ret = frame_queue.get(frame);
            if (!isPlaying) {
                break;
            }
            if (!ret) {
                continue;
            }
            ...      // 转成PCM数据,并放入opensl播放队列中
    
            // 设置当前音频播放的时间戳(音视频同步时使用) --- pts 显示时间戳; dts 解码时间戳
            colock = frame->pts * av_q2d(timeBase);
    
            break;
        }
    
        // 转换成功后,释放frame对象
        releaseFrame(frame);
    
        return out_buffer_size;
    }
    
    VideoChannel.cpp
    /**
     * 从frame queue中获取frame对象,并传给window对象真正显示
     */
    void VideoChannel::showFrame() {
        ...     // 显示frame的前期准备
    
        double delay = 0;
        while (isPlaying) {
            if (!isPlaying) {
                break;
            }
    
            ret = frame_queue.get(frame);
            if (!isPlaying) {
                break;
            }
    
            if (!ret) {
                continue;
            }
    
           ...       // 数据转换并显示
    
            // 视频 与 音频 的时间戳 的差异, timeDif单位为 秒
            videoClock = frame->pts * av_q2d(timeBase);
            if (audioChannel) {
                timeDif = videoClock - audioChannel->colock;
            }
    
            end = getCurrentTimeUs();
            extra_delay =
                    frame->repeat_pict / (double) (2 * fps);   // 如果没有重复帧,这个值为0;为重复帧时,导致的额外延时,单位 秒
    
            // 统一转换为单位 秒  (end - start)是解码一帧消耗的时间
            delay = (1.0f / fps) + extra_delay - (end - start) / 1000000.0f;
            delay = delay < 0 ? 0 : delay;
    
            // 根据timeDif来动态调整休眠时间 --- 音视频同步的精髓
            adjustSleepTime(timeDif, delay);
    
            // 释放 frame, RGBA数据都出来了,frame就没有用了
            releaseFrame(frame);
        }
    
        releaseFrame(frame);
        av_freep(&dst_data[0]);
        isPlaying = false;
    }
    
    /**
     * 根据timeDif,调整sleep时间
     *
     * @param timeDif   视频时钟 - 音频时钟 的时间差
     * @param delay     视频当前帧应该delay的时间
     */
    void VideoChannel::adjustSleepTime(double timeDif, double delay) const {
        if (timeDif >= 0) {
            // 如果视频超前超过1秒,视频增加一帧的播放时间,让音频可以追上来
            if (timeDif > 1) {
                // 延时时间翻倍
                // 延时 ms   --- av_usleep 入参单位是 微秒 us,
                av_usleep(delay * 2 * 1000000);
            } else {
                delay = delay + timeDif;
                delay = delay < 0 ? 0 : delay;
                av_usleep(delay * 1000000);
            };
        }
            // 视频落后
        else {
            // 如果落后超过1秒,就丢帧
            if (timeDif < -1) {
                // 丢帧处理 --- 丢下一帧
                dropFrame(frame_queue);
            } else {
                delay = delay + timeDif;
                delay = delay < 0 ? 0 : delay;
                av_usleep(delay * 1000000);
            }
        }
    }
    

    相关文章

      网友评论

          本文标题:android 音视频同步

          本文链接:https://www.haomeiwen.com/subject/xlmhwhtx.html