问题描述
背景
最近,我使用 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 (将#修改为@)