基于QT的YUV/RGB的播放器

        对最近学习音视频的一点成果汇总,前面对雷神的一些项目进行了复现只是将平台从MFC变为了QT,利用FFMPEGle+SDL可以直接播放MP4等文件。后面想直接做一个像素数据的播放器可以播放YUV+RGB。然后查找资料,做了一个像素数据播放器。

1.实现的播放器的功能

(1)从文件夹获取文件路径;

(2)读取yuv与rgb文件;

(3)将像素数据一帧一帧播放出来;

(4)设置视频的宽高以及播放速度

(5)视频暂停和退出;

2.涉及知识点

(1)QT的基础知识点(创建ui、控件、布局、控件设置)

(2)YUV数据和rgb数据的存储格式、读取以及yuv和rgb的转换

(3)QT信号与槽函数

(4)QT多线程

3.方案设计

        在设计播放器的时候,我们首先考虑用Qlabel的QPIXMAP来显示图像,但是QPIXMAP只能显示rgb数据不能直接显示YUV数据,所以需要把YUV转换为RGB,rgb数据也不是直接给Qlabel显示,要经过QImage传递。转换的方法有三种,第一种是用公式法直接转换,第二种是用ffmpeg里的sws_scale函数来转换,第三种方法是采用查表法,在这里我实验了第一种和第二种,第三种比较麻烦没有尝试。经查阅资料发现还可以用QPaint来渲染图像,这里没有采用这种方法。

4.存在问题

        第一:功能不够完整,没有实现进度条,

        第二:播放完一个文件再播放另一个文件时存在一些bug,有时会卡死,怀疑是多线程的问题,还没解决。

5.实现代码

(1)多线程类videoplayer.h

#ifndef VIDEOPLAYER_H
#define VIDEOPLAYER_H

#include <QThread>
#include <QImage>
#include <QDebug>
#include<QMutex>

class VideoPlayer: public QThread
{
    Q_OBJECT

public:
    explicit VideoPlayer();
    ~VideoPlayer();

    void setFileName(QString path) {
        mFileName = path;
        qDebug() << mFileName;
        
    }
    void setwh(int w,int h) {
        vwidth = w;
        vheight = h;
        qDebug() << vwidth << vheight;
    }
    void setrate(int rate) {
        vtime = 1000/rate;
    }
    void startPlay();
    void pausePlay();
    void stopPlay();
    void resume();
    void threadPR();//切换
 

signals:
    void sig_GetOneFrame(QImage); //没获取到一帧图像 就发送此信号

protected:
    void run();

private:
   QString mFileName;
   int vwidth;
   int vheight;
   int vtime;
   bool m_buttonState;//if pause m_buttonState=false,else =true
   QMutex m_mutex;//互斥量
   int m_i;
};

#endif // VIDEOPLAYER_H

(2)videoplayer.cpp

#include "qtread.h"
#include"QtWidgetsApplication3.h"

extern "C"
{
#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"
#include "libswscale/swscale.h"
#include "libavutil/imgutils.h"
}

#include <stdio.h>
VideoPlayer::VideoPlayer()
{
	m_buttonState=false;//if pause m_buttonState=false,else =true
	m_i=0;
}

VideoPlayer::~VideoPlayer()
{

}

void VideoPlayer::startPlay()
{
    ///调用 QThread 的start函数 将会自动执行下面的run函数 run函数是一个新的线程
    this->start();

}




