qt使用ffmpeg显示rtsp视频流

时间:2022-12-13 23:21:31

最简单的方式是直接使用qlable实现


#ifndef QWIDEGETPLAY_H
#define QWIDEGETPLAY_H

#include <QWidget>
#include <QThread>
#include <QImage>
#include <QPainter>
#include <QDebug>
#include <QLabel>
extern "C" {
#pragma comment(lib,"avcodec.lib")
#pragma comment(lib,"avformat.lib")
#pragma comment(lib,"avutil.lib")
#pragma comment(lib,"swscale.lib")


#include "libavutil/avutil.h"
#include "libavformat/avformat.h"
#include "libavcodec/avcodec.h"
#include "libswscale/swscale.h"
#include "libavutil/imgutils.h"
#include "libavutil/opt.h"
}

QStringList ffGetStreamInfo(QString url);


class QWidegetRender : public QThread
{
	Q_OBJECT

public:
	QWidegetRender(QLabel* toRender,QObject *parent=0);
	~QWidegetRender();

	void startRender(QString playUrl);
	void stopRender();

public slots:
	void onImage();
	void onFScreen();
 signals:
	void imageReady();
	
protected:
	bool eventFilter(QObject *obj, QEvent *event);
protected:
	virtual void run();
	int create_swsContext(int iFFPixFmt);
	int decode_packet(int *got_frame, int cached);
	int open_codec_context(int *stream_idx,	AVFormatContext *fmt_ctx, enum AVMediaType type);
	static int ffInterrupt_callback(void* param);

private:
	QLabel* m_Widget;
	QPainter* painter;
	bool m_bFullSrceen;
	bool m_bStop;

	AVFormatContext *fmt_ctx ;
	AVCodecContext *video_dec_ctx ;
	AVStream *video_stream ;
	QString src_filename ;
	SwsContext* sws_cxt;
	uint8_t *video_dst_data[4] ;
	int video_dst_linesize[4];
	int video_dst_bufsize;
	int video_stream_idx ;
	AVFrame *frame ;
	AVPacket pkt;
};

#endif // QWIDEGETPLAY_H

#include "qwidegetplay.h"
#include <QEvent>
#include "qproxyserver.h"
#include <QMessageBox>
QWidegetRender::QWidegetRender(QLabel* toRender,QObject *parent)
	: QThread(parent)
{
	m_bStop = true;
	m_bFullSrceen = false;
	painter = new QPainter(toRender);
	m_Widget = toRender;
	 connect(this,SIGNAL(imageReady()),this,SLOT(onImage()),Qt::BlockingQueuedConnection);
	//connect(m_Widget,SIGNAL(triggered()),this,SLOT(onFScreen()));
	//m_Widget->installEventFilter(this);
	

	
}

QWidegetRender::~QWidegetRender()
{
	if (!m_bStop)
	{
		stopRender();
	}
	if (painter)
	{
		delete painter;
	}
}

void QWidegetRender::startRender(QString playUrl)
{
	CRtspCheck rtspCheck;
	if (playUrl.isEmpty() || !rtspCheck.openUrl(playUrl))
	{
		QMessageBox::information(NULL,"warnning",QStringLiteral("该链接无法播放"));
		return ;
	}
	src_filename = playUrl;	
	if (!m_bStop)
	{
		stopRender();
	}
	m_bStop = false;
	
	start();
}

void QWidegetRender::stopRender()
{
	
	disconnect(this,SIGNAL(imageReady()),this,SLOT(onImage())); // 防止wait阻塞主线程时,解码线程imageReady信号得不到处理而阻塞	
	 m_bStop = true;
	if(isRunning() && !wait(10000))
	 {
		 qDebug()<<"wait render thread failed";
	 }
     qDebug()<<"wait render thread ok";
	 
	 connect(this,SIGNAL(imageReady()),this,SLOT(onImage()),Qt::BlockingQueuedConnection);
	 QPixmap pixBlack(1,1);
	 pixBlack.fill(Qt::black);
	 m_Widget->setPixmap(pixBlack.scaled(m_Widget->width(),m_Widget->height()));
	
}
bool QWidegetRender::eventFilter(QObject *obj, QEvent *event)
{
	if (obj == m_Widget && event->type()==QEvent::MouseButtonDblClick)
	{
		onFScreen();
		return true;
	}
	else
	{
		return QObject::eventFilter(obj, event);
	}
}
void QWidegetRender::onFScreen()
{
	if (m_bStop)
	{
		return;
	}
	static int flags ;
	if (!m_bFullSrceen)
	{
		flags = m_Widget->windowFlags();
		m_Widget->setWindowFlags(Qt::Dialog);
		m_Widget->showFullScreen();
	}
	else
	{
		m_Widget->setWindowFlags((Qt::WindowFlags)flags);
		m_Widget->showNormal();
		m_Widget->setGeometry(9,9,431,221);

	}
	m_bFullSrceen = !m_bFullSrceen;

}
struct st_dev_streamInfo
{
	QString width;
	QString heigt;
};
typedef st_dev_streamInfo st_dev_streamInfo;



