FFmpeg入门详解之67:Qt FFmpeg开发播放器
理解播放器的基本框架
熟悉常用的結構體
AVFormatContext
AVCodecContext
AVCodec
AVFrame
AVPacket
AVStream
理解基本的同步原理
開發(fā)準備
開發(fā)環(huán)境
Windows7
QT 5.9.8 + Creator 4.4.1
第三方庫
FFMPEG 用來讀取碼流以及解碼
SDL2 用來顯示畫面
框架
框圖如圖所示
代碼
線程劃分
主循環(huán)讀取數(shù)據(jù)
音頻線程解碼并播放聲音
視頻線程解碼并顯示視頻
文件劃分
main.c 做初始化工作,讀取碼流,分發(fā)碼流
audio.c 音頻解碼和聲音播放
AudioInit 初始化音頻
AudioClose 釋放資源
AudioDecodeThread 音頻解碼和播放線程
AudioPlay 播放聲音
AudioPacketPush 寫入未解碼的音頻包
AudioPacketSize 當前音頻尚未解碼的數(shù)據(jù)總容量
AudioSetTimeBase 音頻的base time(以時鐘有關系,比如TS為1/90KHZ,另一常見的為1/1000)
vidoe.c 視頻解碼和視頻播放
VideoInit 初始化視頻
VideoClose 釋放資源
VideoDecodeThread 視頻解碼和播放線程
VideoDisplay 顯示幀
VideoPacketPush 寫入未解碼的視頻包
VideoPacketSize 當前視頻尚未解碼的數(shù)據(jù)總容量
VideoGetFirstFrame 是否已經(jīng)解出第一幀
VideoSetTimeBase 視頻的base time
avpacket_queue.c 音頻、視頻隊列,存儲解碼前的數(shù)據(jù)
PacketQueueInit 初始化隊列
PacketQueueGetSize 隊列中所有packet的數(shù)據(jù)長度(單位為字節(jié))
PacketQueuePut 插入元素
PacketQueueTake 讀取元素
log.c 打印日志
LogInit 初始化Log模塊,啟動后將在運行目錄生成一個log.txt的文件,每次程序重啟都會重新產(chǎn)生一個文件并將原來的文件覆蓋。
LogDebug、LogInfo、LogError和LogNotice 對應不同級別的打印,但目前只是通過宏是否實現(xiàn)來控制
FunEntry 用在函數(shù)入口
FunExit 用在函數(shù)出口
clock.c 時鐘同步用
AVClockResetTime 重置時鐘
AVClockGetCurTime 獲取當前時間
AVClockSetTime 設置時間
AVClockEnable 使能時鐘
AVClockDisable 禁用時鐘
AVClockIsEnable 獲取時鐘是否使能
AvPacketQueue
#ifndef?AVPACKETQUEUE_H #define?AVPACKETQUEUE_H #include?<QQueue> extern?"C" { #include?"libavcodec/avcodec.h" #include?"libavformat/avformat.h" } #include?"SDL2/SDL.h" class?AvPacketQueue { public:explicit?AvPacketQueue();void?enqueue(AVPacket?*packet);void?dequeue(AVPacket?*packet,?bool?isBlock);bool?isEmpty();void?empty();int?queueSize(); private:SDL_mutex?*mutex;SDL_cond?*cond;QQueue<AVPacket>?queue; }; #endif?//?AVPACKETQUEUE_H #include?"avpacketqueue.h" AvPacketQueue::AvPacketQueue() {mutex???=?SDL_CreateMutex();cond????=?SDL_CreateCond(); } void?AvPacketQueue::enqueue(AVPacket?*packet) {SDL_LockMutex(mutex);queue.enqueue(*packet);SDL_CondSignal(cond);SDL_UnlockMutex(mutex); } void?AvPacketQueue::dequeue(AVPacket?*packet,?bool?isBlock) {SDL_LockMutex(mutex);while?(1)?{if?(!queue.isEmpty())?{*packet?=?queue.dequeue();break;}?else?if?(!isBlock)?{break;}?else?{SDL_CondWait(cond,?mutex);}}SDL_UnlockMutex(mutex); } void?AvPacketQueue::empty() {SDL_LockMutex(mutex);while?(queue.size()?>?0)?{AVPacket?packet?=?queue.dequeue();av_packet_unref(&packet);}SDL_UnlockMutex(mutex); } bool?AvPacketQueue::isEmpty() {return?queue.isEmpty(); } int?AvPacketQueue::queueSize() {return?queue.size(); }AudioDecoder
#ifndef?AUDIODECODER_H #define?AUDIODECODER_H #include?<QObject> extern?"C" {#include?"libswresample/swresample.h" } #include?"avpacketqueue.h" class?AudioDecoder?:?public?QObject {Q_OBJECT public:explicit?AudioDecoder(QObject?*parent?=?nullptr);int?openAudio(AVFormatContext?*pFormatCtx,?int?index);void?closeAudio();void?pauseAudio(bool?pause);void?stopAudio();int?getVolume();void?setVolume(int?volume);double?getAudioClock();void?packetEnqueue(AVPacket?*packet);void?emptyAudioData();void?setTotalTime(qint64?time); private:int?decodeAudio();static?void?audioCallback(void?*userdata,?quint8?*stream,?int?SDL_AudioBufSize);bool?isStop;bool?isPause;bool?isreadFinished;qint64?totalTime;double?clock;int?volume;AVStream?*stream;quint8?*audioBuf;quint32?audioBufSize;DECLARE_ALIGNED(16,?quint8,?audioBuf1)?[192000];quint32?audioBufSize1;quint32?audioBufIndex;SDL_AudioSpec?spec;quint32?audioDeviceFormat;??//?audio?device?sample?formatquint8?audioDepth;struct?SwrContext?*aCovertCtx;qint64?audioDstChannelLayout;enum?AVSampleFormat?audioDstFmt;???//?audio?decode?sample?formatqint64?audioSrcChannelLayout;int?audioSrcChannels;enum?AVSampleFormat?audioSrcFmt;int?audioSrcFreq;AVCodecContext?*codecCtx;??????????//?audio?codec?contextAvPacketQueue?packetQueue;AVPacket?packet;int?sendReturn; signals:void?playFinished(); public?slots:void?readFileFinished(); }; #endif?//?AUDIODECODER_H #include?<QDebug> #include?"audiodecoder.h" /*?Minimum?SDL?audio?buffer?size,?in?samples.?*/ #define?SDL_AUDIO_MIN_BUFFER_SIZE?512 /*?Calculate?actual?buffer?size?keeping?in?mind?not?cause?too?frequent?audio?callbacks?*/ #define?SDL_AUDIO_MAX_CALLBACKS_PER_SEC?30 AudioDecoder::AudioDecoder(QObject?*parent)?:QObject(parent),isStop(false),isPause(false),isreadFinished(false),totalTime(0),clock(0),volume(SDL_MIX_MAXVOLUME),audioDeviceFormat(AUDIO_F32SYS),aCovertCtx(NULL),sendReturn(0) { } int?AudioDecoder::openAudio(AVFormatContext?*pFormatCtx,?int?index) {AVCodec?*codec;SDL_AudioSpec?wantedSpec;int?wantedNbChannels;const?char?*env;/*??soundtrack?array?use?to?adjust?*/int?nextNbChannels[]???=?{0,?0,?1,?6,?2,?6,?4,?6};int?nextSampleRates[]??=?{0,?44100,?48000,?96000,?192000};int?nextSampleRateIdx?=?FF_ARRAY_ELEMS(nextSampleRates)?-?1;isStop?=?false;isPause?=?false;isreadFinished?=?false;audioSrcFmt?=?AV_SAMPLE_FMT_NONE;audioSrcChannelLayout?=?0;audioSrcFreq?=?0;pFormatCtx->streams[index]->discard?=?AVDISCARD_DEFAULT;stream?=?pFormatCtx->streams[index];codecCtx?=?avcodec_alloc_context3(NULL);avcodec_parameters_to_context(codecCtx,?pFormatCtx->streams[index]->codecpar);/*?find?audio?decoder?*/if?((codec?=?avcodec_find_decoder(codecCtx->codec_id))?==?NULL)?{avcodec_free_context(&codecCtx);qDebug()?<<?"Audio?decoder?not?found.";return?-1;}/*?open?audio?decoder?*/if?(avcodec_open2(codecCtx,?codec,?NULL)?<?0)?{avcodec_free_context(&codecCtx);qDebug()?<<?"Could?not?open?audio?decoder.";return?-1;}totalTime?=?pFormatCtx->duration;env?=?SDL_getenv("SDL_AUDIO_CHANNELS");if?(env)?{qDebug()?<<?"SDL?audio?channels";wantedNbChannels?=?atoi(env);audioDstChannelLayout?=?av_get_default_channel_layout(wantedNbChannels);}wantedNbChannels?=?codecCtx->channels;if?(!audioDstChannelLayout?||(wantedNbChannels?!=?av_get_channel_layout_nb_channels(audioDstChannelLayout)))?{audioDstChannelLayout?=?av_get_default_channel_layout(wantedNbChannels);audioDstChannelLayout?&=?~AV_CH_LAYOUT_STEREO_DOWNMIX;}wantedSpec.channels????=?av_get_channel_layout_nb_channels(audioDstChannelLayout);wantedSpec.freq????????=?codecCtx->sample_rate;if?(wantedSpec.freq?<=?0?||?wantedSpec.channels?<=?0)?{avcodec_free_context(&codecCtx);qDebug()?<<?"Invalid?sample?rate?or?channel?count,?freq:?"?<<?wantedSpec.freq?<<?"?channels:?"?<<?wantedSpec.channels;return?-1;}while?(nextSampleRateIdx?&&?nextSampleRates[nextSampleRateIdx]?>=?wantedSpec.freq)?{nextSampleRateIdx--;}wantedSpec.format??????=?audioDeviceFormat;wantedSpec.silence?????=?0;wantedSpec.samples?????=?FFMAX(SDL_AUDIO_MIN_BUFFER_SIZE,?2?<<?av_log2(wantedSpec.freq?/?SDL_AUDIO_MAX_CALLBACKS_PER_SEC));//音頻回調(diào)函數(shù):最重要wantedSpec.callback????=?&AudioDecoder::audioCallback;wantedSpec.userdata????=?this;/*?This?function?opens?the?audio?device?with?the?desired?parameters,?placing*?the?actual?hardware?parameters?in?the?structure?pointed?to?spec.*/while?(1)?{while?(SDL_OpenAudio(&wantedSpec,?&spec)?<?0)?{qDebug()?<<?QString("SDL_OpenAudio?(%1?channels,?%2?Hz):?%3").arg(wantedSpec.channels).arg(wantedSpec.freq).arg(SDL_GetError());wantedSpec.channels?=?nextNbChannels[FFMIN(7,?wantedSpec.channels)];if?(!wantedSpec.channels)?{wantedSpec.freq?=?nextSampleRates[nextSampleRateIdx--];wantedSpec.channels?=?wantedNbChannels;if?(!wantedSpec.freq)?{avcodec_free_context(&codecCtx);qDebug()?<<?"No?more?combinations?to?try,?audio?open?failed";return?-1;}}audioDstChannelLayout?=?av_get_default_channel_layout(wantedSpec.channels);}if?(spec.format?!=?audioDeviceFormat)?{qDebug()?<<?"SDL?audio?format:?"?<<?wantedSpec.format?<<?"?is?not?supported"<<?",?set?to?advised?audio?format:?"?<<??spec.format;wantedSpec.format?=?spec.format;audioDeviceFormat?=?spec.format;SDL_CloseAudio();}?else?{break;}}if?(spec.channels?!=?wantedSpec.channels)?{audioDstChannelLayout?=?av_get_default_channel_layout(spec.channels);if?(!audioDstChannelLayout)?{avcodec_free_context(&codecCtx);qDebug()?<<?"SDL?advised?channel?count?"?<<?spec.channels?<<?"?is?not?supported!";return?-1;}}/*?set?sample?format?*/switch?(audioDeviceFormat)?{case?AUDIO_U8:audioDstFmt????=?AV_SAMPLE_FMT_U8;audioDepth?=?1;break;case?AUDIO_S16SYS:audioDstFmt????=?AV_SAMPLE_FMT_S16;audioDepth?=?2;break;case?AUDIO_S32SYS:audioDstFmt????=?AV_SAMPLE_FMT_S32;audioDepth?=?4;break;case?AUDIO_F32SYS:audioDstFmt????=?AV_SAMPLE_FMT_FLT;audioDepth?=?4;break;default:audioDstFmt????=?AV_SAMPLE_FMT_S16;audioDepth?=?2;break;}/*?open?sound?*/SDL_PauseAudio(0);return?0; } void?AudioDecoder::closeAudio() {emptyAudioData();SDL_LockAudio();SDL_CloseAudio();SDL_UnlockAudio();avcodec_close(codecCtx);avcodec_free_context(&codecCtx); } void?AudioDecoder::readFileFinished() {isreadFinished?=?true; } void?AudioDecoder::pauseAudio(bool?pause) {isPause?=?pause; } void?AudioDecoder::stopAudio() {isStop?=?true; } void?AudioDecoder::packetEnqueue(AVPacket?*packet) {packetQueue.enqueue(packet); } void?AudioDecoder::emptyAudioData() {audioBuf?=?nullptr;audioBufIndex?=?0;audioBufSize?=?0;audioBufSize1?=?0;clock?=?0;sendReturn?=?0;packetQueue.empty(); } int?AudioDecoder::getVolume() {return?volume; } void?AudioDecoder::setVolume(int?volume) {this->volume?=?volume; } double?AudioDecoder::getAudioClock() {if?(codecCtx)?{/*?control?audio?pts?according?to?audio?buffer?data?size?*/int?hwBufSize???=?audioBufSize?-?audioBufIndex;int?bytesPerSec?=?codecCtx->sample_rate?*?codecCtx->channels?*?audioDepth;clock?-=?static_cast<double>(hwBufSize)?/?bytesPerSec;}return?clock; } void?AudioDecoder::audioCallback(void?*userdata,?quint8?*stream,?int?SDL_AudioBufSize) {AudioDecoder?*decoder?=?(AudioDecoder?*)userdata;int?decodedSize;/*?SDL_BufSize?means?audio?play?buffer?left?size*?while?it?greater?than?0,?means?counld?fill?data?to?it*/while?(SDL_AudioBufSize?>?0)?{if?(decoder->isStop)?{return?;}if?(decoder->isPause)?{SDL_Delay(10);continue;}/*?no?data?in?buffer?*/if?(decoder->audioBufIndex?>=?decoder->audioBufSize)?{decodedSize?=?decoder->decodeAudio();/*?if?error,?just?output?silence?*/if?(decodedSize?<?0)?{/*?if?not?decoded?data,?just?output?silence?*/decoder->audioBufSize?=?1024;decoder->audioBuf?=?nullptr;}?else?{decoder->audioBufSize?=?decodedSize;}decoder->audioBufIndex?=?0;}/*?calculate?number?of?data?that?haven't?play?*/int?left?=?decoder->audioBufSize?-?decoder->audioBufIndex;if?(left?>?SDL_AudioBufSize)?{left?=?SDL_AudioBufSize;}if?(decoder->audioBuf)?{memset(stream,?0,?left);SDL_MixAudio(stream,?decoder->audioBuf?+?decoder->audioBufIndex,?left,?decoder->volume);}SDL_AudioBufSize?-=?left;stream?+=?left;decoder->audioBufIndex?+=?left;} } int?AudioDecoder::decodeAudio() {int?ret;AVFrame?*frame?=?av_frame_alloc();int?resampledDataSize;if?(!frame)?{qDebug()?<<?"Decode?audio?frame?alloc?failed.";return?-1;}if?(isStop)?{return?-1;}if?(packetQueue.queueSize()?<=?0)?{if?(isreadFinished)?{isStop?=?true;SDL_Delay(100);emit?playFinished();}return?-1;}/*?get?new?packet?whiel?last?packet?all?has?been?resolved?*/if?(sendReturn?!=?AVERROR(EAGAIN))?{packetQueue.dequeue(&packet,?true);}if?(!strcmp((char*)packet.data,?"FLUSH"))?{avcodec_flush_buffers(codecCtx);av_packet_unref(&packet);av_frame_free(&frame);sendReturn?=?0;qDebug()?<<?"seek?audio";return?-1;}/*?while?return?-11?means?packet?have?data?not?resolved,*?this?packet?cannot?be?unref*/sendReturn?=?avcodec_send_packet(codecCtx,?&packet);if?((sendReturn?<?0)?&&?(sendReturn?!=?AVERROR(EAGAIN))?&&?(sendReturn?!=?AVERROR_EOF))?{av_packet_unref(&packet);av_frame_free(&frame);qDebug()?<<?"Audio?send?to?decoder?failed,?error?code:?"?<<?sendReturn;return?sendReturn;}ret?=?avcodec_receive_frame(codecCtx,?frame);if?((ret?<?0)?&&?(ret?!=?AVERROR(EAGAIN)))?{av_packet_unref(&packet);av_frame_free(&frame);qDebug()?<<?"Audio?frame?decode?failed,?error?code:?"?<<?ret;return?ret;}if?(frame->pts?!=?AV_NOPTS_VALUE)?{clock?=?av_q2d(stream->time_base)?*?frame->pts; //????????qDebug()?<<?"no?pts";}/*?get?audio?channels?*/qint64?inChannelLayout?=?(frame->channel_layout?&&?frame->channels?==?av_get_channel_layout_nb_channels(frame->channel_layout))??frame->channel_layout?:?av_get_default_channel_layout(frame->channels);if?(frame->format???????!=?audioSrcFmt??????????????||inChannelLayout?????!=?audioSrcChannelLayout????||frame->sample_rate??!=?audioSrcFreq?????????????||!aCovertCtx)?{if?(aCovertCtx)?{swr_free(&aCovertCtx);}/*?init?swr?audio?convert?context?*/aCovertCtx?=?swr_alloc_set_opts(nullptr,?audioDstChannelLayout,?audioDstFmt,?spec.freq,inChannelLayout,?(AVSampleFormat)frame->format?,?frame->sample_rate,?0,?NULL);if?(!aCovertCtx?||?(swr_init(aCovertCtx)?<?0))?{av_packet_unref(&packet);av_frame_free(&frame);return?-1;}audioSrcFmt?????????????=?(AVSampleFormat)frame->format;audioSrcChannelLayout???=?inChannelLayout;audioSrcFreq????????????=?frame->sample_rate;audioSrcChannels????????=?frame->channels;}if?(aCovertCtx)?{const?quint8?**in???=?(const?quint8?**)frame->extended_data;uint8_t?*out[]?=?{audioBuf1};int?outCount?=?sizeof(audioBuf1)?/?spec.channels?/?av_get_bytes_per_sample(audioDstFmt);int?sampleSize?=?swr_convert(aCovertCtx,?out,?outCount,?in,?frame->nb_samples);if?(sampleSize?<?0)?{///qDebug()?<<?"swr?convert?failed";av_packet_unref(&packet);av_frame_free(&frame);return?-1;}if?(sampleSize?==?outCount)?{qDebug()?<<?"audio?buffer?is?probably?too?small";if?(swr_init(aCovertCtx)?<?0)?{swr_free(&aCovertCtx);}}audioBuf?=?audioBuf1;resampledDataSize?=?sampleSize?*?spec.channels?*?av_get_bytes_per_sample(audioDstFmt);}?else?{audioBuf?=?frame->data[0];resampledDataSize?=?av_samples_get_buffer_size(NULL,?frame->channels,?frame->nb_samples,?static_cast<AVSampleFormat>(frame->format),?1);}clock?+=?static_cast<double>(resampledDataSize)?/?(audioDepth?*?codecCtx->channels?*?codecCtx->sample_rate);if?(sendReturn?!=?AVERROR(EAGAIN))?{av_packet_unref(&packet);}av_frame_free(&frame);return?resampledDataSize; }MainDecoder
#ifndef?DECODER_H #define?DECODER_H #include?<QThread> #include?<QImage> extern?"C" { #include?"libavfilter/avfiltergraph.h" #include?"libavfilter/buffersink.h" #include?"libavfilter/buffersrc.h" #include?"libswscale/swscale.h" #include?"libavdevice/avdevice.h" #include?"libavutil/pixfmt.h" #include?"libavutil/opt.h" #include?"libavcodec/avfft.h" #include?"libavutil/imgutils.h" } #include?"audiodecoder.h" class?MainDecoder?:?public?QThread {Q_OBJECT public:enum?PlayState?{STOP,PAUSE,PLAYING,FINISH};explicit?MainDecoder();~MainDecoder();double?getCurrentTime();void?seekProgress(qint64?pos);int?getVolume();void?setVolume(int?volume); private:void?run();void?clearData();void?setPlayState(MainDecoder::PlayState?state);void?displayVideo(QImage?image);static?int?videoThread(void?*arg);double?synchronize(AVFrame?*frame,?double?pts);bool?isRealtime(AVFormatContext?*pFormatCtx);int?initFilter();int?fileType;int?videoIndex;int?audioIndex;int?subtitleIndex;QString?currentFile;QString?currentType;qint64?timeTotal;AVPacket?seekPacket;qint64?seekPos;double?seekTime;PlayState?playState;bool?isStop;bool?gotStop;bool?isPause;bool?isSeek;bool?isReadFinished;bool?isDecodeFinished;AVFormatContext?*pFormatCtx;AVCodecContext?*pCodecCtx;??????????//?video?codec?contextAvPacketQueue?videoQueue;AvPacketQueue?subtitleQueue;AVStream?*videoStream;double?videoClk;????//?video?frame?timestampAudioDecoder?*audioDecoder;AVFilterGraph???*filterGraph;AVFilterContext?*filterSinkCxt;AVFilterContext?*filterSrcCxt; public?slots:void?decoderFile(QString?file,?QString?type);void?stopVideo();void?pauseVideo();void?audioFinished(); signals:void?readFinished();void?gotVideo(QImage?image);void?gotVideoTime(qint64?time);void?playStateChanged(MainDecoder::PlayState?state); }; #endif?//?DECODER_H?大家好,我的第一本書正式出版了,可以在京東各大店鋪搶購哦。
《FFmpeg入門詳解--音視頻原理及應用:梅會東:清華大學出版社》
京東自營鏈接:https://item.jd.com/13377793.html
京東其它鏈接:https://search.jd.com/Search?keyword=FFmpeg%E5%85%A5%E9%97%A8%E8%AF%A6%E8%A7%A3--%E9%9F%B3%E8%A7%86%E9%A2%91%E5%8E%9F%E7%90%86%E5%8F%8A%E5%BA%94%E7%94%A8&enc=utf-8&suggest=1.his.0.0&wq=&pvid=24e80535073b4e1f98e30a3e6963fe81
?
?
出書過程非常艱辛,來回校正了好幾遍,后續(xù)還有FFmpeg系列的其它圖書。
第一本:FFmpeg入門詳解--音視頻原理及應用--梅會東--清華大學出版社
第二本:FFmpeg入門詳解--流媒體直播原理及應用--梅會東--清華大學出版社
第三本:FFmpeg入門詳解--命令行及音視頻特效原理及應用--梅會東--清華大學出版社
第四本:FFmpeg入門詳解--SDK二次開發(fā)及直播美顏原理及應用--梅會東--清華大學出版社
===================================
總結
以上是生活随笔為你收集整理的FFmpeg入门详解之67:Qt FFmpeg开发播放器的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: mysql修复表命令_mysql命令修复
- 下一篇: 列表表格及表单美化