怎么做网站界面分析,建设网站终身免费,临沂网站制作公司,免费下载模板的网站有哪些目录
学习课题#xff1a;逐步构建开发播放器【QT5 FFmpeg6 SDL2】
步骤 VideoOutPut模块 1、初始化【分配缓存、读取信息】 2、开始线程工作【从队列读帧-缩放-发送渲染信号到窗口】 VideoWidget自定义Widget类 1、定义内部变量 2、如果使用SDL#xff0c;需要进…目录
学习课题逐步构建开发播放器【QT5 FFmpeg6 SDL2】
步骤 VideoOutPut模块 1、初始化【分配缓存、读取信息】 2、开始线程工作【从队列读帧-缩放-发送渲染信号到窗口】 VideoWidget自定义Widget类 1、定义内部变量 2、如果使用SDL需要进行初始化 3、接收到信号后需要执行槽函数进行渲染 主要代码
分配缓存 // 根据格式和视频宽高获取一张图像的字节数据大小int byte av_image_get_buffer_size(AV_PIX_FMT_RGB32, videoWidth, videoHeight, 1);// 分配缓存空间buffer (uint8_t *) av_malloc(byte * sizeof(uint8_t));// 类似于格式化已经申请的内存av_image_fill_arrays(playFrame-data, playFrame-linesize, buffer, AV_PIX_FMT_RGB32, videoWidth, videoHeight, 1);// 初始化分配并返回SwsContextswsContext sws_getContext(decCtx-width, decCtx-height, pixelFormat, videoWidth, videoHeight, AV_PIX_FMT_RGB32, SWS_FAST_BILINEAR, nullptr, nullptr, nullptr);
执行缩放
sws_scale(swsContext, (const uint8_t *const *) frame-data, frame-linesize, 0, decCtx-height, playFrame-data, playFrame-linesize);
创建SDL窗口纹理渲染器
// 这里必须要先SDL_CreateWindowFrom然后在设置父类为this否则会直接把父类当成参数作为背景sdlWindow SDL_CreateWindowFrom((void *) winId());//渲染器sdlRenderer SDL_CreateRenderer(sdlWindow, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC);//纹理 大小是视频大小sdlTexture SDL_CreateTexture(sdlRenderer, SDL_PIXELFORMAT_BGRA32, SDL_TEXTUREACCESS_STREAMING, 1920, 1080);SDL_SetTextureScaleMode(sdlTexture, SDL_ScaleModeLinear);
SDL渲染 int ret 1;ret SDL_RenderClear(sdlRenderer);if (ret ! 0) {qDebug() SDL_RenderClear error;}// 帧数据更新到纹理ret SDL_UpdateTexture(sdlTexture, m_originalSDLRect, frame-data[0], frame-linesize[0]);// 如果这里的帧数据已经是YUV则需要使用下面的// ret SDL_UpdateYUVTexture(sdlTexture, sdlRect2, frame-data[0], frame-linesize[0], frame-data[1], frame-linesize[1], frame-data[2], frame-linesize[2]);if (ret ! 0) {qDebug() SDL_UpdateYUVTexture error: SDL_GetError();}// 将纹理绘制到渲染器上ret SDL_RenderCopy(sdlRenderer, sdlTexture, m_originalSDLRect, nullptr);if (ret ! 0) {qDebug() SDL_RenderCopy error;}// 刷新渲染器将内容显示到窗口上SDL_RenderPresent(sdlRenderer); SDL窗口渲染在mac系统上好像有点问题缩放的时候图像会很模糊暂时不知道是什么情况。望知道的朋友告知原因。 QPainter渲染 使用Qt的QPainter进行渲染可以原画像显示不会有模糊的情况。 // 注意这里需要使用RGB格式
QImage image((uchar *) frame-data[0], 1920, 1080, QImage::Format_RGB32);
QPainter painter(this);
painter.drawImage(this-rect(), image);
完整模块
VideoOutPut 1、run函数实现内为什么需要av_usleep(39999) 在run函数中是进行循环读取帧然后缩放最后发送信号进行渲染如果我们不进行sleep哪么就会在极短的时间内循环读取完所有的帧渲染过快导致无法看清图像因此我们需要进行sleep这个时间对于单个视频流来说一般是FPS就是画面每秒传输帧数,通俗来讲就是指动画或视频的画面数。这个值一般是可以通过视频流的一些参数计算出来的我们放到同步部分在说。 // VideoOutPut.h
#include FFmpegHeader.h
#include SDL.h
#include queue/AVFrameQueue.h
#include QDebug
#include QObject
#include QtGui
#include QtWidgets
#include threadclass VideoOutPut : public QObject {Q_OBJECT
private:std::thread *m_thread;bool isStopped true; // 是否已经停止 停止时退出线程bool isPlaying false;// 是否正在播放bool isPause false; // 是否暂停uint8_t *buffer;//存储解码后图片buffer//图像缩放、颜色空间转换上下文SwsContext *swsContext;AVFrame *playFrame; //转换后的帧对象AVFrameQueue *frameQueue;//解码后的帧队列// 解码器上下文AVCodecContext *decCtx;// 音频解码器上下文
public:VideoOutPut(AVCodecContext *dec_ctx, AVFrameQueue *frame_queue);int init();int start();void run();int videoWidth; //视频宽度int videoHeight;//视频高度
Q_SIGNALS:void refreshImage(const SDL_Rect sdlRect, AVFrame *frame);
};// VideoOutPut.cpp
#include VideoOutPut.h
VideoOutPut::VideoOutPut(AVCodecContext *dec_ctx, AVFrameQueue *frame_queue): decCtx(dec_ctx), frameQueue(frame_queue) {
}
int VideoOutPut::init() {//获取分辨率大小videoWidth decCtx-width;videoHeight decCtx-height;playFrame av_frame_alloc();AVPixelFormat pixelFormat decCtx-pix_fmt;qDebug() av_get_pix_fmt_name(pixelFormat);int byte av_image_get_buffer_size(AV_PIX_FMT_RGB32, videoWidth, videoHeight, 1);buffer (uint8_t *) av_malloc(byte * sizeof(uint8_t));av_image_fill_arrays(playFrame-data, playFrame-linesize, buffer, AV_PIX_FMT_RGB32, videoWidth, videoHeight, 1);swsContext sws_getContext(decCtx-width, decCtx-height, pixelFormat, videoWidth, videoHeight, AV_PIX_FMT_RGB32, SWS_FAST_BILINEAR, nullptr, nullptr, nullptr);return 1;
}
int VideoOutPut::start() {m_thread new std::thread(VideoOutPut::run, this);if (!m_thread-joinable()) {qDebug() VideoOutPut视频帧处理线程创建失败;return -1;}isStopped false;isPlaying true;return 0;
}void VideoOutPut::run() {AVFrame *frame;while (!isStopped) {frame frameQueue-pop(10);if (frame) {//图像缩放、颜色空间转换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);av_usleep(39999);}}
}
VideoWidget 我们这里使用到QWidget直接把SDL封装进去做一个自定义类之后使用qt creator 提升一下就行了。 注意在使用时如果要把VideoWidget嵌入到某个widget内部需要在new 之后在单独setParent不然会把整个父类主窗口当作sdl窗口。 //VideoWidget.h
#include FFmpegHeader.h
#include SDL.h
#include QApplication
#include QDebug
#include QMetaType
#include QWidget
#include QtGuiclass VideoWidget : public QWidget {Q_OBJECT
private:SDL_Rect m_originalSDLRect;AVFrame *frame nullptr;SDL_Window *sdlWindow nullptr;SDL_Renderer *sdlRenderer nullptr;SDL_Texture *sdlTexture nullptr;protected:void paintEvent(QPaintEvent *event) override;public:VideoWidget(QWidget *parent 0);~VideoWidget();void initSDL();
public slots:void updateImage(const SDL_Rect sdl_rect, AVFrame *frame);
};//VideoWidget.cpp
#include VideoWidget.h
VideoWidget::VideoWidget(QWidget *parent): QWidget(parent) {// 注册SDL类qRegisterMetaTypeSDL_Rect(SDL_Rect);
}
VideoWidget::~VideoWidget() {if (frame)av_frame_free(frame);if (sdlTexture)SDL_DestroyTexture(sdlTexture);if (sdlRenderer)SDL_DestroyRenderer(sdlRenderer);if (sdlWindow)SDL_DestroyWindow(sdlWindow);SDL_Quit();
}
void VideoWidget::initSDL() {// SDL initif (SDL_Init(SDL_INIT_VIDEO) ! 0) {qDebug() SDL_INIT_VIDEO error;return;}// 这里必须要先SDL_CreateWindowFrom然后在设置父类为this否则会直接把父类当成参数作为背景sdlWindow SDL_CreateWindowFrom((void *) winId());//渲染器sdlRenderer SDL_CreateRenderer(sdlWindow, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC);//纹理 大小是视频大小sdlTexture SDL_CreateTexture(sdlRenderer, SDL_PIXELFORMAT_BGRA32, SDL_TEXTUREACCESS_STREAMING, 1920, 1080);SDL_SetTextureScaleMode(sdlTexture, SDL_ScaleModeLinear);
}
void VideoWidget::updateImage(const SDL_Rect sdl_rect, AVFrame *frame) {this-m_originalSDLRect sdl_rect;this-frame frame;this-update();
}
void VideoWidget::paintEvent(QPaintEvent *event) {if (!frame) {return;}
#if 0//QPainter渲染QImage image((uchar *) frame-data[0], 1920, 1080, QImage::Format_RGB32);QPainter painter(this);painter.drawImage(this-rect(), image);
#endif#if 1//SDL渲染int ret 1;ret SDL_RenderClear(sdlRenderer);if (ret ! 0) {qDebug() SDL_RenderClear error;}// 创建SDL纹理并从表面创建ret SDL_UpdateTexture(sdlTexture, m_originalSDLRect, frame-data[0], frame-linesize[0]);// ret SDL_UpdateYUVTexture(sdlTexture, sdlRect2, frame-data[0], frame-linesize[0], frame-data[1], frame-linesize[1], frame-data[2], frame-linesize[2]);if (ret ! 0) {qDebug() SDL_UpdateYUVTexture error: SDL_GetError();}// 将纹理绘制到渲染器上ret SDL_RenderCopy(sdlRenderer, sdlTexture, m_originalSDLRect, nullptr);if (ret ! 0) {qDebug() SDL_RenderCopy error;}// 刷新渲染器将内容显示到窗口上SDL_RenderPresent(sdlRenderer);
#endif
}
PlayerMain 因为我们使用到了QT所以先简单创建一个qt的ui并且在内调用播放视频的函数测试是否能够看见画面 //PlayerMain.h
#include QWidget
#include QtCore
//-----------
#include queue/AVFrameQueue.h
#include queue/AVPacketQueue.h
#include thread/DecodeThread.h
#include thread/DemuxThread.h
#include widget/VideoWidget.h
#include output/VideoOutPut.hQT_BEGIN_NAMESPACE
namespace Ui {class PlayerMain;
}
QT_END_NAMESPACEclass PlayerMain : public QWidget {Q_OBJECTpublic:explicit PlayerMain(QWidget *parent nullptr);~PlayerMain() override;private:Ui::PlayerMain *ui;// 解复用DemuxThread *demuxThread;DecodeThread *audioDecodeThread;DecodeThread *videoDecodeThread;// 解码-音频AVPacketQueue audioPacketQueue;AVFrameQueue audioFrameQueue;// 解码-视频AVPacketQueue videoPacketQueue;AVFrameQueue videoFrameQueue;VideoOutPut *videoOutPut;
};//PlayerMain.cpp
#include PlayerMain.h
#include ui_PlayerMain.hPlayerMain::PlayerMain(QWidget *parent): QWidget(parent), ui(new Ui::PlayerMain) {ui-setupUi(this);// 解复用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();// video outputthis-resize(1920/2,1080/2);videoOutPut new VideoOutPut(videoDecodeThread-dec_ctx, videoFrameQueue);videoOutPut-init();VideoWidget *videoWidget new VideoWidget(this);connect(videoOutPut, VideoOutPut::refreshImage, videoWidget, VideoWidget::updateImage);videoWidget-show();videoWidget-initSDL();videoOutPut-start();
// videoWidget-setParent(this);
}PlayerMain::~PlayerMain() {delete ui;
}
测试运行结果 播放器开发(五)视频帧处理并用SDL渲染播放 结果