void QWidegetRender::run()
{
	
	qDebug()<<"render thread starting";
	fmt_ctx = avformat_alloc_context();
	video_dec_ctx=NULL ;
	video_stream=NULL ;
	sws_cxt=NULL;
	for (int i =0;i<4;i++)
	{
		video_dst_data[i]=NULL;
		video_dst_linesize[i] = NULL;
	}

	video_dst_bufsize = 0;
	video_stream_idx = 0;
	frame = NULL;
	AVInputFormat* fmtInput=NULL ;
	char deviceBuffer[256] ={0};
	int ret = 0, got_frame;
	AVDictionary* options = NULL;

	//src_filename = "rtsp://192.168.0.134:8554/Video640x480@60fps";
	//src_filename = "rtsp://admin:12345@192.168.0.6:1025/av_stream/ch1/main"; 
	QByteArray arr = src_filename.toLatin1();
	const char* playUrl = arr.data();
	//QStringList wh = ffGetStreamInfo(src_filename);
	/* register all formats and codecs */

	//av_register_all();	
	//avformat_network_init();

	fmt_ctx->interrupt_callback.callback = QWidegetRender::ffInterrupt_callback;
	fmt_ctx->interrupt_callback.opaque = this;
	av_dict_set(&options, "rtsp_transport", "tcp", 0);
	if (avformat_open_input(&fmt_ctx, playUrl, NULL, &options) < 0) {
		fprintf(stderr, "Could not open source file %s\n", src_filename);
		goto end;
	}
	//void ** context ;
	//const AVOption* retOpt = av_opt_find2(&fmt_ctx,"udp","",0,AV_OPT_SEARCH_FAKE_OBJ,0);
	

	if (open_codec_context(&video_stream_idx, fmt_ctx, AVMEDIA_TYPE_VIDEO) >= 0) {
		video_stream = fmt_ctx->streams[video_stream_idx];
		video_dec_ctx = video_stream->codec;

	}

	/* dump input information to stderr */
	av_dump_format(fmt_ctx, 0, playUrl, 0);

	if (!video_stream) {
		fprintf(stderr, "Could not find  video stream in the input, aborting\n");
		ret = 1;
		goto end;
	}

	frame = avcodec_alloc_frame();
	if (!frame) {
		fprintf(stderr, "Could not allocate frame\n");
		ret = AVERROR(ENOMEM);
		goto end;
	}

	/* initialize packet, set data to NULL, let the demuxer fill it */
	av_init_packet(&pkt);
	pkt.data = NULL;
	pkt.size = 0;

	int skipOneFrame = 0;
	/* read frames from the file */
	while (av_read_frame(fmt_ctx, &pkt) >= 0) {

		decode_packet(&got_frame, 0);

		av_free_packet(&pkt);

		if (m_bStop)
		{
			qDebug()<<"render play stop, break loop...";
			break;
		}
	}

	qDebug()<<"out of loop...";
	/* flush cached frames */
	pkt.data = NULL;
	pkt.size = 0;
	do {
		qDebug()<<"flush cached frames";
		decode_packet(&got_frame, 1);
	} while (got_frame);

end:
	qDebug()<<"play end, free resource...";
	if (video_dec_ctx)
		avcodec_close(video_dec_ctx);
	if(fmt_ctx)
	avformat_close_input(&fmt_ctx);

	if(frame)
	av_free(frame);

	if(video_dst_data[0])
	av_free(video_dst_data[0]);

	if(sws_cxt){
	sws_freeContext(sws_cxt);
	sws_cxt = NULL;
	}

	qDebug()<<"render thread exit";
	return ;
}