void VideoPlayer::pausePlay()
{
	qDebug() << QString("pause :%1").arg(m_buttonState);
	this->m_mutex.lock();
	this->m_buttonState = false;
	qDebug() << QString("pause");
}
void VideoPlayer::resume()
{
	qDebug() << QString("resume :%1").arg(m_buttonState);
	this->m_mutex.unlock();
	this->m_buttonState = true;
	qDebug() << QString("resume");

}
void VideoPlayer::stopPlay() {
	//this->exit(0);
	qDebug() << QString("stop");
	//this->wait();
	this->terminate();
	this->m_mutex.unlock();
}
void VideoPlayer::threadPR() {
	if (m_buttonState)
	{
		pausePlay();
	}
	else
	{
		resume();
	}
}
void VideoPlayer::run() {
	m_buttonState = true;
	while (1) {
		AVFrame* pFrameYUV, * pFrameRGB;
		QString url = mFileName;
		qDebug() << url;
		QByteArray ba2;
		ba2.append(url);     //也可以 ba2 = s2.toLatin1();
		char* filepath = ba2.data();
		qDebug() << filepath;
		int len = strlen(filepath);
		int width = vwidth;
		int height = vheight;
		int time = vtime;
		qDebug() << "time" << time;
		qDebug() << "321" << &filepath[len - 3];
		pFrameRGB = av_frame_alloc();
		pFrameYUV = av_frame_alloc();
		unsigned char* rgb_buffer;
		struct SwsContext* img_convert_ctx;
		FILE* fp = fopen(filepath, "rb+");
		if (strcmp(&filepath[len - 3], "yuv") == 0) {
			rgb_buffer = (unsigned char*)av_malloc(av_image_get_buffer_size(AV_PIX_FMT_RGB32, width, height, 1));
			unsigned char* yuv_buffer = (unsigned char*)malloc(width * height * 3 / 2);
			av_image_fill_arrays(pFrameRGB->data, pFrameRGB->linesize, rgb_buffer, AV_PIX_FMT_RGB32, width, height, 1);//填充上下文
			img_convert_ctx = sws_getContext(width, height, AV_PIX_FMT_YUV420P, width, height, AV_PIX_FMT_RGB32, SWS_BICUBIC, NULL, NULL, NULL);
			qDebug() << "doit";
			int index = 0;
			while (1) {
				qDebug() << "dit";
				m_i++;
				m_mutex.lock();
				qDebug() << "dt";
				fread(yuv_buffer, 1, width * height * 3 / 2, fp);
				if (yuv_buffer <= 0)
					break;
				av_image_fill_arrays(pFrameYUV->data, pFrameYUV->linesize, yuv_buffer, AV_PIX_FMT_YUV420P, width, height, 1);//填充上下文
				qDebug() << "it";

				sws_scale(img_convert_ctx, (const unsigned char* const*)pFrameYUV->data, pFrameYUV->linesize, 0, height, pFrameRGB->data, pFrameRGB->linesize);
				QImage tmpImg((uchar*)rgb_buffer, width, height, QImage::Format_RGB32);
				QImage image = tmpImg.copy(); //把图像复制一份 传递给界面显示
				emit sig_GetOneFrame(image);
				QThread::msleep(time);
				m_mutex.unlock();
			}
		}
		else if (strcmp(&filepath[len - 3], "rgb") == 0) {
			unsigned char* rgb_buffer = (unsigned char*)malloc(width * height * 3);

			while (1) {
				m_i++;
				m_mutex.lock();
				fread(rgb_buffer, 1, width * height * 3, fp);

				if (rgb_buffer <= 0)
					break;
				QImage tmpImg((uchar*)rgb_buffer, width, height, QImage::Format_RGB888);
				QImage image = tmpImg.copy(); //把图像复制一份 传递给界面显示
				emit sig_GetOneFrame(image);
				QThread::msleep(time);//休眠10ms
				m_mutex.unlock();
			}
		}

		else
		{
			qDebug() << QString::fromUtf8("输入文件不符合规定");
		}

		fclose(fp);
	}
};

/*yuv转RGB的第二种方法
void VideoPlayer::run() {
    m_buttonState = true;
	QString url = mFileName;
	qDebug() << url;
	QByteArray ba2;
	ba2.append(url);     //也可以 ba2 = s2.toLatin1();
	char *filepath = ba2.data();
	qDebug() << filepath;
	int len=strlen(filepath);
	int width = vwidth;
	int height = vheight;
	int time = vtime;
	qDebug() << "time" << time;
	qDebug() << "321" << &filepath[len - 3];

	FILE* fp = fopen(filepath,"rb+");
	if (strcmp(&filepath[len - 3],"yuv") == 0) {
		qDebug() << "doit";
		unsigned char* outbuffer = (unsigned char*)malloc(width * height * 3 / 2);
		unsigned char* rgb_buffer = (unsigned char*)malloc(width * height * 3);
		int index = 0;
		while (1) {
            qDebug() << "dit";
			m_i++;
			m_mutex.lock();
			fread(outbuffer, 1, width * height * 3 / 2, fp);
			if (outbuffer <= 0)
				break;
			for (int y = 0; y < height; y++) {
				for (int x = 0; x < width; x++) {
					int indexY = y * width + x;
					int indexU = width * height + y / 2 * width / 2 + x / 2;
					int indexV = width * height + width * height / 4 + y / 2 * width / 2 + x / 2;
					unsigned char Y = outbuffer[indexY];
					unsigned char U = outbuffer[indexU];
					unsigned char V = outbuffer[indexV];

					rgb_buffer[index++] = Y + 1.402 * (V - 128); //R
					rgb_buffer[index++] = Y - 0.34413 * (U - 128) - 0.71414 * (V - 128); //G
					rgb_buffer[index++] = Y + 1.772 * (U - 128); //B
				}
			}
			index = 0;
			QImage tmpImg((uchar*)rgb_buffer, width, height, QImage::Format_RGB888);
			QImage image = tmpImg.copy(); //把图像复制一份 传递给界面显示
			emit sig_GetOneFrame(image);
			QThread::msleep(time);
            m_mutex.unlock();
		}
	}
	else if (strcmp(&filepath[len - 3], "rgb") == 0) {
		unsigned char* rgb_buffer = (unsigned char*)malloc(width * height * 3);
		while (1) {
            m_i++;
			m_mutex.lock();
			fread(rgb_buffer, 1, width * height * 3, fp);
			if (rgb_buffer <= 0)
				break;
			QImage tmpImg((uchar*)rgb_buffer, width, height, QImage::Format_RGB888);
			QImage image = tmpImg.copy(); //把图像复制一份 传递给界面显示
			emit sig_GetOneFrame(image);
			QThread::msleep(time);//休眠10ms
            m_mutex.unlock();
		}
	}

	else
	{
		qDebug ()<< QString::fromUtf8( "输入文件不符合规定");
	}
	fclose(fp);
};*/
*/

