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
    评论
### 回答1: 在Qt中显示YUV数据可以通过使用QImage类来实现。 首先,需要创建一个QImage对象用于存储YUV数据。YUV数据是一种颜色编码格式,分为Y分量(亮度)和UV分量(色度),通常以3个连续的平面存储。因此,需要将YUV数据转换为RGB格式,才能在Qt中进行显示。可以使用FFmpeg等库来进行YUV到RGB的转换操作。 接下来,可以使用QImage的setPixel函数来设置每个像素的值。循环遍历YUV数据的每个像素,依次计算对应的RGB值,并使用setPixel函数设置对应的像素值。 最后,将QImage对象显示到窗口上。可以使用QLabel或者QPainter来绘制图像。对于使用QLabel,可以使用其setPixmap函数将QImage对象设置为其显示的图像。对于使用QPainter,可以使用其drawImage函数来在绘图区域绘制QImage对象。 此外,还可以在显示过程中对图像进行缩放、裁剪、旋转等操作。可以使用QImage的scaled、copy和transformed函数来实现相应功能。 综上所述,通过以上方法可以在Qt中显示YUV数据。 ### 回答2: Qt是一个功能强大的跨平台应用程序开发框架,可以用于开发图形界面应用程序。在Qt中显示YUV数据需要进行以下步骤: 1. 首先,需要创建一个Qt的窗口组件,用于显示YUV数据。可以使用QWidget或QLabel等组件。 2. 接下来,需要将YUV数据格式转换为Qt支持的RGB格式。可以使用OpenCV库或Qt自带的相关函数进行转换。 3. 创建一个QImage对象,并使用转换后的RGB数据填充该对象。可以使用QImage的setPixel函数或者使用QImage构造函数直接传入数据。 4. 将QImage对象绘制到窗口组件上。可以使用QWidget的paintEvent函数中的QPainter对象将QImage绘制出来。 以下是一个简单的示例代码: ``` // 创建一个显示YUV数据的窗口组件 QWidget *widget = new QWidget(); // 将YUV数据转换为RGB格式 // 这里假设已经有了转换后的RGB数据,存储在rgbData中 // 创建QImage对象,并使用转换后的RGB数据填充 QImage image(rgbData, width, height, QImage::Format_RGB888); // 在窗口组件上绘制QImage对象 void Widget::paintEvent(QPaintEvent *) { QPainter painter(this); painter.drawImage(0, 0, image); } ``` 通过上述步骤,就可以在Qt中显示YUV数据了。注意,以上代码是一个简单示例,实际使用中可能需要根据具体需求进行相应的修改和优化。 ### 回答3: Qt是一个跨平台的图形用户界面应用程序开发框架,可以用于开发各种类型的应用程序。如果要在Qt中显示YUV数据,可以采取以下步骤: 1. 首先,需要将原始的YUV数据转换为Qt可以理解的图像格式,例如RGB格式。可以使用各种转换算法来实现这一步骤,比如将Y、U和V三个分量合成为RGB图像。 2. 一旦将YUV数据转换为RGB格式,可以使用Qt中提供的图像处理类来创建一个QImage对象。QImage是Qt中表示图像的类,可以用于存储和处理图像数据。 3. 接着,可以将QImage对象显示在Qt的窗口中。可以使用Qt中的QWidget、QLabel或者QGraphicsView等控件,将QImage对象作为控件的背景图像显示出来。可以通过设置控件的属性或使用API来实现图像显示。 4. 如果要实现实时显示YUV数据,可以将以上步骤封装在一个循环中,并将YUV数据不断传入进行处理和显示。可以使用Qt的定时器或者多线程来实现数据的实时获取和处理。 需要注意的是,YUV数据的格式可能存在不同的变种,如YUV420、YUV422、YUV444等。在实际显示时,需要根据具体的数据格式来进行相应的转换和处理。此外,由于YUV数据是一种压缩格式,其显示效果可能与RGB格式有所不同,所以在显示时可能需要进行一些补偿或调整以获得更好的图像质量。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值