当前位置:网站首页>Ffmpeg+sdl+qt is a simple video player
Ffmpeg+sdl+qt is a simple video player
2022-07-29 02:43:00 【rjszcb】
————————————————
Copyright notice : This paper is about CSDN Blogger 「 Deng Wenyao 」 The original article of , follow CC 4.0 BY-SA Copyright agreement , For reprint, please attach the original source link and this statement .
Link to the original text :https://blog.csdn.net/AlonewaitingNew/article/details/109001130
FFmpeg Audio and video decoding
FFmpeg Is a library for audio and video decoding ,FFmpeg The decoding process of can be divided into the following steps :
av_register_all(): Register all components
avformat_open_input(): Open the input video file
av_format_find_stream_info(): Get video file information
avcodec_find_decoder(): Find the corresponding decoder
avcodec_open2(): Turn on the decoder
avcodec_decode_video2(): Decompress a frame of data
avcodec_close(): Turn off decoder
avformat_close_input(): Turn off input video
FFmpeg Data structure of :
because FFmepg yes C Language completion , Therefore, we mainly need to understand the structure type .
FFmpeg The decoded data structure is as follows :
stay AVFormatContext This structure mainly stores video information . The main thing is AVStream*, AVInputFormat. stay AVstream Save the video stream and audio stream information in . namely AVCodecContext. Each corresponding video and audio stream information has a corresponding decoder AVCodec.AVFrame A frame of decoded data is saved in 

