QT实现像素数据播放(二)

        前面一篇记录了最后实现的像素数据播放器方案,在实现最后一版前还有两个版本,在这里记录一下。

方案一 

利用Qpainter实现画图,但是经查阅资料,QPainte实现rYUV / RGB 转换费CPU(QLabel也存在这个问题);QPainter大面积绘制效率不高。

功能:播放、暂停、停止

界面设计:一共三个按键 播放、暂停、停止

多线程部分与前一篇博客一致,这里只记录qt播放器的代码

qtwidegt.h

#pragma once
#include <QtWidgets/QMainWindow>
#include "ui_QtWidgetsApplication3.h"
#include "qtread.h"
//#include <QPainter.h>
//#include<qpixmap.h>

class QtWidgetsApplication3 : public QMainWindow
{
    Q_OBJECT

public:
    QtWidgetsApplication3(QWidget *parent = Q_NULLPTR);
    Ui::QtWidgetsApplication3Class ui;
    void paintEvent(QPaintEvent* event);
protected:
    void resizeEvent(QResizeEvent*);
signals:
    void sig_GetOneFrame(QImage);//

private:
 
    VideoPlayer* mPlayer;
    int w=848;//设置像素数据的宽高
    int h=480;
    int rate=25;//设置播放速度
    QImage mImage;
 private slots:
     void slotGetOneFrame(QImage img);
     void play();
     void stop();
     void threadPR();
};

cpp文件

#include "QtWidgetsApplication3.h"
#include<QPainter>
#include<QFileDialog>
#include<QResizeEvent>
#include<QHBoxLayout>
#include <QDebug>

QtWidgetsApplication3::QtWidgetsApplication3(QWidget *parent)
    : QMainWindow(parent)
{
	
	ui.setupUi(this);

	mPlayer = new VideoPlayer;
	connect(mPlayer, SIGNAL(sig_GetOneFrame(QImage)), this, SLOT(slotGetOneFrame(QImage)));
}


void QtWidgetsApplication3::resizeEvent(QResizeEvent *event) {
	event->oldSize();
	qDebug() << event->size();
}

void QtWidgetsApplication3::slotGetOneFrame(QImage img)
{
	mImage = img;
	update(); //调用update将执行 paintEvent函数
}


void QtWidgetsApplication3::play() {
	//QString url = ui.urlEdit->displayText();
	mPlayer->setFileName("E:/Thinking-in-AV-master/video/sin848_480.yuv");//文件路径
	mPlayer->setwh(w, h);
	mPlayer->startPlay();
	return;
}
void QtWidgetsApplication3::stop() {
	qDebug()<<"停止";
	mPlayer->stopPlay();
}


void QtWidgetsApplication3::changeButton() {
	if (!QString::compare(ui.pauseButton->text(), "PAUSE"))//QString::fromUtf8
	{
		ui.pauseButton->setText("CONTINUE");
	}
	else
	{
		ui.pauseButton->setText("PAUSE");
	}
}

void QtWidgetsApplication3::threadPR() {
	mPlayer->threadPR();
	//nPlayer->threadPR();
}



void QtWidgetsApplication3::paintEvent(QPaintEvent* event) {
	QPainter painter(this);
	painter.setBrush(Qt::black);
	painter.drawRect(0, 0, this->width(), this->height()); //先画成黑色

	if (mImage.size().width() <= 0) return;

	///将图像按比例缩放成和窗口一样大小
	QImage img = mImage.scaled(this->size(), Qt::KeepAspectRatio);

	int x = this->width() - img.width();
	int y = this->height() - img.height();

	x /= 2;
	y /= 2;

	painter.drawImage(QPoint(x, y), img); //画出图像

}

main函数与前面也没有变化,结果如下所示

方案二 SDL+ffmpeg实现MP4播放

        这个方案是最开始的版本,直接利用ffmpeg和SDL实现渲染,最后将SDL写入到Qlabel里,在ui界面创建三个按钮(播放、暂停、停止)以及一个qlabel文本框

头文件

#include <QtWidgets/QMainWindow>
#include "ui_QtWidgetsApplication3.h"


class QtWidgetsApplication3 : public QMainWindow
{
    Q_OBJECT

public:
    QtWidgetsApplication3(QWidget *parent = Q_NULLPTR);
    Ui::QtWidgetsApplication3Class ui;
 
protected:
    void resizeEvent(QResizeEvent*);
 private slots:
     int stop();
     int play();
     int pause();
   
};

cpp文件

#include "QtWidgetsApplication3.h"
#include<QResizeEvent>
#include <QDebug>

#define __STDC_CONSTANT_MACROS

extern "C"
{
#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"
#include "libswscale/swscale.h"
#include "libavutil/imgutils.h"
#include "SDL2/SDL.h"
}
#define SFM_REFRESH_EVENT  (SDL_USEREVENT + 1)//定义事件
#define SFM_BREAK_EVENT  (SDL_USEREVENT + 2)

