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

问题描述

背景

最近,我使用 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;
    }
}

解决方法

暂无找到可以解决该程序问题的有效方法,小编努力寻找整理中!

如果你已经找到好的解决方法,欢迎将解决方案带上本链接一起发送给小编。

小编邮箱:dio#foxmail.com (将#修改为@)

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...