1. 程式人生 > >原來Qt從視訊中獲取每一幀資料如此簡單

原來Qt從視訊中獲取每一幀資料如此簡單

有時候需要在視訊上畫圖,所以需要能獲取到每一幀視訊資料。

以前從視訊檔案或視訊流中得到幀,一般都是使用qt + ffmpeg或qt + vlc。

qt對顯示處理視訊大體有以下方法:

1. QMediaPlayer + QVideoWidget

這種方法只適合簡單的顯示視訊功能,不適合對視訊進行處理(比如畫圖)

2. QMediaPlayer + QGraphicsVideoItem + QGraphicsScene + QGraphicsView

這種方法功能強大,除了顯示視訊功能,還可以做複雜的圖形處理(具體可以檢視QGraphicsScene的使用)

3. QMediaPlayer + QAbstractVideoSurface

這種方法比較簡單,是我下面要介給的。可以獲取到每一幀視訊資料,基本可以實現與qt + ffmpeg或qt + vlc相同的效果。

自定義VideoSurface:

class VideoSurface : public QAbstractVideoSurface
{
	Q_OBJECT

public:
	VideoSurface(QObject *parent = Q_NULLPTR);
	~VideoSurface();

	QList<QVideoFrame::PixelFormat> supportedPixelFormats(QAbstractVideoBuffer::HandleType handleType = QAbstractVideoBuffer::NoHandle) const;
	bool present(const QVideoFrame &frame);

signals:
	void frameAvailable(QVideoFrame &frame);

};

實現如下:

VideoSurface::VideoSurface(QObject *parent)
	: QAbstractVideoSurface(parent)
{
}

VideoSurface::~VideoSurface()
{
}

QList<QVideoFrame::PixelFormat> VideoSurface::supportedPixelFormats(QAbstractVideoBuffer::HandleType handleType) const
{
	QList<QVideoFrame::PixelFormat> listPixelFormats;

	listPixelFormats << QVideoFrame::Format_ARGB32
		<< QVideoFrame::Format_ARGB32_Premultiplied
		<< QVideoFrame::Format_RGB32
		<< QVideoFrame::Format_RGB24
		<< QVideoFrame::Format_RGB565
		<< QVideoFrame::Format_RGB555
		<< QVideoFrame::Format_ARGB8565_Premultiplied
		<< QVideoFrame::Format_BGRA32
		<< QVideoFrame::Format_BGRA32_Premultiplied
		<< QVideoFrame::Format_BGR32
		<< QVideoFrame::Format_BGR24
		<< QVideoFrame::Format_BGR565
		<< QVideoFrame::Format_BGR555
		<< QVideoFrame::Format_BGRA5658_Premultiplied
		<< QVideoFrame::Format_AYUV444
		<< QVideoFrame::Format_AYUV444_Premultiplied
		<< QVideoFrame::Format_YUV444
		<< QVideoFrame::Format_YUV420P
		<< QVideoFrame::Format_YV12
		<< QVideoFrame::Format_UYVY
		<< QVideoFrame::Format_YUYV
		<< QVideoFrame::Format_NV12
		<< QVideoFrame::Format_NV21
		<< QVideoFrame::Format_IMC1
		<< QVideoFrame::Format_IMC2
		<< QVideoFrame::Format_IMC3
		<< QVideoFrame::Format_IMC4
		<< QVideoFrame::Format_Y8
		<< QVideoFrame::Format_Y16
		<< QVideoFrame::Format_Jpeg
		<< QVideoFrame::Format_CameraRaw
		<< QVideoFrame::Format_AdobeDng;

	//qDebug() << listPixelFormats;

	// Return the formats you will support
	return listPixelFormats;
}

bool  VideoSurface::present(const QVideoFrame &frame)
{
	// Handle the frame and do your processing
	if (frame.isValid())
	{
		QVideoFrame cloneFrame(frame);
		emit frameAvailable(cloneFrame);

		return true;
	}

	return false;
}

看了上面的程式碼就知道,只需要外部連線frameAvailable訊號就可以獲取到每一幀資料。

具體使用:

QMediaPlayer *mediaPlayer = new QMediaPlayer;
VideoSurface *videoSurface = new VideoSurface;
mediaPlayer->setVideoOutput(videoSurface);
mediaPlayer->setMedia(QUrl("rtsp://admin:[email protected]"));
mediaPlayer->play();

把frameAvailable訊號與顯示視窗的槽連線,比如:

connect(videoSurface, SIGNAL(frameAvailable(QVideoFrame &)), this, SLOT(ProcessFrame(QVideoFrame &)));
void QtVideoTest::ProcessFrame(QVideoFrame &frame)
{
	qDebug() << "=============ProcessFrame===============";
	qDebug() << "width : " << frame.width() << " height : " << frame.height();
	qDebug() << "start time : " << frame.startTime()/1000 << "ms";
	qDebug() << "end time : " << frame.endTime()/1000 << "ms";
	qDebug() << "pixelFormat :" << frame.pixelFormat();

	frame.map(QAbstractVideoBuffer::ReadOnly);
	QImage recvImage(frame.bits(), frame.width(), frame.height(), QVideoFrame::imageFormatFromPixelFormat(frame.pixelFormat()));

	qDebug() << "frame data size :" << frame.mappedBytes();
	frame.unmap();
}

如上,QVideoFrame轉QImage,拿QImage進行畫圖操作就簡單了。