可以免费注册的网站,网站建设中网站需求分析报告内容,兰州网站建设怎么选,福州有做网站引流的吗目录
学习课题#xff1a;逐步构建开发播放器【QT5 FFmpeg6 SDL2】
原理 简单分析#xff1a; 下图简单描述了在一个播放过程中#xff0c;假设我们先播放音频#xff0c;对比一个公共时间轴#xff0c;视频就会始终比音频慢0.003s。 我们在日常中用一些播放器播放视频…目录
学习课题逐步构建开发播放器【QT5 FFmpeg6 SDL2】
原理 简单分析 下图简单描述了在一个播放过程中假设我们先播放音频对比一个公共时间轴视频就会始终比音频慢0.003s。 我们在日常中用一些播放器播放视频资源时可能会遇见“画面中人先说话声音后面才听到” 或者是“声音先出来了画面中人物嘴巴还没动”的情况这些情况的发生就是“音频播放当前帧的时间轴位置视频播放当前帧时间轴的位置 或者是”。 所以需要进行音视频同步这里说的同步并不是完全同步只是在“音频”视频时让视频加速追上音频在“音频”视频时让视频减速等待音频 就像是有两个叫”音频“和”视频“的同学在操场上跑一千米两个人的差距非常小”音频“超过了”视频“”视频“就会加速追上去两人几乎保持全程”同步“最后冲刺终点时两个人同时冲线。 如何实现 通过分析我们现在知道的就是要做【在“音频”视频时让视频加速追上音频】【在“音频”视频时让视频减速等待音频】 这个事情因为我们使用了FFmpeg框架在AVFrame[帧结构体]中定义了字段“pts” 解释Presentation timestamp in time_base units (time when frame should be shown to user) 就是指这一帧需要显示给用户的“时间点”。 即在“音频pts”视频pts时让视频加速追上音频 在“音频pts”视频pts时让视频减速等待音频。 步骤 MediaSync模块 1、在audio写入实际播放数据之前记录对应当前帧数据的pts 2、在video读取帧数据开始进行缩放渲染之前判断“视频pts”是否小于音频pts根据结果进行加速或者是等待。 AudioOutPut模块 添加代码在合适的位置设置pts VideoOutPut模块 添加代码在帧读取后判断“视频pts”是否小于音频pts 添加的部分
AudioOutPut 1、添加了一个存放音频pts的队列 2、在SDL回调中把pts设置进MediaSync提供给video获取进行“加速” or “等待” 为什么使用队列 我看过很多的文章他们在实现音视频同步的时候都不会用到队列去存放pts而是使用一些样本计算公式去算出pts然后设置进时钟。 例如“时长音频数据长度bytes/(声道数∗采样率∗位深度/8)””时长采样数/采样率“ 这两条公式在理论上是可行的但是在一些特殊情况下就会变得不同步比如根据公式计算出来的帧时长与实际帧的时长不同即使是非常细微的差距也会导致音视频同步出现异常。 我在做音视频同步的时候就刚好遇到了这种特殊情况根据公式计算出来的pts一直是固定的0.02322而实际帧(AVFrame)结构体下pts字段所表示的每一帧的pts差距并不固定是0.02322有时候会小于有时候会大于我的理解是这个“帧差距”代表的就是这一音频帧实际的持续时间如果我们用公式计算出来的值与实际的不符就会导致在video进行同步时获得了错误的音频pts导致同步出现异常。 //AudioOutPut.h
std::queuedouble*ptsQueue;//音频帧pts队列
MediaSync *sync;
AVRational streamTimeBase;
// 设置音频流的TimeBase
void setStreamTimeBase(AVRational streamTimeBase);
// 添加同步对象
void setSync(MediaSync *sync);//AudioOutPut.cpp
int AudioOutPut::init(int mode) {...fifo av_audio_fifo_alloc(playSampleFmt, playChannels, spec.samples * 5);ptsQueue new std::queuedouble();...
}void AudioOutPut::AudioCallBackFromQueue(Uint8 *stream, int len) {...//locksync-setAudioPts(ptsQueue-front());ptsQueue-pop();//read...
}void AudioOutPut::run() {...while (true) {SDL_LockMutex(mtx);if (av_audio_fifo_space(fifo) playSamples) {//保存ptspts frame-pts * av_q2d(streamTimeBase);ptsQueue-push(pts);av_audio_fifo_write(fifo, (void **) audioBuffer, playSamples);SDL_UnlockMutex(mtx);av_frame_unref(frame);break;}SDL_UnlockMutex(mtx);//队列可用空间不足则延时等待SDL_Delay((double) playSamples / playSampleRate);}...
} void AudioOutPut::setSync(MediaSync *sync) {this-sync sync;
}
void AudioOutPut::setStreamTimeBase(AVRational streamTimeBase) {this-streamTimeBase streamTimeBase;
}VideoOutPut 获取从audio中拿到的pts计算vidio_pts与audio_pts的差距判断进行“加速“还是”等待“ //VideoOutPut.h
MediaSync *sync;
AVRational streamTimeBase;// 设置视频流的streamTimeBase
void setStreamTimeBase(AVRational streamTimeBase);
// 添加同步对象
void setSync(MediaSync *sync);//VideoOutPut.cpp
void VideoOutPut::run() {AVFrame *frame;double pts;double diff;double audio_pts;while (!isStopped) {frame frameQueue-pop(10);if (frame) {//同步pts frame-pts * av_q2d(streamTimeBase);audio_pts sync-getAudioPts();diff pts - audio_pts;if (diff 0) {av_usleep(diff * 1000000.0);}//图像缩放、颜色空间转换sws_scale(swsContext, (const uint8_t *const *) frame-data, frame-linesize, 0, decCtx-height, playFrame-data, playFrame-linesize);av_frame_unref(frame);//视频区域SDL_Rect sdlRect;sdlRect.x 0;sdlRect.y 0;sdlRect.w decCtx-width;sdlRect.h decCtx-height;//渲染到sdl窗口emit refreshImage(sdlRect, playFrame);}}
}void VideoOutPut::setStreamTimeBase(AVRational streamTimeBase) {this-streamTimeBase streamTimeBase;
}
void VideoOutPut::setSync(MediaSync *sync) {this-sync sync;
}完整代码
MediaSync 直接添加get set函数即可单独新建类存放后续可能进行优化拓展 //MediaSync.h
#include mutex/*** 用于进行音视频同步*/
class MediaSync {
private:std::mutex m_mutex; // 互斥锁double m_audioPts0;
public:/*** 设置音频pts* param pts 经过frame-pts * av_q2d(time_base)的pts*/void setAudioPts(double pts);/*** 获取音频经过frame-pts * av_q2d(time_base)的pts* return m_audioPts*/double getAudioPts();
};//MediaSync.cpp
#include MediaSync.h
void MediaSync::setAudioPts(double pts) {m_audioPts pts;
}double MediaSync::getAudioPts() {return m_audioPts;
}
测试运行结果
PlayerMain
//PlayerMain.h
MediaSync *sync;//PlayerMain.cpp
PlayerMain::PlayerMain(QWidget *parent): QWidget(parent), ui(new Ui::PlayerMain) {ui-setupUi(this);sync new MediaSync();// 解复用demuxThread new DemuxThread(audioPacketQueue, videoPacketQueue);demuxThread-setUrl(/Users/mac/Downloads/0911超前派对于文文孟佳爆笑猜词 王源欧阳靖脑洞大开.mp4);// demuxThread-setUrl(/Users/mac/Downloads/23.mp4);demuxThread-start();int ret;// 解码-音频audioDecodeThread new DecodeThread(demuxThread-getCodec(MediaType::Audio),demuxThread-getCodecParameters(MediaType::Audio),audioPacketQueue,audioFrameQueue);audioDecodeThread-init();audioDecodeThread-start();// 解码-视频videoDecodeThread new DecodeThread(demuxThread-getCodec(MediaType::Video),demuxThread-getCodecParameters(MediaType::Video),videoPacketQueue,videoFrameQueue);videoDecodeThread-init();videoDecodeThread-start();//output// audioaudioOutPut new AudioOutPut(audioDecodeThread-dec_ctx, audioFrameQueue);audioOutPut-init(1);audioOutPut-setSync(sync);audioOutPut-setStreamTimeBase(*demuxThread-getStreamTimeBase(MediaType::Audio));// videothis-resize(1920 / 2, 1080 / 2);videoOutPut new VideoOutPut(videoDecodeThread-dec_ctx, videoFrameQueue);videoOutPut-init();videoOutPut-setSync(sync);videoOutPut-setStreamTimeBase(*demuxThread-getStreamTimeBase(MediaType::Video));VideoWidget *videoWidget new VideoWidget(this);connect(videoOutPut, VideoOutPut::refreshImage, videoWidget, VideoWidget::updateImage);videoWidget-show();videoWidget-initSDL();audioOutPut-start();videoOutPut-start();// videoWidget-setParent(this);
} 播放器开发(七)音视频同步实现