为什么基于ffmpeg的android视频播放器无法同步视频时间和音频时间?

时间:2021-07-06 03:40:20

标签: android ffmpeg android-ffmpeg

背景

最近,我使用 ffmpeg 编写了我的第一个 Android 视频播放器。但是视频通道的时间比音频通道的时间快2~3倍。

问题

为什么安卓视频播放器音视频不同步?视频大约比音频快 2~3 倍。感谢您的阅读和解答。

代码

简而言之,我使用PacketDispatcher从http hlv源读取AVPacket:

void PacketDispatcher::RealDispatch() {
    while (GetStatus() != DISPATCHER_STOP) {
        ...

        AVPacket *av_packet = av_packet_alloc();
        int ret = av_read_frame(av_format_context, av_packet);

        // PacketDispatcher is who read the AVPacket from http hlv source 
        // and dispatch to decoder by its stream index.
        decoder_map[av_packet->stream_index]->Push(av_packet);
    }
}

然后,由生产者-消费者模式编写的解码器,解码器维护一个队列,用于存储从 PacketDispatcher 接收到的所有 AVPacket。代码如下:

// write to the queue
void BaseDecoder::Push(AVPacket *av_packet) {
    ...
    av_packet_queue.push(av_packet);
    ...
}

// real decode logic
void BaseDecoder::RealDecode() {
    ...
    while (true) {
        // read frame from codec
        AVFrame *av_frame = av_frame_alloc();
        ret = avcodec_receive_frame(av_codec_ctx, av_frame);

        void *decode_result = DecodeFrame(av_frame);
        // send to render
        m_render->Render(decode_result);
    }
}

然后,我在 Render 中做渲染逻辑。 Render 也是由 Producer-Consumer Pattern 编写的,它维护一个队列,用于存储从 Decoder 接收到的 AVFrame,代码如下:

// write AVFrame
void BaseRender::Render(void *frame_data) {
    ...
    frame_queue.push(frame_data);
    ...
}

// render to surface or Open SL
void BaseRender::RealRender() {
    if (m_render_synchronizer && m_render_synchronizer->Sync(frame_data)) {
        continue;
    }
}

最后,同步器会根据帧pts决定休眠时间丢帧帧pts是

frame_data->pts = av_frame->best_effort_timestamp * av_q2d(GetTimeBase());

此外,视频额外延迟是

frame_data->video_extra_delay = av_frame->repeat_pict * 1.0 / fps * 2.0;

RenderSynchronizer 代码如下:

bool RenderSynchronizer::Sync(void *frame_data) {
    auto base_frame_data = static_cast<BaseFrameData *>(frame_data);
    if (base_frame_data->media_type == AVMEDIA_TYPE_AUDIO) {
        audio_pts = pcm_data->pts;
        return false;
    } else if (base_frame_data->media_type == AVMEDIA_TYPE_VIDEO) {
        video_pts = rgba_data->pts;
        return ReceiveVideoFrame(static_cast<RGBAData *>(frame_data));
    }
    return false;
}

bool RenderSynchronizer::ReceiveVideoFrame(RGBAData *rgba_data) {
    if (audio_pts <= 0 || video_pts <= 0) {
        return false;
    }

    double diff = video_pts - audio_pts;
    if (diff > 0) {
        if (diff > 1) {
            av_usleep((unsigned int) (rgba_data->extra_delay * 1000000.0));
        } else {
            av_usleep((unsigned int) ((diff + rgba_data->extra_delay) * 1000000.0));
        }
        return false;
    } else if (diff < 0) {
        LOGD(TAG, "drop video frame");
        return true;
    } else {
        return false;
    }
}

0 个答案:

没有答案