android 音视频同步

2020-04-25  本文已影响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);
        }
    }
}
上一篇下一篇

猜你喜欢

热点阅读