int QWidegetRender::create_swsContext(int iFFPixFmt)
{
	int ret = 0;
	/* allocate image where the decoded image will be put */
	ret = av_image_alloc(video_dst_data, video_dst_linesize,
		video_dec_ctx->width,video_dec_ctx->height,
		(AVPixelFormat)iFFPixFmt, 4);
	if (ret < 0) {
		fprintf(stderr, "Could not allocate raw video buffer\n");
		return -1;
	}
	video_dst_bufsize = ret;

	sws_cxt = sws_getContext(video_dec_ctx->width,video_dec_ctx->height,video_dec_ctx->pix_fmt,
		video_dec_ctx->width,video_dec_ctx->height,(AVPixelFormat)iFFPixFmt,SWS_BILINEAR,NULL,NULL,NULL);

	return 0;
}

int QWidegetRender::decode_packet(int *got_frame, int cached)
{
	int ret = 0;

	if (pkt.stream_index == video_stream_idx) {
		/* decode video frame */
		ret = avcodec_decode_video2(video_dec_ctx, frame, got_frame, &pkt);
		if (ret < 0) {
			fprintf(stderr, "Error decoding video frame\n");
			return ret;
		}

		if (*got_frame) {
			if(!sws_cxt){
				create_swsContext(AV_PIX_FMT_RGB32);
			}
			sws_scale(sws_cxt, frame->data,frame->linesize,0,video_dec_ctx->height,
				video_dst_data,video_dst_linesize);
			 emit imageReady();
		}
	} 

	return ret;
}

int QWidegetRender::open_codec_context(int *stream_idx,
	AVFormatContext *fmt_ctx, enum AVMediaType type)
{
	int ret;
	AVStream *st;
	AVCodecContext *dec_ctx = NULL;
	AVCodec *dec = NULL;

	ret = av_find_best_stream(fmt_ctx, type, -1, -1, NULL, 0);
	if (ret < 0) {
		fprintf(stderr, "Could not find %s stream in input file '%s'\n",
			av_get_media_type_string(type), src_filename);
		return ret;
	} else {
		*stream_idx = ret;
		st = fmt_ctx->streams[*stream_idx];

		/* find decoder for the stream */
		dec_ctx = st->codec;
		dec = avcodec_find_decoder(dec_ctx->codec_id);
		if (!dec) {
			fprintf(stderr, "Failed to find %s codec\n",
				av_get_media_type_string(type));
			return ret;
		}

		if ((ret = avcodec_open2(dec_ctx, dec, NULL)) < 0) {
			fprintf(stderr, "Failed to open %s codec\n",
				av_get_media_type_string(type));
			return ret;
		}
	}

	return 0;
}


void QWidegetRender::onImage()
{
	//qDebug()<<"onImage()" <<m_Widget->width()<< " " <<m_Widget->height();
	QImage image(video_dst_data[0],video_dec_ctx->width,video_dec_ctx->height,QImage::Format_RGB32);
	QImage destImage = image.scaled(m_Widget->width(),m_Widget->height(),Qt::IgnoreAspectRatio);
	m_Widget->setPixmap(QPixmap::fromImage(destImage));
	//m_Widget->setPixmap(QPixmap::fromImage(image));

}

int QWidegetRender::ffInterrupt_callback(void* param)
{
	QWidegetRender* pThis = (QWidegetRender*)param;
	if (pThis)
	{
		if (pThis->m_bStop)
		return 1;
		else
		return 0;
	}
	return 1;
}


QStringList ffGetStreamInfo(QString url)
{
	QStringList result;
	result<<"0"<<"0";
	AVFormatContext* fmt_ctx = NULL;
	AVDictionary* options = NULL;
	QByteArray arr = url.toLatin1();
	const char* playUrl = arr.data();
	av_dict_set(&options, "rtsp_transport", "tcp", 0);
	if (avformat_open_input(&fmt_ctx, playUrl, NULL, &options) < 0) {
		qDebug()<<"avformat_open_input error!";
		return result;
	}

	if (avformat_find_stream_info(fmt_ctx,NULL) < 0)
	{
		qDebug()<<"avformat_find_stream_info error!";
		if(&fmt_ctx)
			avformat_close_input(&fmt_ctx);
		return result;
	}
	for (int i = 0; i < fmt_ctx->nb_streams;i++)
	{
		AVStream* pstream =  fmt_ctx->streams[i];
		if (pstream)
		{
			AVCodecContext* pCodec = pstream->codec;
			if (pCodec && pCodec->codec_type == AVMEDIA_TYPE_VIDEO)
			{				
				QString width;
				QString heigt;
				width.setNum(pCodec->width);
				heigt.setNum(pCodec->height);
				result[0] = width;
				result[1] = heigt;
			}
		}
	}
	if(&fmt_ctx)
		avformat_close_input(&fmt_ctx);
	return result ;

}