Qt Qml 开发超高清 4K、8K 视频直播视频客户端
Qt Qml 中提供了丰富的多媒体相关的模块:
类型 | 描述 |
---|---|
MediaPlayer | 为场景添加音频/视频播放功能。 |
CaptureSession | 创建一个用于捕获音频/视频的会话。 |
Camera | 访问连接到系统的相机。 |
AudioInput | 访问连接到系统的音频输入。 |
AudioOutput | 访问连接到系统的音频输出(扬声器、耳机)。 |
VideoOutput | 显示视频内容。 |
MediaRecorder | 记录来自 CaptureSession 的音频/视频。 |
ImageCapture | 从相机中捕捉静止图像。 |
Video | 将视频播放功能添加到场景中, 使用 MediaPlayer 和 VideoOutput 类型来提供视频播放功能。 |
想要实现视频客户端,这里只需使用 VideoOutput
和 MediaPlayer
即可。
一般本地播放的简单使用如下:
Rectangle {
width: 800
height: 600
color: "black"
MediaPlayer {
id: player
source: "file://video.webm"
videoOutput: videoOutput
}
VideoOutput {
id: videoOutput
anchors.fill: parent
}
}
然而这种方式依赖 MediaPlayer
,而 MediaPlayer
依赖系统 ( 本机平台 ) 提供的编解码器,效果不佳且没有扩展性,因此我们采用SkeyePlayerPro
做为后端播放器。
关于SkeyePlayerPro
是视开科技开发和维护的全功能的流媒体播放器,支持 RTSP、RTMP、HTTP、HLS、UDP、RTP、File 等多种流媒体协议播放、支持本地文件播放,支持本地抓拍、本地录像、播放旋转、多屏播放、倍数播放等多种功能特性,核心基于 FFmpeg,稳定、高效、可靠、可控,支持 Windows、Android、iOS 等多个平台,目前在多家教育、安防、行业型公司,都得到的应用,广受好评!
- 支持高效 4K / 8K 解码。
- 支持 CPU 软解 / GPU 硬解。
- 支持视频如 H.264,H.265,MPEG4,MJPEG。
- API 简单好用且易于集成。
Qt 中集成相当容易,.pro
加入下面命令即可:
#SkeyePlayerPro相关
LIBS += -L$$PWD/lib/Player \
-llibSkeyePlayer \
-llibSkeyePlayerPro
INCLUDEPATH += $$PWD/lib/Player/Src \
$$PWD/lib/SkeyePlayer
另一方面,想要在 QML 中播放视频我们需要自己实现一个提供视频帧的 QML 接口类,有两种方法:
- 提供
QMediaObject
派生类属性,该属性需要具有可用的QVideoRenderControl
,类似于下面:
class MyMediaPlayer : QObject
{
public:
QMediaObject *mediaObject();
};
- 基于
QObject
的类提供可写videoSurface
属性,可以接受基于QAbstractVideoSurface
的类,然后传递自己的QVideoFrame
即可,这也正是我使用的方法:
class VideoFrameProvider : public QObject
{
Q_OBJECT
Q_PROPERTY(QAbstractVideoSurface *videoSurface READ videoSurface WRITE setVideoSurface)
Q_PROPERTY(QString videoUrl READ videoUrl WRITE setVideoUrl NOTIFY videoUrlChanged)
public:
VideoFrameProvider(QObject *parent = nullptr);
~VideoFrameProvider();
QAbstractVideoSurface *videoSurface();
void setVideoSurface(QAbstractVideoSurface *surface);
QString videoUrl() const;
void setVideoUrl(const QString &url);
void setFormat(int width, int heigth, QVideoFrame::PixelFormat pixFormat);
signals:
void newVideoFrame(const char *frame);
void videoUrlChanged();
private slots:
void onNewVideoFrameReceived(const char *frame);
private:
//SkeyePlayerPro提供的回调
static int VideoFrameProvider::playCallback(SKEYE_CALLBACK_TYPE_ENUM callbackType, int channelId, void *userPtr, int mediaType, char *buf, SKEYE_FRAME_INFO *frameInfo);
QAbstractVideoSurface *m_surface = nullptr;
QVideoSurfaceFormat m_format;
QString m_videoUrl;
bool m_initFormat = false;
}
关键实现,这里省略了一些 SkeyePlayerPro
的初始化和回调等等设置:
VideoFrameProvider::VideoFrameProvider(QObject *parent)
: QObject(parent)
{
connect(this, &VideoFrameProvider::newVideoFrame, this, &VideoFrameProvider::onNewVideoFrameReceived, Qt::QueuedConnection);
}
QAbstractVideoSurface *VideoFrameProvider::videoSurface()
{
return m_surface;
}
void VideoFrameProvider::setVideoSurface(QAbstractVideoSurface *surface)
{
if (m_surface && m_surface != surface && m_surface->isActive()) {
m_surface->stop();
}
m_surface = surface;
if (m_surface && m_format.isValid()) {
m_format = m_surface->nearestFormat(m_format);
m_surface->start(m_format);
}
}
QString VideoFrameProvider::videoUrl() const
{
return m_videoUrl;
}
void VideoFrameProvider::setVideoUrl(const QString &url)
{
if (m_videoUrl != url) {
m_videoUrl = url;
emit videoUrlChanged();
}
}
void VideoFrameProvider::setFormat(int width, int heigth, QVideoFrame::PixelFormat pixFormat)
{
QVideoSurfaceFormat format(QSize(width, heigth), pixFormat);
m_format = format;
if (m_surface) {
if (m_surface->isActive()) {
m_surface->stop();
}
m_format = m_surface->nearestFormat(format);
m_surface->start(m_format);
}
}
void VideoFrameProvider::onNewVideoFrameReceived(const char *frame)
{
int size = 0;
int width = m_format.frameWidth();
int height = m_format.frameHeight();
if (m_format.pixelFormat() == QVideoFrame::Format_YUV420P) {
size = width * height * 3 / 2;
}
QVideoFrame videoFrame(size, QSize(width, height), width, m_format.pixelFormat());
if (videoFrame.map(QAbstractVideoBuffer::WriteOnly)) {
memmove(videoFrame.bits(), frame, size);
videoFrame.unmap();
}
if (m_surface && m_surface->isActive()) {
if (!m_surface->present(videoFrame)) {
qDebug() << "VideoFrameProvider Suface Error:" << m_surface->error();
}
}
}
int VideoFrameProvider::playCallback(SKEYE_CALLBACK_TYPE_ENUM callbackType, int channelId, void *userPtr, int mediaType, char *buf, SKEYE_FRAME_INFO *frameInfo)
{
Q_UNUSED(channelId);
VideoFrameProvider *_this = reinterpret_cast<VideoFrameProvider *>(userPtr);
if (callbackType == SKEYE_TYPE_DECODE_DATA && mediaType == MEDIA_TYPE_VIDEO) {
auto frameWidth = frameInfo->width ;
auto frameHeight = frameInfo->height;
if (buf) {
if (!_this->m_initFormat) {
_this->setFormat(frameWidth, frameHeight, QVideoFrame::Format_YUV420P);
_this->m_initFormat = true;
}
emit _this->newVideoFrame(buf);
}
}
return 0;
}
最后将上面的 MediaPlayer
替换为 VideoProvider
即可:
Rectangle {
width: 800
height: 600
color: "black"
VideoFrameProvider {
id: provider
source: "rtsp://192.168.0.33:8554/channel=1"
}
VideoOutput {
id: videoOutput
anchors.fill: parent
source: provider
}
}
关于SkeyeARS
SkeyeARS全景AR增强监视系统, 是视开科技开发的一款基于宽场景多路视频无缝拼接、视频实时增强、监视目标增强显示、目标自动跟踪、视频存储回放、远程数据传输和多通道全景视频同步显示等功能的综合视频AR增强监视系统,广泛应用于智慧交通、智慧城市、智慧机场等大场景智能监控领域。