FFmpeg code
Based on the above information , I wrote one myself Class, Used to decode video into YUV data .
FFmpeg.h
#pragma once
#include<string>
#define __STDC_CONSTANT_MACROS
#ifdef __cplusplus
extern "C"
{
#endif
#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"
#include "libswscale/swscale.h"
#ifdef __cplusplus
}
#endif
#include<memory>
enum class INITERRO {
INIT_ERRO_NO_ERRO,
INIT_ERRO_ALLOC_FAILED,
INIT_ERRO_OPEN_FAILED,
INIT_ERRO_FIND_FAILED,
INIT_ERRO_NO_VIDEO,
INIT_ERRO_NO_AUDIO,
INIT_ERRO_DECODE_FAILED,
INIT_ERRO_OPEN2_FAILED
};
class FFmpeg
{
public:
FFmpeg();
FFmpeg(std::string path);
INITERRO initFFmpeg(const std::string& path="");
std::shared_ptr<uint8_t> getFrameYUV(int& isVideo);
int getWidth();
int getHeight();
~FFmpeg();
private:
std::string filePath;
int videoIndex;
int audioIndex;
AVFormatContext* pFormatCtx;
AVCodecContext* pCodercCtxVideo;
AVCodecContext* pCodercCtxAudio;
AVCodec* pCodercVideo;
AVFrame* pFrame;
AVFrame* pFrameYUV;
AVPacket* pPacket;
struct SwsContext* imgConvertCtx;
};
FFmpeg.cpp
#include "FFmpeg.h"
#include<iostream>
FFmpeg::FFmpeg()
{
}
FFmpeg::FFmpeg(std::string path)
{
}
INITERRO FFmpeg::initFFmpeg(const std::string& path)
{
if (path.empty() && this->filePath.empty()) {
return INITERRO::INIT_ERRO_OPEN_FAILED;
}
// Give priority to the incoming parameters
std::string pathTemp = path.empty() ? this->filePath : path;
// initialization
av_register_all();
avformat_network_init();
this->pFormatCtx = avformat_alloc_context();
//open_input
if (avformat_open_input(&this->pFormatCtx, pathTemp.c_str(), NULL,NULL) != 0) {
return INITERRO::INIT_ERRO_OPEN_FAILED;
}
//find_stream_infomation
if (avformat_find_stream_info(this->pFormatCtx, NULL) < 0) {
return INITERRO::INIT_ERRO_FIND_FAILED;
}
//find_decoder
videoIndex = -1;
audioIndex = -1;
for (int i = 0; i < this->pFormatCtx->nb_streams; ++i) {
if (this->pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) {
videoIndex = i;
}
if (this->pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO) {
audioIndex = i;
}
}
if (videoIndex != -1) {
pCodercCtxVideo = pFormatCtx->streams[videoIndex]->codec;
pCodercVideo = avcodec_find_decoder(pCodercCtxVideo->codec_id);
if (pCodercVideo == NULL) {
printf("Codec not found.\n");
return INITERRO::INIT_ERRO_DECODE_FAILED;
}
}
if (audioIndex != -1) {
pCodercCtxAudio = pFormatCtx->streams[audioIndex]->codec;
//
}
//open2
if (avcodec_open2(pCodercCtxVideo, pCodercVideo, NULL) < 0) {
return INITERRO::INIT_ERRO_OPEN2_FAILED;
}
//init
pFrame = av_frame_alloc();
pFrameYUV = av_frame_alloc();
uint8_t* outBuffer = (uint8_t*)av_malloc(avpicture_get_size(AV_PIX_FMT_YUV420P, pCodercCtxVideo->width, pCodercCtxVideo->height));
avpicture_fill((AVPicture*)pFrameYUV, outBuffer, AV_PIX_FMT_YUV420P, pCodercCtxVideo->width, pCodercCtxVideo->height);
pPacket = (AVPacket*)av_malloc(sizeof(AVPacket));
//Output Info-----------------------------
/*printf("--------------- File Information ----------------\n"); av_dump_format(pFormatCtx, 0, pathTemp.c_str(), 0); printf("-------------------------------------------------\n");*/
imgConvertCtx = sws_getContext(pCodercCtxVideo->width, pCodercCtxVideo->height, pCodercCtxVideo->pix_fmt,
pCodercCtxVideo->width, pCodercCtxVideo->height, AV_PIX_FMT_YUV420P, 4, NULL, NULL, NULL);
return INITERRO::INIT_ERRO_NO_ERRO;
}
std::shared_ptr<uint8_t> FFmpeg::getFrameYUV(int& isVideo)
{
int y_size = pCodercCtxVideo->width * pCodercCtxVideo->height;
std::shared_ptr<uint8_t> bufferFram(new uint8_t[y_size * 3 / 2]);
int ret = -1;
int gotPic = -1;
if (av_read_frame(pFormatCtx, pPacket) < 0) {
isVideo = -2;
return bufferFram;
}
if (pPacket->stream_index == videoIndex) {
ret = avcodec_decode_video2(pCodercCtxVideo, pFrame, &gotPic, pPacket);
if (gotPic) {
sws_scale(imgConvertCtx, (const uint8_t* const*)pFrame->data, pFrame->linesize, 0, pCodercCtxVideo->height,
pFrameYUV->data, pFrameYUV->linesize);
// U V The data are Y Of 1/4 Reduce the width and height by half
memcpy(bufferFram.get(), pFrameYUV->data[0], y_size);
memcpy(bufferFram.get() + y_size, pFrameYUV->data[1], y_size / 4);
memcpy(bufferFram.get() + y_size + y_size / 4, pFrameYUV->data[2], y_size / 4);
isVideo = 1;
}
else {
isVideo = -1;
}
}
else {
isVideo = 0;
}
av_free_packet(pPacket);
return bufferFram;
}
int FFmpeg::getWidth()
{
return this->pCodercCtxVideo->width;
}
int FFmpeg::getHeight()
{
return this->pCodercCtxVideo->height;
}
FFmpeg::~FFmpeg()
{
sws_freeContext(imgConvertCtx);
av_frame_free(&pFrameYUV);
av_frame_free(&pFrame);
avcodec_close(pCodercCtxVideo);
avcodec_close(pCodercCtxAudio);
avformat_close_input(&pFormatCtx);
}
Finally, the function returns a smart pointer , The pointer points to an array , This array contains YUV data . Use SDL The player can play the video 

