File indexing completed on 2025-01-05 03:57:27
0001 /* ============================================================ 0002 * 0003 * This file is a part of digiKam project 0004 * https://www.digikam.org 0005 * 0006 * Date : 2024-01-14 0007 * Description : QtAVPlayer video integration class. 0008 * 0009 * SPDX-FileCopyrightText: 2023-2024 by Gilles Caulier <caulier dot gilles at gmail dot com> 0010 * 0011 * SPDX-License-Identifier: GPL-2.0-or-later 0012 * 0013 * ============================================================ */ 0014 0015 #include "dvideowidget.h" 0016 0017 // Qt includes 0018 0019 #include <QList> 0020 #include <QMutex> 0021 #include <QMutexLocker> 0022 #include <QVBoxLayout> 0023 #include <QAbstractVideoSurface> 0024 #include <QVideoSurfaceFormat> 0025 #include <QGraphicsScene> 0026 #include <QGraphicsVideoItem> 0027 0028 // Local includes 0029 0030 #include "digikam_debug.h" 0031 0032 namespace Digikam 0033 { 0034 0035 class Q_DECL_HIDDEN DVideoWidget::Private 0036 { 0037 0038 public: 0039 0040 Private() = default; 0041 0042 QGraphicsScene* videoScene = nullptr; 0043 QGraphicsView* videoView = nullptr; 0044 QGraphicsVideoItem* videoItem = nullptr; 0045 0046 QAVPlayer* player = nullptr; 0047 QAVAudioOutput* audioOutput = nullptr; 0048 0049 QVideoFrame videoFrame; 0050 QMutex mutex; 0051 0052 int videoOrientation = 0; 0053 }; 0054 0055 DVideoWidget::DVideoWidget(QWidget* const parent) 0056 : QFrame(parent), 0057 d (new Private) 0058 { 0059 setMouseTracking(true); 0060 setFrameStyle(QFrame::StyledPanel | QFrame::Plain); 0061 setLineWidth(1); 0062 0063 d->videoScene = new QGraphicsScene(parent); 0064 d->videoView = new QGraphicsView(d->videoScene); 0065 d->videoView->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); 0066 d->videoView->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); 0067 d->videoView->setSizeAdjustPolicy(QAbstractScrollArea::AdjustToContents); 0068 d->videoItem = new QGraphicsVideoItem(); 0069 d->videoScene->addItem(d->videoItem); 0070 d->videoItem->setAspectRatioMode(Qt::IgnoreAspectRatio); 0071 d->videoView->setMouseTracking(true); 0072 0073 QVBoxLayout* const vbox2 = new QVBoxLayout(this); 0074 vbox2->addWidget(d->videoView); 0075 vbox2->setContentsMargins(QMargins(0, 0, 0, 9)); 0076 vbox2->setSpacing(0); 0077 0078 d->player = new QAVPlayer(this); 0079 0080 d->audioOutput = new QAVAudioOutput(this); 0081 0082 connect(d->player, &QAVPlayer::audioFrame, 0083 this, &DVideoWidget::slotAudioFrame, 0084 Qt::DirectConnection); 0085 0086 connect(d->player, &QAVPlayer::videoFrame, 0087 this, &DVideoWidget::slotVideoFrame, 0088 Qt::DirectConnection); 0089 } 0090 0091 DVideoWidget::~DVideoWidget() 0092 { 0093 delete d; 0094 } 0095 0096 QAVPlayer* DVideoWidget::player() const 0097 { 0098 return d->player; 0099 } 0100 0101 QGraphicsView* DVideoWidget::view() const 0102 { 0103 return d->videoView; 0104 } 0105 0106 QAVAudioOutput* DVideoWidget::audioOutput() const 0107 { 0108 return d->audioOutput; 0109 } 0110 0111 QVideoFrame DVideoWidget::videoFrame() const 0112 { 0113 QMutexLocker lock(&d->mutex); 0114 0115 return d->videoFrame; 0116 } 0117 0118 void DVideoWidget::slotAudioFrame(const QAVAudioFrame& frame) 0119 { 0120 d->audioOutput->play(frame); 0121 } 0122 0123 void DVideoWidget::slotVideoFrame(const QAVVideoFrame& frame) 0124 { 0125 if (!d->videoItem->videoSurface()) 0126 { 0127 return; 0128 } 0129 0130 { 0131 QMutexLocker lock(&d->mutex); 0132 0133 d->videoFrame = frame.convertTo(AV_PIX_FMT_RGB32); 0134 } 0135 0136 if ( 0137 !d->videoItem->videoSurface()->isActive() || 0138 (d->videoItem->videoSurface()->surfaceFormat().frameSize() != d->videoFrame.size()) 0139 ) 0140 { 0141 QVideoSurfaceFormat f(d->videoFrame.size(), d->videoFrame.pixelFormat(), d->videoFrame.handleType()); 0142 d->videoItem->videoSurface()->start(f); 0143 } 0144 0145 if (d->videoItem->videoSurface()->isActive()) 0146 { 0147 d->videoItem->videoSurface()->present(d->videoFrame); 0148 } 0149 0150 Q_EMIT positionChanged(d->player->position()); 0151 } 0152 0153 int DVideoWidget::videoMediaOrientation() const 0154 { 0155 int orientation = 0; 0156 0157 if (d->player->availableVideoStreams().count() > 0) 0158 { 0159 qCDebug(DIGIKAM_GENERAL_LOG) << "Video Metadata from" << d->player->source(); 0160 qCDebug(DIGIKAM_GENERAL_LOG) << "---"; 0161 qCDebug(DIGIKAM_GENERAL_LOG) << "Video Streams Available:" << d->player->availableVideoStreams().count(); 0162 0163 Q_FOREACH (const QAVStream& vstream, d->player->availableVideoStreams()) 0164 { 0165 QMap<QString, QString> vals = vstream.metadata(); 0166 0167 for (QMap<QString, QString>::const_iterator it = vals.constBegin() ; it != vals.constEnd() ; ++it) 0168 { 0169 qCDebug(DIGIKAM_GENERAL_LOG) << it.key() << it.value(); 0170 } 0171 } 0172 0173 qCDebug(DIGIKAM_GENERAL_LOG) << "---"; 0174 0175 QList<QAVStream> vstream = d->player->currentVideoStreams(); 0176 0177 if (!vstream.isEmpty()) 0178 { 0179 QMap<QString, QString> vals = vstream.first().metadata(); 0180 0181 if (!vals.isEmpty()) 0182 { 0183 if (vals.contains(QLatin1String("rotate"))) 0184 { 0185 orientation = vals[QLatin1String("rotate")].toInt(); 0186 } 0187 } 0188 } 0189 } 0190 0191 return orientation; 0192 } 0193 0194 void DVideoWidget::adjustVideoSize() 0195 { 0196 d->videoItem->resetTransform(); 0197 0198 QSizeF nativeSize = d->videoItem->nativeSize(); 0199 int mediaOrientation = videoMediaOrientation(); 0200 0201 if ((nativeSize.width() < 1.0) || 0202 (nativeSize.height() < 1.0)) 0203 { 0204 return; 0205 } 0206 0207 if ((mediaOrientation == 90) || 0208 (mediaOrientation == 270)) 0209 { 0210 nativeSize.transpose(); 0211 } 0212 0213 double ratio = (nativeSize.width() / 0214 nativeSize.height()); 0215 0216 if (d->videoView->width() > d->videoView->height()) 0217 { 0218 QSizeF vsize(d->videoView->height() * ratio, 0219 d->videoView->height()); 0220 d->videoItem->setSize(vsize); 0221 } 0222 else 0223 { 0224 QSizeF vsize(d->videoView->width(), 0225 d->videoView->width() / ratio); 0226 d->videoItem->setSize(vsize); 0227 } 0228 0229 d->videoView->setSceneRect(0, 0, d->videoItem->size().width(), 0230 d->videoItem->size().height()); 0231 0232 QPointF center = d->videoItem->boundingRect().center(); 0233 d->videoItem->setTransformOriginPoint(center); 0234 d->videoItem->setRotation(d->videoOrientation); 0235 0236 d->videoView->fitInView(d->videoItem, Qt::KeepAspectRatio); 0237 d->videoView->centerOn(d->videoItem); 0238 d->videoView->raise(); 0239 } 0240 0241 void DVideoWidget::setVideoItemOrientation(int orientation) 0242 { 0243 d->videoOrientation = orientation; 0244 adjustVideoSize(); 0245 } 0246 0247 int DVideoWidget::videoItemOrientation() const 0248 { 0249 return d->videoOrientation; 0250 } 0251 0252 } // namespace Digikam 0253 0254 #include "moc_dvideowidget.cpp"