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(
'/');
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;
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(),
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(),
"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");
410 m_audioId = mmr_output_attach(m_context,
411 defaultAudioDevice.isEmpty() ?
"snd:" : defaultAudioDevice.constData(),
"audio");
413 if (m_audioId == -1) {
414 emitMmError(
"mmr_output_attach() for audio failed");
424 if (isInputAttached())
427 const QByteArray resourcePath = resourcePathForUrl(m_media);
429 if (resourcePath.isEmpty())
432 if (mmr_input_attach(m_context, resourcePath.constData(),
"track") != 0) {
433 emitMmError(QStringLiteral(
"mmr_input_attach() failed for ")
434 + QString::fromUtf8(resourcePath));
436 mediaStatusChanged(QMediaPlayer::InvalidMedia);
441 m_inputAttached =
true;
451 if (isVideoOutputAttached())
454 if (isAudioOutputAttached())
457 if (isInputAttached())
467 if (m_platformVideoSink)
468 m_platformVideoSink->setVideoFrame({});
470 if (isVideoOutputAttached())
471 mmr_output_detach(m_context, m_videoId);
478 if (isAudioOutputAttached())
479 mmr_output_detach(m_context, m_audioId);
486 if (isInputAttached())
487 mmr_input_detach(m_context);
489 m_inputAttached =
false;
494 return m_videoId != -1;
499 return m_audioId != -1;
504 return m_inputAttached;
509 if (!m_platformVideoSink)
512 QVideoFrameFormat format(size, QVideoFrameFormat::Format_BGRX8888);
514 const QVideoFrame actualFrame = m_windowGrabber->isEglImageSupported()
515 ? QVideoFramePrivate::createFrame(std::make_unique<QnxTextureBuffer>(m_windowGrabber),
517 : QVideoFramePrivate::createFrame(std::make_unique<QnxRasterBuffer>(m_windowGrabber),
520 m_platformVideoSink->setVideoFrame(actualFrame);
525 return m_metaData.duration();
535 if (m_position == position)
538 m_pendingPosition = position;
539 m_flushPositionTimer.start();
544 if (!m_context || !m_metaData.isSeekable() || mediaStatus() == QMediaPlayer::NoMedia)
547 if (mmr_seek(m_context, QString::number(position).toLatin1()) != 0)
548 emitMmError(
"Seeking failed");
553 setPositionInternal(m_pendingPosition);
558 return m_flushPositionTimer.isActive();
563 m_deferredSpeedEnabled = enabled;
568 return m_deferredSpeedEnabled;
573 const int normalizedVolume = ::normalizeVolume(volume);
575 if (m_volume == normalizedVolume)
578 m_volume = normalizedVolume;
586 if (m_muted == muted)
596 if (!m_context || m_audioId == -1)
599 const int volume = m_muted ? 0 : m_volume;
602 std::snprintf(buf,
sizeof buf,
"%d", volume);
605 dict = strm_dict_set(dict,
"volume", buf);
607 if (mmr_output_parameters(m_context, m_audioId, dict) != 0)
608 emitMmError(
"mmr_output_parameters: Setting volume failed");
613 QAudioOutput *out = output ? output->q :
nullptr;
614 if (m_audioOutput == out)
618 disconnect(m_audioOutput.get());
621 connect(out, &QAudioOutput::volumeChanged,
this, &QQnxMediaPlayer::setVolume);
622 connect(out, &QAudioOutput::mutedChanged,
this, &QQnxMediaPlayer::setMuted);
624 setVolume(out ? out->volume() : 1.);
625 setMuted(out ? out->isMuted() :
true);
632 return m_bufferLevel/100.0f;
637 return m_metaData.hasAudio();
642 return m_metaData.hasVideo();
647 return m_metaData.isSeekable();
653 return QMediaTimeRange(0, m_metaData.duration());
658 return ::speedToRate(m_speed);
666 const int speed = ::rateToSpeed(rate);
668 if (m_speed == speed)
673 if (isDeferredSpeedEnabled() && state() != QMediaPlayer::PlayingState) {
674 m_deferredSpeed = speed;
678 if (mmr_speed_set(m_context, speed) != 0)
679 emitMmError(
"mmr_speed_set failed");
700 stateChanged(QMediaPlayer::StoppedState);
701 mediaStatusChanged(QMediaPlayer::LoadingMedia);
705 updateMetaData(
nullptr);
711 if (!m_media.isValid() || !m_connection || !m_context || m_audioId == -1) {
712 stateChanged(QMediaPlayer::StoppedState);
716 if (state() == QMediaPlayer::PlayingState)
719 setDeferredSpeedEnabled(
false);
721 if (m_deferredSpeed) {
722 setPlaybackRate(::speedToRate(*m_deferredSpeed));
723 m_deferredSpeed = {};
725 setPlaybackRate(::speedToRate(m_configuredSpeed));
728 setDeferredSpeedEnabled(
true);
731 if (state() == QMediaPlayer::PausedState) {
736 if (mediaStatus() == QMediaPlayer::EndOfMedia)
737 setPositionInternal(0);
742 if (mmr_play(m_context) != 0) {
743 stateChanged(QMediaPlayer::StoppedState);
744 emitMmError(
"mmr_play() failed");
748 stateChanged(QMediaPlayer::PlayingState);
753 if (state() != QMediaPlayer::PlayingState)
762 || state() == QMediaPlayer::StoppedState
763 || mediaStatus() == QMediaPlayer::NoMedia)
767 setPositionInternal(0);
774 m_platformVideoSink = videoSink
775 ?
static_cast<QQnxVideoSink *>(videoSink->platformVideoSink())
781 m_eventThread =
new QQnxMediaEventThread(m_context);
783 connect(m_eventThread, &QQnxMediaEventThread::eventPending,
784 this, &QQnxMediaPlayer::readEvents);
786 m_eventThread->setObjectName(QStringLiteral(
"MmrEventThread-") + QString::number(m_id));
787 m_eventThread->start();
792 delete m_eventThread;
793 m_eventThread =
nullptr;
805 m_position = newPosition;
807 if (state() == QMediaPlayer::PausedState)
810 positionChanged(m_position);
815 m_bufferLevel = capacity == 0 ? 0 : level /
static_cast<
float>(capacity) * 100.0f;
816 m_bufferLevel = qBound(0, m_bufferLevel, 100);
817 bufferProgressChanged(m_bufferLevel/100.0f);
822 m_metaData.update(dict);
824 durationChanged(m_metaData.duration());
825 audioAvailableChanged(m_metaData.hasAudio());
826 videoAvailableChanged(m_metaData.hasVideo());
827 seekableChanged(m_metaData.isSeekable());
832 emitMmError(QString::fromUtf8(msg));
837 int errorCode = MMR_ERROR_NONE;
838 const QString errorMessage = mmErrorMessage(msg, m_context, &errorCode);
839 emit error(errorCode, errorMessage);
844 const QString errorMessage = QString::fromLatin1(
"%1: %2").arg(msg).arg(QString::fromUtf8(strerror(errno)));
845 emit error(errno, errorMessage);
851 while (
const mmr_event_t *event = mmr_event_get(m_context)) {
852 if (event->type == MMR_EVENT_NONE)
855 switch (event->type) {
856 case MMR_EVENT_STATUS:
857 handleMmEventStatus(event);
859 case MMR_EVENT_STATE:
860 handleMmEventState(event);
862 case MMR_EVENT_METADATA:
863 updateMetaData(event->data);
865 case MMR_EVENT_ERROR:
866 handleMmEventError(event);
869 case MMR_EVENT_OVERFLOW:
870 case MMR_EVENT_WARNING:
871 case MMR_EVENT_PLAYLIST:
872 case MMR_EVENT_INPUT:
873 case MMR_EVENT_OUTPUT:
874 case MMR_EVENT_CTXTPAR:
875 case MMR_EVENT_TRKPAR:
876 case MMR_EVENT_OTHER:
887#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) const 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.