SDLPlayer.h
#pragma once
#include<string>
#ifdef __cplusplus
extern "C"
{
#endif
#include"sdl/SDL.h"
#ifdef __cplusplus
}
#endif
//Refresh Event
#define REFRESH_EVENT (SDL_USEREVENT + 1)
//Break
#define BREAK_EVENT (SDL_USEREVENT + 2)
class SDLPlayer
{
public:
SDLPlayer();
int initPlayer(void* winID=NULL);
void setHeight(int height);
void setWidth(int width);
void setPlayerTitle(std::string title);
int playYUV(uint8_t* buffer, SDL_Rect sdlRect, int delayTime = 40);
int playYUV(uint8_t* buffer, int delayTime = 40);
void pause();
void start();
void quit();
static int refreshThread(void* opaque);
~SDLPlayer();
private:
int windowH;
int windowW;
int height;
int width;
std::string title;
//SDL WINDOW
SDL_Window* window;
SDL_Texture* sdlTexture;
SDL_Renderer* sdlRanderer;
// event
SDL_Event event;
int threadExit;
int threadPause;
int delayTime;
};
SDLPlayer.cpp
#include "SDLPlayer.h"
SDLPlayer::SDLPlayer():
height(420),
width(640),
title("SDL player"),
window(nullptr),
sdlTexture(nullptr),
sdlRanderer(nullptr),
threadExit(0),
threadPause(0),
delayTime(40)
{
}
int SDLPlayer::initPlayer(void* winID)
{
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER)) {
printf("Could not initialize SDL - %s\n", SDL_GetError());
return -1;
}
windowW = this->width;
windowH = this->height;
if (winID != NULL) {
window = SDL_CreateWindowFrom(winID);
}
else {
window = SDL_CreateWindow(title.c_str(), SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
windowW, windowH, SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE);
}
if (window == nullptr) {
printf("SDL: could not create window - exiting:%s\n", SDL_GetError());
return -1;
}
sdlRanderer = SDL_CreateRenderer(window, -1, 0);
SDL_ShowWindow(window);
Uint32 pixformat = 0;
pixformat = SDL_PIXELFORMAT_IYUV;
sdlTexture = SDL_CreateTexture(sdlRanderer, pixformat,
SDL_TEXTUREACCESS_STREAMING, this->width, this->height);
// Create a new thread
SDL_CreateThread(SDLPlayer::refreshThread, NULL, this);
return 1;
}
void SDLPlayer::setHeight(int _height)
{
height = _height;
}
void SDLPlayer::setWidth(int _width)
{
this->width = _width;
}
void SDLPlayer::setPlayerTitle(std::string _title)
{
title = _title;
}
int SDLPlayer::playYUV(uint8_t* buffer, SDL_Rect sdlRect, int delayTime)
{
SDL_WaitEvent(&event);
if (event.type == REFRESH_EVENT) {
SDL_UpdateTexture(sdlTexture, NULL, buffer, this->width);
SDL_RenderClear(sdlRanderer);
SDL_RenderCopy(sdlRanderer, sdlTexture, NULL, &sdlRect);
SDL_RenderPresent(sdlRanderer);
//Delay 40ms
this->delayTime = delayTime;
}
else if (event.type == SDL_QUIT) {
this->threadExit = 1;
}
else if (event.type == BREAK_EVENT) {
return -1;
}
else if (event.type == SDL_WINDOWEVENT) {
//If Resize
SDL_GetWindowSize(window, &windowW, &windowH);
}
return 0;
}
int SDLPlayer::playYUV(uint8_t* buffer,int delayTime)
{
SDL_WaitEvent(&event);
if (event.type == REFRESH_EVENT) {
SDL_Rect sdlRect;
SDL_UpdateTexture(sdlTexture, NULL, buffer, this->width);
sdlRect.x = 0;
sdlRect.y = 0;
sdlRect.w = windowW;
sdlRect.h = windowH;
SDL_RenderClear(sdlRanderer);
SDL_RenderCopy(sdlRanderer, sdlTexture, NULL, &sdlRect);
SDL_RenderPresent(sdlRanderer);
//Delay 40ms
this->delayTime = delayTime;
}
else if (event.type == SDL_QUIT) {
this->threadExit = 1;
}
else if (event.type == SDL_WINDOWEVENT) {
//If Resize
SDL_GetWindowSize(window, &windowW, &windowH);
}
else if (event.type == BREAK_EVENT) {
return -1;
}
return 0;
}
void SDLPlayer::pause()
{
if (this->threadPause == 1) {
this->threadPause = 0;
}
else {
this->threadPause = 1;
}
}
void SDLPlayer::start()
{
this->threadPause = 0;
}
void SDLPlayer::quit()
{
this->threadExit = 1;
}
int SDLPlayer::refreshThread(void* opaque)
{
SDLPlayer* sdl = (SDLPlayer*)opaque;
while (!sdl->threadExit)
{
if (!sdl->threadPause) {
SDL_Event _event;
_event.type = REFRESH_EVENT;
SDL_PushEvent(&_event);
SDL_Delay(sdl->delayTime);
}
}
SDL_Event event;
event.type = BREAK_EVENT;
SDL_PushEvent(&event);
return 0;
}
SDLPlayer::~SDLPlayer()
{
SDL_DestroyWindow(window);
SDL_Quit();
}
SDL+FFmpe You can complete a video player .
QT The embedded SDL
playThread = new PlayThread;
ui.setupUi(this);
// Core code
playThread->setWindow((void*)ui.labelPlay->winId());
putenv(winID);
connect(ui.pushButtonPlay, &QPushButton::clicked, this, &FFmpegPlayer::play);
connect(ui.pushButtonPause, &QPushButton::clicked, this, &FFmpegPlayer::pause);
connect(ui.pushButtonClose, &QPushButton::clicked, this, &FFmpegPlayer::playerExit);
connect(ui.pushButtonOpen, &QPushButton::clicked, this, &FFmpegPlayer::openFile);
QT Thread functions
PlayThread.h
#pragma once
#include <qthread.h>
#include"FFmpeg.h"
#include"SDLPlayer.h"
#include<QString>
class PlayThread :
public QThread
{
public:
PlayThread();
~PlayThread();
void setWindow(void*);
virtual void run();
void pause();
void stop();
void setFilePath(QString filePath);
int isStop = 0;
private:
QString filePath;
void* winID;
SDLPlayer sdlPlayer;
};
PlayThread.cpp
#include "PlayThread.h"
PlayThread::PlayThread():filePath("Titanic.ts"),winID(NULL)
{
}
PlayThread::~PlayThread()
{
}
void PlayThread::setWindow(void* winID)
{
this->winID = winID;
}
void PlayThread::run()
{
FFmpeg ffmpeg;
auto r = ffmpeg.initFFmpeg(this->filePath.toStdString().c_str());
if (r != INITERRO::INIT_ERRO_NO_ERRO) {
return;
}
//SDL player
sdlPlayer.setWidth(ffmpeg.getWidth());
sdlPlayer.setHeight(ffmpeg.getHeight());
sdlPlayer.setPlayerTitle("myplayer");
sdlPlayer.initPlayer(this->winID);
int isVideo = -2;
auto buffer = ffmpeg.getFrameYUV(isVideo);
while (isVideo != -2)
{
buffer = ffmpeg.getFrameYUV(isVideo);
if (isVideo == 1) {
auto r = sdlPlayer.playYUV(buffer.get());
if (r == -1) {
break;
}
}
}
}
void PlayThread::pause()
{
sdlPlayer.pause();
}
void PlayThread::stop()
{
sdlPlayer.quit();
}
void PlayThread::setFilePath(QString filePath)
{
this->filePath = filePath;
}

