中国建设银行总行网站,陕西省建设网官网住房和城乡厅官网,网站分析怎么做,虚拟主机与网站建设学习课题#xff1a;逐步构建开发播放器【QT5 FFmpeg6 SDL2】
前言 根据第一章内容#xff0c;我们首先可以先把解复用和解码模块完成#xff0c;其中需要使用到多线程以及队列#xff0c;还需要使用FFmpeg进行解复用和解码动作的实现。 创建BaseQueue基类
BaseQueue.h…学习课题逐步构建开发播放器【QT5 FFmpeg6 SDL2】
前言 根据第一章内容我们首先可以先把解复用和解码模块完成其中需要使用到多线程以及队列还需要使用FFmpeg进行解复用和解码动作的实现。 创建BaseQueue基类
BaseQueue.h
#include condition_variable
#include mutex
#include queueusing namespace std;templateclass T
class BaseQueue {
public:/*** 唤醒所有等待线程设置异常标识为1*/void abort() {m_abort 1;m_cond.notify_all();}/*** push进m_queue** param val 需要push的值* return 1是成功 or -1是m_abort1可能有异常*/int push(T val) {lock_guardmutex lock(m_mutex);if (m_abort 1) {return -1;}m_queue.push(val);m_cond.notify_one();return 0;}/*** 从基类 front 到 val 并执行基类std::queue.pop** param val 存放front值的地址引用* param timeout 持有锁的上限时间(ms)* return 1是成功 or -1是m_abort1可能有异常 or 是m_queue为空*/int pop(T val, int timeout 0) {unique_lockmutex lock(m_mutex);if (m_queue.empty()) {m_cond.wait_for(lock, chrono::microseconds(timeout), [this] {return !m_queue.empty() | m_abort;});}if (m_abort 1) {return -1;}if (m_queue.empty()) {return -2;}// there is address referenceval m_queue.front();m_queue.pop();return 0;}/*** 从基类std::queue.front 获取值保存到引用地址val** param val 存放front值的地址引用* return 1是成功 or -1是m_abort1可能有异常 or 是m_queue为空*/int front(T val) {lock_guardmutex lock(m_mutex);if (m_abort 1) {return -1;}if (m_queue.empty()) {return -2;}val m_queue.front();return 0;}int size() {lock_guardmutex lock(m_mutex);return m_queue.size();}private:int m_abort 0;// 是否中止mutex m_mutex; // 锁condition_variable m_cond;queueT m_queue;
};
创建AVPacketQueue包队列类和AVFrameQueue帧队列类 在AVPacketQueue和AVFrameQueue中分别实现模版BaseQueue基类并且可以添加一些错误的信息打印。 我们可以新建一个头文件FFmpegHeader.h用来存放导入ffmpeg的一些代码后面用的时候导入这个文件就可以了 FFmpegHeader.h
extern C {
#include libavcodec/avcodec.h
#include libavfilter/avfilter.h
#include libavformat/avformat.h
#include libavutil/audio_fifo.h
#include libavutil/avassert.h
#include libavutil/ffversion.h
#include libavutil/frame.h
#include libavutil/imgutils.h
#include libavutil/opt.h
#include libavutil/pixdesc.h
#include libavutil/time.h
#include libswresample/swresample.h
#include libswscale/swscale.h#ifdef ffmpegdevice
#include libavdevice/avdevice.h
#endif
}#include qdatetime.h
#pragma execution_character_set(utf-8)#define TIMEMS qPrintable(QTime::currentTime().toString(HH:mm:ss zzz))
#define TIME qPrintable(QTime::currentTime().toString(HH:mm:ss))
#define QDATE qPrintable(QDate::currentDate().toString(yyyy-MM-dd))
#define QTIME qPrintable(QTime::currentTime().toString(HH-mm-ss))
#define DATETIME qPrintable(QDateTime::currentDateTime().toString(yyyy-MM-dd HH:mm:ss))
#define STRDATETIME qPrintable(QDateTime::currentDateTime().toString(yyyy-MM-dd-HH-mm-ss))
#define STRDATETIMEMS qPrintable(QDateTime::currentDateTime().toString(yyyy-MM-dd-HH-mm-ss-zzz))AVPacketQueue
//AVPacketQueue.h
#include BaseQueue.h
#include FFmpegHeader.h
#include cstdioclass AVPacketQueue {
public:~AVPacketQueue();int push(AVPacket *val);AVPacket *pop(int timeout);void release();int size();private:BaseQueueAVPacket * m_queue;
};//AVPacketQueue.cpp
#include AVPacketQueue.hAVPacketQueue::~AVPacketQueue() {release();m_queue.abort();
}int AVPacketQueue::push(AVPacket *val) {AVPacket *tmp_pkt av_packet_alloc();av_packet_move_ref(tmp_pkt, val);return m_queue.push(tmp_pkt);
}
AVPacket *AVPacketQueue::pop(int timeout) {AVPacket *tmp_pkt nullptr;int ret m_queue.pop(tmp_pkt, timeout);if (ret -1) {printf(AVPacketQueue::pop -- m_abort1可能有异常);}if (ret -2) {printf(AVPacketQueue::pop -- 队列为空);}return tmp_pkt;
}
void AVPacketQueue::release() {while (true) {AVPacket *pkt nullptr;int ret m_queue.pop(pkt, 1);if (ret 0) {break;} else {av_packet_free(pkt);continue;}}
}
int AVPacketQueue::size() {return m_queue.size();
}
AVFrameQueue
//AVFrameQueue.h
#include BaseQueue.h
#include FFmpegHeader.h
#include cstdioclass AVFrameQueue {
public:~AVFrameQueue();int push(AVFrame *val);AVFrame *pop(int timeout);void release();int size();private:BaseQueueAVFrame * m_queue;
};//AVFrameQueue.cpp
#include AVFrameQueue.h
AVFrameQueue::~AVFrameQueue() {release();m_queue.abort();
}int AVFrameQueue::push(AVFrame *val) {AVFrame *tmp_frame av_frame_alloc();av_frame_move_ref(tmp_frame, val);return m_queue.push(tmp_frame);
}
AVFrame *AVFrameQueue::pop(int timeout) {AVFrame *tmp_frame nullptr;int ret m_queue.pop(tmp_frame, timeout);if (ret -1) {printf(AVFrameQueue::pop -- m_abort1可能有异常);}if (ret -2) {printf(AVFrameQueue::pop -- 队列为空);}return tmp_frame;
}
void AVFrameQueue::release() {while (true) {AVFrame *pkt nullptr;int ret m_queue.pop(pkt, 1);if (ret 0) {break;} else {av_frame_free(pkt);continue;}}
}
int AVFrameQueue::size() {return m_queue.size();
}
创建BaseThread抽象类
#include QThread/*** 因为我们后面可能需要用到qt信号传递是否暂停所以使用QThread*/
class BaseThread : public QThread {Q_OBJECT
public:// 初始化virtual int init() 0;// 创建线程 开始run工作virtual int start() 0;// 停止线程 释放资源virtual void stop() {isStopped true;if (m_thread) {if (m_thread-isRunning()) {m_thread-wait(-1);}delete m_thread;m_thread nullptr;}};protected:bool isStopped true; // 是否已经停止 停止时退出线程bool isPlaying false;// 是否正在播放bool isPause false; // 是否暂停QThread *m_thread nullptr;
};
创建DemuxThread解复用线程模块和DecodeThread解码线程模块 DemuxThread解复用线程 1、打开视频文件解封装操作。 2、读取流信息并添加打印信息。 3、解复用(循环分离视频流和音频流)。 DemuxThread
//DemuxThread.h
enum class MediaType {Audio,Video
};class DemuxThread : public BaseThread {
private:QString m_url nullptr;AVFormatContext *ic nullptr;int m_videoStreamIndex -1;int m_audioStreamIndex -1;const AVCodec *m_videoCodec;const AVCodec *m_audioCodec;AVPacketQueue *m_audioQueue;AVPacketQueue *m_videoQueue;public:DemuxThread(AVPacketQueue *mAudioQueue, AVPacketQueue *mVideoQueue);DemuxThread(const QString url, AVPacketQueue *mAudioQueue, AVPacketQueue *mVideoQueue);~DemuxThread() override;// 打开视频文件读取信息int init() override;int start() override;void stop() override;void run() override;void setUrl(const QString url);AVCodecParameters *getCodecParameters(MediaType type);AVRational *getStreamTimeBase(MediaType type);const AVCodec *getCodec(MediaType type);
};//DemuxThread.cpp
#include DemuxThread.h
DemuxThread::DemuxThread(AVPacketQueue *mAudioQueue, AVPacketQueue *mVideoQueue): m_audioQueue(mAudioQueue), m_videoQueue(mVideoQueue) {
}
DemuxThread::DemuxThread(const QString url, AVPacketQueue *mAudioQueue, AVPacketQueue *mVideoQueue): m_url(url), m_audioQueue(mAudioQueue), m_videoQueue(mVideoQueue) {
}DemuxThread::~DemuxThread() {if (m_thread) {this-stop();}
}
int DemuxThread::init() {if (m_url nullptr) {qDebug() 没有设置文件链接;return -1;}ic avformat_alloc_context();int ret;ret avformat_open_input(ic, m_url.toUtf8(), nullptr, nullptr);if (ret 0) {qDebug() avformat_open_input 函数发送错误;return -1;}ret avformat_find_stream_info(ic, nullptr);if (ret 0) {qDebug() avformat_find_stream_info 函数发送错误;return -1;}m_videoStreamIndex av_find_best_stream(ic, AVMEDIA_TYPE_VIDEO, -1, -1, m_videoCodec, 0);if (m_videoStreamIndex 0) {qDebug() 没有找到视频流索引 av_find_best_stream error;return -1;}AVCodecParameters *codecParameters_video ic-streams[m_videoStreamIndex]-codecpar;QString codecNameVideo avcodec_get_name(codecParameters_video-codec_id);qDebug() 视频流 codecNameVideo;m_audioStreamIndex av_find_best_stream(ic, AVMEDIA_TYPE_AUDIO, -1, -1, m_audioCodec, 0);if (m_audioStreamIndex 0) {qDebug() 没有找到音频流索引 av_find_best_stream error;return -1;}AVCodecParameters *codecParameters_audio ic-streams[m_audioStreamIndex]-codecpar;QString codecNameAudio avcodec_get_name(codecParameters_audio-codec_id);qDebug() 音频流 codecNameAudio;return 1;
}
int DemuxThread::start() {if (init() ! 1) {qDebug() 打开文件失败停止创建线程;return -1;}isStopped false;isPlaying true;QThread::start();if (!currentThread()) {qDebug() 线程创建失败;return -1;}return 0;
}
void DemuxThread::stop() {BaseThread::stop();if (ic) {avformat_close_input(ic);ic nullptr;}if (m_videoCodec) {m_videoCodec nullptr;}if (m_audioCodec) {m_audioCodec nullptr;}
}
void DemuxThread::run() {int ret;AVPacket pkt;while (!isStopped) {//if (m_audioQueue-size() 10 || m_videoQueue-size() 10) {// qDebug()解复用线程等待 videoSize m_videoQueue-size()audioSize m_audioQueue-size();msleep(10);// std::this_thread::sleep_for(std::chrono::milliseconds(10));continue;}ret av_read_frame(ic, pkt);if (ret 0) {qDebug() 帧读完;break;}if (pkt.stream_index m_audioStreamIndex) {m_audioQueue-push(pkt);// qDebug() audio pkt queue size: m_audioQueue-size();} else if (pkt.stream_index m_videoStreamIndex) {m_videoQueue-push(pkt);// qDebug() video pkt queue size: m_videoQueue-size();} else {av_packet_unref(pkt);}}
}void DemuxThread::setUrl(const QString url) {m_url url;
}
AVCodecParameters *DemuxThread::getCodecParameters(MediaType type) {switch (type) {case MediaType::Audio:if (m_audioStreamIndex ! -1) {return ic-streams[m_audioStreamIndex]-codecpar;} else {return nullptr;}case MediaType::Video:if (m_videoStreamIndex ! -1) {return ic-streams[m_videoStreamIndex]-codecpar;} else {return nullptr;}}
}
AVRational *DemuxThread::getStreamTimeBase(MediaType type) {switch (type) {case MediaType::Audio:if (m_audioStreamIndex ! -1) {return ic-streams[m_audioStreamIndex]-time_base;} else {return nullptr;}case MediaType::Video:if (m_videoStreamIndex ! -1) {return ic-streams[m_videoStreamIndex]-time_base;} else {return nullptr;}}
}
const AVCodec *DemuxThread::getCodec(MediaType type) {switch (type) {case MediaType::Audio:return m_audioCodec;case MediaType::Video:return m_videoCodec;}
}DecodeThread
//DecodeThread.h
#include BaseThread.h
#include FFmpegHeader.h
#include queue/AVFrameQueue.h
#include queue/AVPacketQueue.h
#include QDebugclass DecodeThread : public BaseThread {
private:const AVCodec *m_codec nullptr;AVCodecParameters *m_par nullptr;AVPacketQueue *m_packetQueue nullptr;AVFrameQueue *m_frameQueue nullptr;public:AVCodecContext *dec_ctx nullptr;DecodeThread(const AVCodec *mCodec, AVCodecParameters *mPar, AVPacketQueue *mPacketQueue, AVFrameQueue *mFrameQueue);~DecodeThread() override;int init() override;int start() override;void stop() override;void run() override;
};//DecodeThread.cpp
#include DecodeThread.h
DecodeThread::DecodeThread(const AVCodec *mCodec, AVCodecParameters *mPar, AVPacketQueue *mPacketQueue, AVFrameQueue *mFrameQueue): m_codec(mCodec), m_par(mPar), m_packetQueue(mPacketQueue), m_frameQueue(mFrameQueue) {
}
DecodeThread::~DecodeThread() {stop();
}
int DecodeThread::init() {if (!m_par) {qDebug() AVCodecParameters 为空;return -1;}dec_ctx avcodec_alloc_context3(nullptr);int ret avcodec_parameters_to_context(dec_ctx, m_par);if (ret 0) {qDebug() avcodec_parameters_to_context error;}ret avcodec_open2(dec_ctx, m_codec, nullptr);if (ret 0) {qDebug() avcodec_open2 error;}return 0;
}
void DecodeThread::run() {AVFrame *frame av_frame_alloc();while (!isStopped) {if (m_frameQueue-size() 10) {// qDebug()解码线程等待;msleep(10);// std::this_thread::sleep_for(std::chrono::milliseconds(10));continue;}AVPacket *pkt m_packetQueue-pop(5);if (pkt) {int ret avcodec_send_packet(dec_ctx, pkt);av_packet_free(pkt);if (ret 0) {qDebug() avcodec_send_packet error;break;}while (true) {ret avcodec_receive_frame(dec_ctx, frame);if (ret 0) {m_frameQueue-push(frame);// qDebug()m_frameQueue size:m_frameQueue-size();continue;} else if (AVERROR(EAGAIN) ret) {break;} else {isStopped true;qDebug() avcodec_receive_frame error;break;}}} else {break;}}
}
int DecodeThread::start() {isStopped false;isPlaying true;QThread::start();if (!currentThread()) {qDebug() 线程创建失败;return -1;}return 0;
}
void DecodeThread::stop() {BaseThread::stop();if (dec_ctx)avcodec_close(dec_ctx);
}测试是否能够正常运行 现在解复用线程模块和解码线程模块都已经完成了测试一下是否正常运行 main.cpp
#include QApplication
#include QPushButton
//-----------
#include queue/AVFrameQueue.h
#include queue/AVPacketQueue.h
#include thread/DecodeThread.h
#include thread/DemuxThread.hint main(int argc, char *argv[]) {QApplication a(argc, argv);QPushButton button(Hello world!, nullptr);button.resize(200, 100);button.show();// 解复用DemuxThread *demuxThread;DecodeThread *audioDecodeThread;DecodeThread *videoDecodeThread;// 解码-音频AVPacketQueue audioPacketQueue;AVFrameQueue audioFrameQueue;// 解码-视频AVPacketQueue videoPacketQueue;AVFrameQueue videoFrameQueue;demuxThread new DemuxThread(audioPacketQueue, videoPacketQueue);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();return QApplication::exec();
}测试完成运行正常