int thread_exit;//SDL线程的退出标志,设置为全局变量
int thread_pause;//SDL线程的暂停标志,设置为全局变量

QtWidgetsApplication3::QtWidgetsApplication3(QWidget *parent)
    : QMainWindow(parent)
{
	thread_exit = 0;
	thread_pause = 0;
	ui.setupUi(this);
    connect(ui.playButton, SIGNAL(clicked()), this, SLOT(play()));
	connect(ui.pauseButton, SIGNAL(clicked()), this, SLOT(pause()));
    connect(ui.stopButton, SIGNAL(clicked()), this, SLOT(stop()));

}
//SDL线程控制
int sfp_refresh_thread(void* opaque) {
	thread_exit = 0;
	thread_pause = 0;

	while (!thread_exit) {
		if (!thread_pause) {
			SDL_Event event;
			event.type = SFM_REFRESH_EVENT;
			SDL_PushEvent(&event);
		}
		SDL_Delay(40);
	}
	thread_exit = 0;
	thread_pause = 0;
	//Break
	SDL_Event event;
	event.type = SFM_BREAK_EVENT;
	SDL_PushEvent(&event);
	return 0;
}


//停止播放槽函数
int QtWidgetsApplication3::stop() {
	thread_exit = 1;
	return 0;
}

//暂停播放槽函数
int QtWidgetsApplication3::pause() {
	if (ui.pauseButton->text() == "PAUSE") {
		thread_pause = 1;
		ui.pauseButton->setText("CONTINUE");
	}
	else{
		thread_pause = 0;
		ui.pauseButton->setText("PAUSE");
	}
	
	return 0;
}

//播放槽函数
int QtWidgetsApplication3::play() {
	AVFormatContext* pFormatCtx;
	int				i, videoindex;
	AVCodecContext* pCodecCtx;
	AVCodec* pCodec;
	AVFrame* pFrame, * pFrameYUV;
	unsigned char* out_buffer;
	AVPacket* packet;
	int ret, got_picture;

	//------------SDL----------------
	int screen_w, screen_h;
	SDL_Window* screen;
	SDL_Renderer* sdlRenderer;
	SDL_Texture* sdlTexture;
	SDL_Rect sdlRect;
	SDL_Thread* video_tid;
	SDL_Event event;

	struct SwsContext* img_convert_ctx;
	const char* filepath = "E://Thinking-in-AV-master//video//nxn.mp4";
	pFormatCtx = avformat_alloc_context();

	if (avformat_open_input(&pFormatCtx, filepath, NULL, NULL) != 0) {
		qDebug() << "Couldn't open input stream.\n";
		return -1;
	}
	if (avformat_find_stream_info(pFormatCtx, NULL) < 0) {
		qDebug()<<"Couldn't find stream information.\n";
		return -1;
	}
	videoindex = -1;
	for (i = 0; i < pFormatCtx->nb_streams; i++)
		if (pFormatCtx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
			videoindex = i;
			break;
		}
	if (videoindex == -1) {
		printf("Didn't find a video stream.\n");
		return -1;
	}

	AVCodecParameters* pPmt;
	pPmt = pFormatCtx->streams[videoindex]->codecpar;
	pCodec = avcodec_find_decoder(pPmt->codec_id);//获得codec
	pCodecCtx = avcodec_alloc_context3(pCodec);///构造AVCodecContext ,并将vcodec填入AVCodecContext中
	avcodec_parameters_to_context(pCodecCtx, pFormatCtx->streams[videoindex]>codecpar);//初始化AVCodecContext
	if (pCodec == NULL) {
		printf("Codec not found.\n");
		return -1;
	}
	if (avcodec_open2(pCodecCtx, pCodec, NULL) < 0) {
		printf("Could not open codec.\n");
		return -1;
	}
	pFrame = av_frame_alloc();
	pFrameYUV = av_frame_alloc();

	out_buffer = (unsigned char*)av_malloc(av_image_get_buffer_size(AV_PIX_FMT_YUV420P, pCodecCtx->width, pCodecCtx->height, 1));
	av_image_fill_arrays(pFrameYUV->data, pFrameYUV->linesize, out_buffer,
		AV_PIX_FMT_YUV420P, pCodecCtx->width, pCodecCtx->height, 1);

	//Output Info-----------------------------
	//qDebug()>>"---------------- File Information ---------------\n";
	//v_dump_format(pFormatCtx, 0, filepath, 0);
	//qDebug()>>"-------------------------------------------------\n";

	img_convert_ctx = sws_getContext(pCodecCtx->width, pCodecCtx->height, pCodecCtx->pix_fmt,
		pCodecCtx->width, pCodecCtx->height, AV_PIX_FMT_YUV420P, SWS_BICUBIC, NULL, NULL, NULL);


	if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER)) {
		printf("Could not initialize SDL - %s\n", SDL_GetError());
		return -1;
	}
	//SDL 2.0 Support for multiple windows
	screen_w = pCodecCtx->width;
	screen_h = pCodecCtx->height;

