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 };
71 m_windowGrabber = QQnxWindowGrabber;
87 m_handle = m_windowGrabber->getNextTextureId();
104 if (mode != QVideoFrame::ReadOnly)
108 qWarning(
"QnxRasterBuffer: need to unmap before mapping");
112 buffer = m_windowGrabber->getNextBuffer();
116 .bytesPerLine = { buffer.stride },
117 .data = { buffer.data },
118 .dataSize = { buffer.width * buffer.height * buffer.pixelSize }
131 QQnxWindowGrabber::BufferView buffer;
138 , QPlatformMediaPlayer(parent)
139 , m_windowGrabber(
new QQnxWindowGrabber(
this))
141 m_flushPositionTimer.setSingleShot(
true);
142 m_flushPositionTimer.setInterval(100);
144 connect(&m_flushPositionTimer, &QTimer::timeout,
this, &QQnxMediaPlayer::flushPosition);
146 connect(m_windowGrabber, &QQnxWindowGrabber::updateScene,
this, &QQnxMediaPlayer::updateScene);
160 static int idCounter = 0;
162 m_connection = mmr_connect(
nullptr);
164 emitPError(QString::fromLatin1(
"Unable to connect to the multimedia renderer"));
169 m_contextName = QString::fromLatin1(
"QQnxMediaPlayer_%1_%2").arg(m_id)
170 .arg(QCoreApplication::applicationPid());
171 m_context = mmr_context_create(m_connection, m_contextName.toLatin1().constData(),
172 0, S_IRWXU|S_IRWXG|S_IRWXO);
174 emitPError(QString::fromLatin1(
"Unable to create context"));
184 if (!event || event->type != MMR_EVENT_STATE)
187 switch (event->state) {
188 case MMR_STATE_DESTROYED:
191 mediaStatusChanged(QMediaPlayer::NoMedia);
192 stateChanged(QMediaPlayer::StoppedState);
196 case MMR_STATE_STOPPED:
197 stateChanged(QMediaPlayer::StoppedState);
200 if (m_platformVideoSink)
201 m_platformVideoSink->setVideoFrame({});
203 case MMR_STATE_PLAYING:
204 if (event->speed == 0) {
205 stateChanged(QMediaPlayer::PausedState);
207 }
else if (state() == QMediaPlayer::PausedState) {
209 stateChanged(QMediaPlayer::PlayingState);
212 stateChanged(QMediaPlayer::PlayingState);
215 if (event->speed != m_speed) {
216 m_speed = event->speed;
218 if (state() != QMediaPlayer::PausedState)
219 m_configuredSpeed = m_speed;
221 playbackRateChanged(::speedToRate(m_speed));
229 if (!event || event->type != MMR_EVENT_STATUS)
233 handleMmEventStatusData(event->data);
236 if (!event->pos_str || isPendingPositionFlush())
239 const QByteArray valueBa(event->pos_str);
242 const qint64 position = valueBa.toLongLong(&ok);
245 qCritical(
"Could not parse position from '%s'", valueBa.constData());
247 handleMmPositionChanged(position);
255 const auto getValue = [data](
const char *key) -> QString {
256 const strm_string_t *value = strm_dict_find_rstr(data, key);
261 return QString::fromUtf8(strm_string_get(value));
265 const QString bufferLevel = getValue(
"bufferlevel");
267 if (!bufferLevel.isEmpty()) {
268 const auto & [level, capacity, ok] = ::parseBufferLevel(bufferLevel);
271 updateBufferLevel(level, capacity);
273 qCritical(
"Could not parse buffer capacity from '%s'", qUtf8Printable(bufferLevel));
277 const QString bufferStatus = getValue(
"bufferstatus");
278 const QString suspended = getValue(
"suspended");
280 if (suspended == QStringLiteral(
"yes"))
281 mediaStatusChanged(QMediaPlayer::StalledMedia);
282 else if (bufferStatus == QStringLiteral(
"buffering"))
283 mediaStatusChanged(QMediaPlayer::BufferingMedia);
284 else if (bufferStatus == QStringLiteral(
"playing"))
285 mediaStatusChanged(QMediaPlayer::BufferedMedia);
298 if (event->details.error.info.error_code == MMR_ERROR_NONE) {
299 mediaStatusChanged(QMediaPlayer::EndOfMedia);
300 stateChanged(QMediaPlayer::StoppedState);
309 mmr_context_destroy(m_context);
311 m_contextName.clear();
315 mmr_disconnect(m_connection);
316 m_connection =
nullptr;
325 if (url.isLocalFile() || url.scheme().isEmpty()) {
326 const QString relativeFilePath = url.scheme().isEmpty() ? url.path() : url.toLocalFile();
327 const QFileInfo fileInfo(relativeFilePath);
328 return QFile::encodeName(QStringLiteral(
"file://") + fileInfo.absoluteFilePath());
332 return url.toEncoded();
339 if (isInputAttached())
342 if (!m_media.isValid() || !m_context) {
343 mediaStatusChanged(QMediaPlayer::NoMedia);
349 if (!(attachVideoOutput() && attachAudioOutput() && attachInput())) {
354 mediaStatusChanged(QMediaPlayer::LoadedMedia);
359 if (isVideoOutputAttached()) {
360 qWarning() <<
"QQnxMediaPlayer: Video output already attached!";
365 qWarning() <<
"QQnxMediaPlayer: No media player context!";
369 const QByteArray windowGroupId = m_windowGrabber->windowGroupId();
370 if (windowGroupId.isEmpty()) {
371 qWarning() <<
"QQnxMediaPlayer: Unable to find window group";
375 static int winIdCounter = 0;
377 const QString windowName = QStringLiteral(
"QQnxVideoSink_%1_%2")
379 .arg(QCoreApplication::applicationPid());
381 m_windowGrabber->setWindowId(windowName.toLatin1());
383 if (m_platformVideoSink)
384 m_windowGrabber
->setRhi(m_platformVideoSink->rhi());
387 const QString videoDeviceUrl = QStringLiteral(
"screen:?winid=%1&wingrp=%2&initflags=invisible&nodstviewport=1")
388 .arg(windowName, QString::fromLatin1(windowGroupId));
390 m_videoId = mmr_output_attach(m_context, videoDeviceUrl.toLatin1().constData(),
"video");
392 if (m_videoId == -1) {
393 qWarning() <<
"mmr_output_attach() for video failed";
402 if (isAudioOutputAttached()) {
403 qWarning() <<
"QQnxMediaPlayer: Audio output already attached!";
407 const QByteArray defaultAudioDevice = qgetenv(
"QQNX_RENDERER_DEFAULT_AUDIO_SINK");
411 const char *fallbackDevice =
418 m_audioId = mmr_output_attach(m_context,
419 defaultAudioDevice.isEmpty() ? fallbackDevice : defaultAudioDevice.constData(),
"audio");
421 if (m_audioId == -1) {
422 emitMmError(
"mmr_output_attach() for audio failed");
432 if (isInputAttached())
435 const QByteArray resourcePath = resourcePathForUrl(m_media);
437 if (resourcePath.isEmpty())
440 if (mmr_input_attach(m_context, resourcePath.constData(),
"track") != 0) {
441 emitMmError(QStringLiteral(
"mmr_input_attach() failed for ")
442 + QString::fromUtf8(resourcePath));
444 mediaStatusChanged(QMediaPlayer::InvalidMedia);
449 m_inputAttached =
true;
459 if (isVideoOutputAttached())
462 if (isAudioOutputAttached())
465 if (isInputAttached())
475 if (m_platformVideoSink)
476 m_platformVideoSink->setVideoFrame({});
478 if (isVideoOutputAttached())
479 mmr_output_detach(m_context, m_videoId);
486 if (isAudioOutputAttached())
487 mmr_output_detach(m_context, m_audioId);
494 if (isInputAttached())
495 mmr_input_detach(m_context);
497 m_inputAttached =
false;
502 return m_videoId != -1;
507 return m_audioId != -1;
512 return m_inputAttached;
517 if (!m_platformVideoSink)
520 QVideoFrameFormat format(size, QVideoFrameFormat::Format_BGRX8888);
522 const QVideoFrame actualFrame = m_windowGrabber->isEglImageSupported()
523 ? QVideoFramePrivate::createFrame(std::make_unique<QnxTextureBuffer>(m_windowGrabber),
525 : QVideoFramePrivate::createFrame(std::make_unique<QnxRasterBuffer>(m_windowGrabber),
528 m_platformVideoSink->setVideoFrame(actualFrame);
533 return m_metaData.duration();
543 if (m_position == position)
546 m_pendingPosition = position;
547 m_flushPositionTimer.start();
552 if (!m_context || !m_metaData.isSeekable() || mediaStatus() == QMediaPlayer::NoMedia)
555 if (mmr_seek(m_context, QString::number(position).toLatin1().constData()) != 0)
556 emitMmError(
"Seeking failed");
561 setPositionInternal(m_pendingPosition);
566 return m_flushPositionTimer.isActive();
571 m_deferredSpeedEnabled = enabled;
576 return m_deferredSpeedEnabled;
583 if (m_volume == normalizedVolume)
586 m_volume = normalizedVolume;
594 if (m_muted == muted)
604 if (!m_context || m_audioId == -1)
607 const int volume = m_muted ? 0 : m_volume;
610 std::snprintf(buf,
sizeof buf,
"%d", volume);
613 dict = strm_dict_set(dict,
"volume", buf);
615 if (mmr_output_parameters(m_context, m_audioId, dict) != 0)
616 emitMmError(
"mmr_output_parameters: Setting volume failed");
621 QAudioOutput *out = output ? output->q :
nullptr;
622 if (m_audioOutput == out)
626 disconnect(m_audioOutput.get());
629 connect(out, &QAudioOutput::volumeChanged,
this, &QQnxMediaPlayer::setVolume);
630 connect(out, &QAudioOutput::mutedChanged,
this, &
QQnxMediaPlayer::setMuted);
632 setVolume(out ? out->volume() : 1.);
633 setMuted(out ? out->isMuted() :
true);
640 return m_bufferLevel/100.0f;
645 return m_metaData.hasAudio();
650 return m_metaData.hasVideo();
655 return m_metaData.isSeekable();
661 return QMediaTimeRange(0, m_metaData.duration());
666 return ::speedToRate(m_speed);
674 const int speed = ::rateToSpeed(rate);
676 if (m_speed == speed)
681 if (isDeferredSpeedEnabled() && state() != QMediaPlayer::PlayingState) {
682 m_deferredSpeed = speed;
686 if (mmr_speed_set(m_context, speed) != 0)
687 emitMmError(
"mmr_speed_set failed");
708 stateChanged(QMediaPlayer::StoppedState);
709 mediaStatusChanged(QMediaPlayer::LoadingMedia);
713 updateMetaData(
nullptr);
719 if (!m_media.isValid() || !m_connection || !m_context || m_audioId == -1) {
720 stateChanged(QMediaPlayer::StoppedState);
724 if (state() == QMediaPlayer::PlayingState)
727 setDeferredSpeedEnabled(
false);
729 if (m_deferredSpeed) {
730 setPlaybackRate(::speedToRate(*m_deferredSpeed));
731 m_deferredSpeed = {};
733 setPlaybackRate(::speedToRate(m_configuredSpeed));
736 setDeferredSpeedEnabled(
true);
739 if (state() == QMediaPlayer::PausedState) {
744 if (mediaStatus() == QMediaPlayer::EndOfMedia)
745 setPositionInternal(0);
750 if (mmr_play(m_context) != 0) {
751 stateChanged(QMediaPlayer::StoppedState);
752 emitMmError(
"mmr_play() failed");
756 stateChanged(QMediaPlayer::PlayingState);
761 if (state() != QMediaPlayer::PlayingState)
770 || state() == QMediaPlayer::StoppedState
771 || mediaStatus() == QMediaPlayer::NoMedia)
775 setPositionInternal(0);
782 m_platformVideoSink = videoSink
783 ?
static_cast<QQnxVideoSink *>(videoSink->platformVideoSink())
789 m_eventThread =
new QQnxMediaEventThread(m_context);
791 connect(m_eventThread, &QQnxMediaEventThread::eventPending,
794 m_eventThread->setObjectName(QStringLiteral(
"MmrEventThread-") + QString::number(m_id));
795 m_eventThread->start();
800 delete m_eventThread;
801 m_eventThread =
nullptr;
813 m_position = newPosition;
815 if (state() == QMediaPlayer::PausedState)
818 positionChanged(m_position);
823 m_bufferLevel = capacity == 0 ? 0 : level /
static_cast<
float>(capacity) * 100.0f;
824 m_bufferLevel = qBound(0, m_bufferLevel, 100);
825 bufferProgressChanged(m_bufferLevel/100.0f);
830 m_metaData.update(dict);
832 durationChanged(m_metaData.duration());
833 audioAvailableChanged(m_metaData.hasAudio());
834 videoAvailableChanged(m_metaData.hasVideo());
835 seekableChanged(m_metaData.isSeekable());
840 emitMmError(QString::fromUtf8(msg));
845 int errorCode = MMR_ERROR_NONE;
846 const QString errorMessage = mmErrorMessage(msg, m_context, &errorCode);
847 Q_EMIT error(QMediaPlayer::ResourceError, errorMessage);
852 const QString errorMessage = QString::fromLatin1(
"%1: %2").arg(msg).arg(QString::fromUtf8(strerror(errno)));
853 Q_EMIT error(QMediaPlayer::ResourceError, errorMessage);
859 while (
const mmr_event_t *event = mmr_event_get(m_context)) {
860 if (event->type == MMR_EVENT_NONE)
863 switch (event->type) {
864 case MMR_EVENT_STATUS:
865 handleMmEventStatus(event);
867 case MMR_EVENT_STATE:
868 handleMmEventState(event);
870 case MMR_EVENT_METADATA:
871 updateMetaData(event->data);
873 case MMR_EVENT_ERROR:
874 handleMmEventError(event);
877 case MMR_EVENT_OVERFLOW:
878 case MMR_EVENT_WARNING:
879 case MMR_EVENT_PLAYLIST:
880 case MMR_EVENT_INPUT:
881 case MMR_EVENT_OUTPUT:
882 case MMR_EVENT_CTXTPAR:
883 case MMR_EVENT_TRKPAR:
884 case MMR_EVENT_OTHER:
895#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.