10#include <private/qhwvideobuffer_p.h>
11#include <private/qvideoframe_p.h>
13#include <QtCore/qabstracteventdispatcher.h>
14#include <QtCore/qcoreapplication.h>
15#include <QtCore/qdir.h>
16#include <QtCore/qfileinfo.h>
17#include <QtCore/quuid.h>
18#include <mm/renderer.h>
19#include <qmediaplayer.h>
20#include <qqnxaudiooutput_p.h>
21#include <qaudiooutput.h>
32 return std::floor(rate * 1000);
37 return std::floor(speed / 1000.0);
42 return std::clamp<
int>(std::floor(volume * 100.0), 0, 100);
50 const int slashPos = value.indexOf(QChar(u'/'));
55 const int level = value.left(slashPos).toInt(&ok);
59 const int capacity = value.mid(slashPos + 1).toInt(&ok);
60 if (!ok || capacity < 0)
63 return { level, capacity,
true };
72 m_windowGrabber = QQnxWindowGrabber;
88 m_handle = m_windowGrabber->getNextTextureId();
105 if (mode != QVideoFrame::ReadOnly)
109 qWarning(
"QnxRasterBuffer: need to unmap before mapping");
113 buffer = m_windowGrabber->getNextBuffer();
117 .bytesPerLine = { buffer.stride },
118 .data = { buffer.data },
119 .dataSize = { buffer.width * buffer.height * buffer.pixelSize }
132 QQnxWindowGrabber::BufferView buffer;
139 , QPlatformMediaPlayer(parent)
140 , m_windowGrabber(
new QQnxWindowGrabber(
this))
142 m_flushPositionTimer.setSingleShot(
true);
143 m_flushPositionTimer.setInterval(100);
145 connect(&m_flushPositionTimer, &QTimer::timeout,
this, &QQnxMediaPlayer::flushPosition);
147 connect(m_windowGrabber, &QQnxWindowGrabber::updateScene,
this, &QQnxMediaPlayer::updateScene);
161 static int idCounter = 0;
163 m_connection = mmr_connect(
nullptr);
165 emitPError(QString::fromLatin1(
"Unable to connect to the multimedia renderer"));
170 m_contextName = QString::fromLatin1(
"QQnxMediaPlayer_%1_%2").arg(m_id)
171 .arg(QCoreApplication::applicationPid());
172 m_context = mmr_context_create(m_connection, m_contextName.toLatin1().constData(),
173 0, S_IRWXU|S_IRWXG|S_IRWXO);
175 emitPError(QString::fromLatin1(
"Unable to create context"));
185 if (!event || event->type != MMR_EVENT_STATE)
188 switch (event->state) {
189 case MMR_STATE_DESTROYED:
192 mediaStatusChanged(QMediaPlayer::NoMedia);
193 stateChanged(QMediaPlayer::StoppedState);
197 case MMR_STATE_STOPPED:
198 stateChanged(QMediaPlayer::StoppedState);
201 if (m_platformVideoSink)
202 m_platformVideoSink->setVideoFrame({});
204 case MMR_STATE_PLAYING:
205 if (event->speed == 0) {
206 stateChanged(QMediaPlayer::PausedState);
208 }
else if (state() == QMediaPlayer::PausedState) {
210 stateChanged(QMediaPlayer::PlayingState);
213 stateChanged(QMediaPlayer::PlayingState);
216 if (event->speed != m_speed) {
217 m_speed = event->speed;
219 if (state() != QMediaPlayer::PausedState)
220 m_configuredSpeed = m_speed;
222 playbackRateChanged(::speedToRate(m_speed));
230 if (!event || event->type != MMR_EVENT_STATUS)
234 handleMmEventStatusData(event->data);
237 if (!event->pos_str || isPendingPositionFlush())
240 const QByteArray valueBa(event->pos_str);
243 const qint64 position = valueBa.toLongLong(&ok);
246 qCritical(
"Could not parse position from '%s'", valueBa.constData());
248 handleMmPositionChanged(position);
256 const auto getValue = [data](
const char *key) -> QString {
257 const strm_string_t *value = strm_dict_find_rstr(data, key);
262 return QString::fromUtf8(strm_string_get(value));
266 const QString bufferLevel = getValue(
"bufferlevel");
268 if (!bufferLevel.isEmpty()) {
269 const auto & [level, capacity, ok] = ::parseBufferLevel(bufferLevel);
272 updateBufferLevel(level, capacity);
274 qCritical(
"Could not parse buffer capacity from '%s'", qUtf8Printable(bufferLevel));
278 const QString bufferStatus = getValue(
"bufferstatus");
279 const QString suspended = getValue(
"suspended");
281 if (suspended == QStringLiteral(
"yes"))
282 mediaStatusChanged(QMediaPlayer::StalledMedia);
283 else if (bufferStatus == QStringLiteral(
"buffering"))
284 mediaStatusChanged(QMediaPlayer::BufferingMedia);
285 else if (bufferStatus == QStringLiteral(
"playing"))
286 mediaStatusChanged(QMediaPlayer::BufferedMedia);
299 if (event->details.error.info.error_code == MMR_ERROR_NONE) {
300 mediaStatusChanged(QMediaPlayer::EndOfMedia);
301 stateChanged(QMediaPlayer::StoppedState);
310 mmr_context_destroy(m_context);
312 m_contextName.clear();
316 mmr_disconnect(m_connection);
317 m_connection =
nullptr;
326 if (url.isLocalFile() || url.scheme().isEmpty()) {
327 const QString relativeFilePath = url.scheme().isEmpty() ? url.path() : url.toLocalFile();
328 const QFileInfo fileInfo(relativeFilePath);
329 return QFile::encodeName(QStringLiteral(
"file://") + fileInfo.absoluteFilePath());
333 return url.toEncoded();
340 if (isInputAttached())
343 if (!m_media.isValid() || !m_context) {
344 mediaStatusChanged(QMediaPlayer::NoMedia);
350 if (!(attachVideoOutput() && attachAudioOutput() && attachInput())) {
355 mediaStatusChanged(QMediaPlayer::LoadedMedia);
360 if (isVideoOutputAttached()) {
361 qWarning() <<
"QQnxMediaPlayer: Video output already attached!";
366 qWarning() <<
"QQnxMediaPlayer: No media player context!";
370 const QByteArray windowGroupId = m_windowGrabber->windowGroupId();
371 if (windowGroupId.isEmpty()) {
372 qWarning() <<
"QQnxMediaPlayer: Unable to find window group";
376 static int winIdCounter = 0;
378 const QString windowName = QStringLiteral(
"QQnxVideoSink_%1_%2")
380 .arg(QCoreApplication::applicationPid());
382 m_windowGrabber->setWindowId(windowName.toLatin1());
384 if (m_platformVideoSink)
385 m_windowGrabber
->setRhi(m_platformVideoSink->rhi());
388 const QString videoDeviceUrl = QStringLiteral(
"screen:?winid=%1&wingrp=%2&initflags=invisible&nodstviewport=1")
389 .arg(windowName, QString::fromLatin1(windowGroupId));
391 m_videoId = mmr_output_attach(m_context, videoDeviceUrl.toLatin1().constData(),
"video");
393 if (m_videoId == -1) {
394 qWarning() <<
"mmr_output_attach() for video failed";
403 if (isAudioOutputAttached()) {
404 qWarning() <<
"QQnxMediaPlayer: Audio output already attached!";
408 const QByteArray defaultAudioDevice = qgetenv(
"QQNX_RENDERER_DEFAULT_AUDIO_SINK");
412 const char *fallbackDevice =
419 m_audioId = mmr_output_attach(m_context,
420 defaultAudioDevice.isEmpty() ? fallbackDevice : defaultAudioDevice.constData(),
"audio");
422 if (m_audioId == -1) {
423 emitMmError(
"mmr_output_attach() for audio failed");
433 if (isInputAttached())
436 const QByteArray resourcePath = resourcePathForUrl(m_media);
438 if (resourcePath.isEmpty())
441 if (mmr_input_attach(m_context, resourcePath.constData(),
"track") != 0) {
442 emitMmError(QStringLiteral(
"mmr_input_attach() failed for ")
443 + QString::fromUtf8(resourcePath));
445 mediaStatusChanged(QMediaPlayer::InvalidMedia);
450 m_inputAttached =
true;
460 if (isVideoOutputAttached())
463 if (isAudioOutputAttached())
466 if (isInputAttached())
476 if (m_platformVideoSink)
477 m_platformVideoSink->setVideoFrame({});
479 if (isVideoOutputAttached())
480 mmr_output_detach(m_context, m_videoId);
487 if (isAudioOutputAttached())
488 mmr_output_detach(m_context, m_audioId);
495 if (isInputAttached())
496 mmr_input_detach(m_context);
498 m_inputAttached =
false;
503 return m_videoId != -1;
508 return m_audioId != -1;
513 return m_inputAttached;
518 if (!m_platformVideoSink)
521 QVideoFrameFormat format(size, QVideoFrameFormat::Format_BGRX8888);
523 const QVideoFrame actualFrame = m_windowGrabber->isEglImageSupported()
524 ? QVideoFramePrivate::createFrame(std::make_unique<QnxTextureBuffer>(m_windowGrabber),
526 : QVideoFramePrivate::createFrame(std::make_unique<QnxRasterBuffer>(m_windowGrabber),
529 m_platformVideoSink->setVideoFrame(actualFrame);
534 return m_metaData.duration();
544 if (m_position == position)
547 m_pendingPosition = position;
548 m_flushPositionTimer.start();
553 if (!m_context || !m_metaData.isSeekable() || mediaStatus() == QMediaPlayer::NoMedia)
556 if (mmr_seek(m_context, QString::number(position).toLatin1().constData()) != 0)
557 emitMmError(
"Seeking failed");
562 setPositionInternal(m_pendingPosition);
567 return m_flushPositionTimer.isActive();
572 m_deferredSpeedEnabled = enabled;
577 return m_deferredSpeedEnabled;
584 if (m_volume == normalizedVolume)
587 m_volume = normalizedVolume;
595 if (m_muted == muted)
605 if (!m_context || m_audioId == -1)
608 const int volume = m_muted ? 0 : m_volume;
611 std::snprintf(buf,
sizeof buf,
"%d", volume);
614 dict = strm_dict_set(dict,
"volume", buf);
616 if (mmr_output_parameters(m_context, m_audioId, dict) != 0)
617 emitMmError(
"mmr_output_parameters: Setting volume failed");
622 QAudioOutput *out = output ? output->q :
nullptr;
623 if (m_audioOutput == out)
627 disconnect(m_audioOutput.get());
630 connect(out, &QAudioOutput::volumeChanged,
this, &QQnxMediaPlayer::setVolume);
631 connect(out, &QAudioOutput::mutedChanged,
this, &
QQnxMediaPlayer::setMuted);
633 setVolume(out ? out->volume() : 1.);
634 setMuted(out ? out->isMuted() :
true);
641 return m_bufferLevel/100.0f;
646 return m_metaData.hasAudio();
651 return m_metaData.hasVideo();
656 return m_metaData.isSeekable();
662 return QMediaTimeRange(0, m_metaData.duration());
667 return ::speedToRate(m_speed);
675 const int speed = ::rateToSpeed(rate);
677 if (m_speed == speed)
682 if (isDeferredSpeedEnabled() && state() != QMediaPlayer::PlayingState) {
683 m_deferredSpeed = speed;
687 if (mmr_speed_set(m_context, speed) != 0)
688 emitMmError(
"mmr_speed_set failed");
709 stateChanged(QMediaPlayer::StoppedState);
710 mediaStatusChanged(QMediaPlayer::LoadingMedia);
714 updateMetaData(
nullptr);
720 if (!m_media.isValid() || !m_connection || !m_context || m_audioId == -1) {
721 stateChanged(QMediaPlayer::StoppedState);
725 if (state() == QMediaPlayer::PlayingState)
728 setDeferredSpeedEnabled(
false);
730 if (m_deferredSpeed) {
731 setPlaybackRate(::speedToRate(*m_deferredSpeed));
732 m_deferredSpeed = {};
734 setPlaybackRate(::speedToRate(m_configuredSpeed));
737 setDeferredSpeedEnabled(
true);
740 if (state() == QMediaPlayer::PausedState) {
745 if (mediaStatus() == QMediaPlayer::EndOfMedia)
746 setPositionInternal(0);
751 if (mmr_play(m_context) != 0) {
752 stateChanged(QMediaPlayer::StoppedState);
753 emitMmError(
"mmr_play() failed");
757 stateChanged(QMediaPlayer::PlayingState);
762 if (state() != QMediaPlayer::PlayingState)
771 || state() == QMediaPlayer::StoppedState
772 || mediaStatus() == QMediaPlayer::NoMedia)
776 setPositionInternal(0);
783 m_platformVideoSink = videoSink
784 ?
static_cast<QQnxVideoSink *>(videoSink->platformVideoSink())
790 m_eventThread =
new QQnxMediaEventThread(m_context);
792 connect(m_eventThread, &QQnxMediaEventThread::eventPending,
795 m_eventThread->setObjectName(QStringLiteral(
"MmrEventThread-") + QString::number(m_id));
796 m_eventThread->start();
801 delete m_eventThread;
802 m_eventThread =
nullptr;
814 m_position = newPosition;
816 if (state() == QMediaPlayer::PausedState)
819 positionChanged(m_position);
824 m_bufferLevel = capacity == 0 ? 0 : level /
static_cast<
float>(capacity) * 100.0f;
825 m_bufferLevel = qBound(0, m_bufferLevel, 100);
826 bufferProgressChanged(m_bufferLevel/100.0f);
831 m_metaData.update(dict);
833 durationChanged(m_metaData.duration());
834 audioAvailableChanged(m_metaData.hasAudio());
835 videoAvailableChanged(m_metaData.hasVideo());
836 seekableChanged(m_metaData.isSeekable());
841 emitMmError(QString::fromUtf8(msg));
846 int errorCode = MMR_ERROR_NONE;
847 const QString errorMessage = mmErrorMessage(msg, m_context, &errorCode);
848 emit error(QMediaPlayer::ResourceError, errorMessage);
853 const QString errorMessage = QString::fromLatin1(
"%1: %2").arg(msg).arg(QString::fromUtf8(strerror(errno)));
854 emit error(QMediaPlayer::ResourceError, errorMessage);
860 while (
const mmr_event_t *event = mmr_event_get(m_context)) {
861 if (event->type == MMR_EVENT_NONE)
864 switch (event->type) {
865 case MMR_EVENT_STATUS:
866 handleMmEventStatus(event);
868 case MMR_EVENT_STATE:
869 handleMmEventState(event);
871 case MMR_EVENT_METADATA:
872 updateMetaData(event->data);
874 case MMR_EVENT_ERROR:
875 handleMmEventError(event);
878 case MMR_EVENT_OVERFLOW:
879 case MMR_EVENT_WARNING:
880 case MMR_EVENT_PLAYLIST:
881 case MMR_EVENT_INPUT:
882 case MMR_EVENT_OUTPUT:
883 case MMR_EVENT_CTXTPAR:
884 case MMR_EVENT_TRKPAR:
885 case MMR_EVENT_OTHER:
896#include "moc_qqnxmediaplayer_p.cpp"
MapData map(QVideoFrame::MapMode mode) override
Maps the planes of a video buffer to memory.
QnxRasterBuffer(QQnxWindowGrabber *windowGrabber)
void unmap() override
Releases the memory mapped by the map() function.
QVideoFrameFormat format() const override
Gets \l QVideoFrameFormat of the underlying video buffer.
quint64 textureHandle(QRhi &, int plane) override
QnxTextureBuffer(QQnxWindowGrabber *QQnxWindowGrabber)
MapData map(QVideoFrame::MapMode) override
Maps the planes of a video buffer to memory.
void unmap() override
Releases the memory mapped by the map() function.