//重设播放器位置和大小
	ui.displaylabel->resize(pCodecCtx->width, pCodecCtx->height);
	this->resize(pCodecCtx->width, pCodecCtx->height + 50);
	ui.playButton->setGeometry((pCodecCtx->width/2)-25,pCodecCtx->height+15,50,20);
	ui.stopButton->setGeometry((pCodecCtx->width / 2)-75, pCodecCtx->height+15, 50, 20);
	ui.pauseButton->setGeometry((pCodecCtx->width / 2)+25, pCodecCtx->height+15, 50, 20);

	screen = SDL_CreateWindowFrom((void*)ui.displaylabel->winId());//将画面嵌入到label种
	//screen = SDL_CreateWindow("Simplest ffmpeg player's Window"SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,screen_w, screen_h, SDL_WINDOW_OPENGL);//这种的话SDL不会嵌入到播放器,会重新弹出一个窗口

	if (!screen) {
		//qDebug()>>"SDL: could not create window - exiting:%s\n">>SDL_GetError();
		return -1;
	}
	sdlRenderer = SDL_CreateRenderer(screen, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC);
	//IYUV: Y + U + V  (3 planes)
	//YV12: Y + V + U  (3 planes)
	sdlTexture = SDL_CreateTexture(sdlRenderer, SDL_PIXELFORMAT_IYUV, SDL_TEXTUREACCESS_STREAMING, pCodecCtx->width, pCodecCtx->height);


	sdlRect.x = 0;
	sdlRect.y = 0;
	sdlRect.w = 2 * screen_w;
	sdlRect.h = 2 * screen_h;

	packet = (AVPacket*)av_malloc(sizeof(AVPacket));

	//创建多线程
	video_tid = SDL_CreateThread(sfp_refresh_thread, NULL, NULL);//函数指针,把一个函数名作为参数
	//------------SDL End------------
	//Event Loop
	SDL_Delay(100);
	for (;;) {//进入循环
		//Wait
		SDL_WaitEvent(&event);//执行到这里不动,等待消息,接受事件,执行子线程
		if (event.type == SFM_REFRESH_EVENT) {//自定义事件,不断刷新屏幕
			while (1) {
				if (av_read_frame(pFormatCtx, packet) < 0)
					thread_exit = 1;

				if (packet->stream_index == videoindex)
					break;
			}
			ret = avcodec_send_packet(pCodecCtx, packet);
			got_picture = avcodec_receive_frame(pCodecCtx, pFrame);
			if (ret < 0) {
				printf("Decode Error.\n");
				return -1;
			}
			if (!got_picture) {
				sws_scale(img_convert_ctx, (const unsigned char* const*)pFrame->data, pFrame->linesize, 0, pCodecCtx->height, pFrameYUV->data, pFrameYUV->linesize);
				//SDL---------------------------
				SDL_UpdateTexture(sdlTexture, NULL, pFrameYUV->data[0], pFrameYUV->linesize[0]);
				SDL_RenderClear(sdlRenderer);
				//SDL_RenderCopy( sdlRenderer, sdlTexture, &sdlRect, &sdlRect );  
				SDL_RenderCopy(sdlRenderer, sdlTexture, NULL, NULL);
				SDL_RenderPresent(sdlRenderer);
				//SDL End-----------------------
			}
			av_packet_unref(packet);
		}
		else if (event.type == SDL_KEYDOWN) {
			//Pause
			if (event.key.keysym.sym == SDLK_SPACE)
				thread_pause = !thread_pause;
		}
		else if (event.type == SDL_QUIT) {//触发窗口×的功能(退出)
			thread_exit = 1;
		}
		else if (event.type == SDL_WINDOWEVENT) {
			SDL_GetWindowSize(screen, &screen_w, &screen_h);//实现自适应变换窗口大小,系统自带事件
		}
		else if (event.type == SFM_BREAK_EVENT) {
			break;
		}

	}
	sws_freeContext(img_convert_ctx);

	SDL_Quit();
	//--------------
	av_frame_free(&pFrameYUV);
	av_frame_free(&pFrame);
	avcodec_close(pCodecCtx);
	avformat_close_input(&pFormatCtx);
	//ui.label_4->setText("播放完毕");
	//ui.label_4->setVisible(true);
	return 0;
}

 主函数

#include "QtWidgetsApplication3.h"
#include <QtWidgets/QApplication>
#include<QTextCodec>
#pragma execution_character_set("utf-8")

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    QtWidgetsApplication3 widget;
    QTextCodec* codec = QTextCodec::codecForName("GBK");//解决中文问题
    QTextCodec::setCodecForLocale(codec);

    //设置窗口的标题
    widget.setWindowTitle("视频播放器");                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  
    
    widget.show();
    return a.exec();//回调函数不断监测窗口事件
}

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值