边栏推荐
- 自动分账系统哪家好?
- Shell 脚本 快速入门 -01
- 代码实现 —— 多项式的最大公因式(线性代数)
- 2022/07/28 learning notes (day18) common APIs
- Explanation of engineering economics terms
- 多重继承与派生类成员标识
- I was stunned by this question that I browsed 746000 times
- Ten methods to prevent blackmail software from attacking data
- Summary of knowledge points of Engineering Economics
- PHP幸运抽奖系统带后台源码
猜你喜欢

FPGA skimming memory (Verilog implementation of ram and FIFO)

laravel框架中实现封装公共方法全局调用

Production scheme and advantages of online 3D digital exhibition hall

云开发口袋工具箱微信小程序源码

When synchronized encounters this thing, there is a big hole, so be careful

I was stunned by this question that I browsed 746000 times

On Multithreading

以科技传递温度,vivo亮相数字中国建设峰会

Redis主从模式、哨兵集群、分片集群

h. 264 code stream explanation
随机推荐
别人的快乐
Read the recent trends of okaleido tiger and tap the value and potential behind it
CatchAdmin实战教程(四)Table组件相关功能实现
网络基础概论
Transform okhttp cache with retrofit
Where, having, group by, order by, is null, not in, subquery, delete, date function
What are the TCP retransmission mechanisms?
如何快速设计一套支持渲染富文本内容的跨端组件
Servlet三种实现方式
Driverless obstacle avoidance technology
全新UI四方聚合支付系统源码/新增USDT提现/最新更新安全升级修复XSS漏洞补单漏洞
How does the Devops team defend against API attacks?
一文读懂Okaleido Tiger近期动态,挖掘背后价值与潜力
On Multithreading
NVIDIA-VPI(Vision Programming Interface)
Production scheme and advantages of online 3D digital exhibition hall
Shell script quick start-01
无线振弦采集系统工作流程
Summary of knowledge points of Engineering Economics
ROCBOSS开源微社区轻论坛类源码