如题所示,在使用海康网络摄像头的SDK开发时,最常用的视频显示方法是使用NET_DVR_RealPlay_V40
或者NET_DVR_RealPlay_V30
函数,在预览参数结构体内的hPlayWnd
参数内放入播放窗口的句柄。这样就能在海康的网络摄像头就能播放到指定界面,同时跟随界面的大小自动调整大小。(注:Linux版本SDK开发,预览接口直接传入有效窗口句柄,在预览过程中,改变窗口大小时,需要调用NET_DVR_ChangeWndResolution
通知底层播放库去获取窗口大小。Windows系统下开发不需要调用相关接口,可以自适应。)
QT的QWidget开发时,包括QWidget、QLabel、QDialog等都是QWidget派生的子类,因此具有WinID()
函数,可以直接获取控件的显示句柄。但是当使用QML作为开发界面设计时,就会遇到问题。由于在QML程序设计中.qml文件只是个界面描述文件,而整个界面是由QQuickWindow或者QQuickView装载的,而只有这两个类继承自QWidget,具有WinID()
函数。若将QQuickWindow或者QQuickView的窗口句柄传入海康播放的SDK则会单独弹出一个视频框,不能在标签内局部显示视频。
折腾了好久,最后基于以上问题找到两个解决方案。
第一是在目标QML界面中嵌入QWidget,参考文章:探索QML与QWidget的无缝集成:QML窗口句柄获取与QWidget嵌入,但是这样会存在一个问题,即main.cpp文件内的应用对象的定义必须采用QApplication,而不能使用更轻量级的QGuiApplication,对于某些情况使用还是具有局限性。这种情况的海康相机播放效果后续实验再验证。
第二就是使用海康的回调函数,取流再放入QVideoFrame,Qt6可以通过QVideoSink显示,Qt5可以通过QAbstractVideoSurface显示。这样就可以在QML界面上任意控制显示区域了。
以QT6为例,记录下第二种方法的具体操作:
新建一个继承自QObject的类,如FrameProvider,并声明videoSink的属性,
FrameProvider.h
#pragma once
#ifndef FRAMEPROVIDER_H
#define FRAMEPROVIDER_H
#include "HCNetSDK.h"
#include <QObject>
#include <QVideoSink>
#include <QPointer>
#include <QTimer>
class FrameProvider : public QObject
{
Q_OBJECT
Q_PROPERTY(QVideoSink* videoSink READ videoSink WRITE setVideoSink NOTIFY videoSinkChanged)
public:
explicit FrameProvider(QObject* parent = nullptr);
~FrameProvider();
QVideoSink* videoSink() const {
return m_videoSink;
}
void setVideoSink(QVideoSink* videoSink) {
if (m_videoSink == videoSink)
return;
m_videoSink = videoSink;
emit videoSinkChanged();
};
BOOL initHCNet();
QTimer* m_timer;
LONG lLoginID;
LONG m_lPlayHandle;
signals:
void videoSinkChanged();
public slots:
void deliverFrame(const QVideoFrame& frame);
void mTimerOut();
private:
QPointer<QVideoSink> m_videoSink;
};
#endif //FRAMEPROVIDER_H
videoSink 属性主要是在QML界面中连接VideoOutput,同时声明帧传递函数deliverFrame(const QVideoFrame& frame)
,用于将QFrame视频帧传入videoSink。
具体实现如下:
FrameProvider.cpp
#include "FrameProvider.h"
#include "plaympeg4.h"
#include <QDebug>
#include <QVideoFrame>
#