(3)创建QT类QtWidgetsApplication3.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);
public slots:
    void size();

private:
 
    VideoPlayer* mPlayer;
    int w;
    int h;
    int rate;
    QImage mImage;
 private slots:
     void slotGetOneFrame(QImage img);
     QString OnBtOpen1();
     void play();
     void stop();
     void changeButton();
     void threadPR();
};

4.QtWidgetsApplication3.cpp

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

QtWidgetsApplication3::QtWidgetsApplication3(QWidget *parent)
    : QMainWindow(parent)
{
	
	ui.setupUi(this);
	QHBoxLayout* Layout_frame = new QHBoxLayout;//水平布局
	Layout_frame->addWidget(ui.label_2);
	Layout_frame->addWidget(ui.rateEdit);
	Layout_frame->addWidget(ui.label_5);

	QHBoxLayout* Layout_rate = new QHBoxLayout;
	Layout_rate->addWidget(ui.label_7);
	Layout_rate ->addWidget(ui.weightEdit);
	Layout_rate->addWidget(ui.label_10);
	Layout_rate->addWidget(ui.highEdit);
	Layout_rate->addWidget(ui.pushButton);

	QHBoxLayout* Layout_button = new QHBoxLayout;
	Layout_button->addWidget(ui.stopButton);
	Layout_button->addWidget(ui.playButton);
	Layout_button->addWidget(ui.pauseButton);

	QHBoxLayout* Layout_file = new QHBoxLayout;
	Layout_file->addWidget(ui.label);
	Layout_file->addWidget(ui.urlEdit);
	Layout_file->addWidget(ui.geteurlButton);


	QVBoxLayout* Layout_1 = new QVBoxLayout;
	Layout_1->addLayout(Layout_frame);
	Layout_1->addLayout(Layout_rate);


	QHBoxLayout* Layout_2 = new QHBoxLayout;
	Layout_2->addLayout(Layout_1);
	Layout_2->addLayout(Layout_button);

	QVBoxLayout* Layout_3 = new QVBoxLayout;
	Layout_3->addLayout(Layout_2);
	Layout_3->addLayout(Layout_file);


	QVBoxLayout* Layout_main = new QVBoxLayout;
	Layout_main->addWidget(ui.displaylabel);
	Layout_main->addLayout(Layout_3);

	ui.centralWidget->setLayout(Layout_main);
	ui.displaylabel->setAlignment(Qt::AlignCenter);



	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函数
	ui.displaylabel->setPixmap(QPixmap::fromImage(img));
}

//获取文件路径槽函数
//
QString QtWidgetsApplication3::OnBtOpen1() {
	QString  filepath1 = QFileDialog::getOpenFileName(this, tr("Select a File"), ".", tr("Images (*.yuv *.rgb)"));
	if (filepath1.isEmpty() ==false)//为空时表示取消操作
	{
		ui.urlEdit->setText(filepath1);
	}
	return filepath1;
}

void QtWidgetsApplication3::size() {
	w = (ui.weightEdit->displayText()).toInt();
	h = (ui.highEdit->displayText()).toInt();
	rate = (ui.rateEdit->displayText()).toInt();
	mPlayer->setrate(rate);
	//ui.centralWidget->resize(w, h + 200);
	ui.displaylabel->setGeometry(0, 0, w, h);
	this->resize(w,h+200);
}

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


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();
}

(5)main.cpp

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

using namespace std;


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

    QTextCodec::setCodecForLocale(codec);

    //设置窗口的标题
    widget.setWindowTitle("视频播放器");                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  
    
    widget.show();
    widget.resize(500,500);
    return a.exec();
}

  • 0
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值