Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qqnxmediaplayer.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 Research In Motion
2// Copyright (C) 2021 The Qt Company
3// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
6#include "common/qqnxmediaeventthread_p.h"
7#include "common/qqnxwindowgrabber_p.h"
8
9#include <private/qhwvideobuffer_p.h>
10#include <QtMultimedia/private/qplatformvideosink_p.h>
11#include <private/qvideoframe_p.h>
12
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 <common/qqnxaudiooutput_p.h>
21#include <qaudiooutput.h>
22
23#include <errno.h>
24#include <sys/strm.h>
25#include <sys/stat.h>
26
27#include <algorithm>
28#include <tuple>
29
30static constexpr int rateToSpeed(qreal rate)
31{
32 return std::floor(rate * 1000);
33}
34
35static constexpr qreal speedToRate(int speed)
36{
37 return std::floor(speed / 1000.0);
38}
39
40static constexpr int normalizeVolume(float volume)
41{
42 return std::clamp<int>(std::floor(volume * 100.0), 0, 100);
43}
44
45static std::tuple<int, int, bool> parseBufferLevel(const QString &value)
46{
47 if (value.isEmpty())
48 return {};
49
50 const int slashPos = value.indexOf(QChar(u'/'));
51 if (slashPos <= 0)
52 return {};
53
54 bool ok = false;
55 const int level = value.left(slashPos).toInt(&ok);
56 if (!ok || level < 0)
57 return {};
58
59 const int capacity = value.mid(slashPos + 1).toInt(&ok);
60 if (!ok || capacity < 0)
61 return {};
62
63 return { level, capacity, true };
64}
65
67{
68public:
70 {
71 m_windowGrabber = QQnxWindowGrabber;
72 m_handle = 0;
73 }
74
75 void unmap() override {}
76
77 MapData map(QVideoFrame::MapMode /*mode*/) override
78 {
79 return {};
80 }
81
82 quint64 textureHandle(QRhi &, int plane) override
83 {
84 if (plane != 0)
85 return 0;
86 if (!m_handle)
87 m_handle = m_windowGrabber->getNextTextureId();
88
89 return m_handle;
90 }
91
92private:
93 QQnxWindowGrabber *m_windowGrabber;
94 quint64 m_handle;
95};
96
98{
99public:
100 QnxRasterBuffer(QQnxWindowGrabber *windowGrabber) { m_windowGrabber = windowGrabber; }
101
102 MapData map(QVideoFrame::MapMode mode) override
103 {
104 if (mode != QVideoFrame::ReadOnly)
105 return {};
106
107 if (buffer.data) {
108 qWarning("QnxRasterBuffer: need to unmap before mapping");
109 return {};
110 }
111
112 buffer = m_windowGrabber->getNextBuffer();
113
114 return {
115 .planeCount = 1,
116 .bytesPerLine = { buffer.stride },
117 .data = { buffer.data },
118 .dataSize = { buffer.width * buffer.height * buffer.pixelSize }
119 };
120 }
121
123 {
124 buffer = {};
125 }
126
127 QVideoFrameFormat format() const override { return {}; }
128
129private:
130 QQnxWindowGrabber *m_windowGrabber;
131 QQnxWindowGrabber::BufferView buffer;
132};
133
134QT_BEGIN_NAMESPACE
135
136QQnxMediaPlayer::QQnxMediaPlayer(QMediaPlayer *parent)
137 : QObject(parent)
138 , QPlatformMediaPlayer(parent)
139 , m_windowGrabber(new QQnxWindowGrabber(this))
140{
141 m_flushPositionTimer.setSingleShot(true);
142 m_flushPositionTimer.setInterval(100);
143
144 connect(&m_flushPositionTimer, &QTimer::timeout, this, &QQnxMediaPlayer::flushPosition);
145
146 connect(m_windowGrabber, &QQnxWindowGrabber::updateScene, this, &QQnxMediaPlayer::updateScene);
147
148 openConnection();
149}
150
152{
153 stop();
154 detach();
155 closeConnection();
156}
157
158void QQnxMediaPlayer::openConnection()
159{
160 static int idCounter = 0;
161
162 m_connection = mmr_connect(nullptr);
163 if (!m_connection) {
164 emitPError(QString::fromLatin1("Unable to connect to the multimedia renderer"));
165 return;
166 }
167
168 m_id = idCounter++;
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);
173 if (!m_context) {
174 emitPError(QString::fromLatin1("Unable to create context"));
175 closeConnection();
176 return;
177 }
178
179 startMonitoring();
180}
181
182void QQnxMediaPlayer::handleMmEventState(const mmr_event_t *event)
183{
184 if (!event || event->type != MMR_EVENT_STATE)
185 return;
186
187 switch (event->state) {
188 case MMR_STATE_DESTROYED:
189 break;
190 case MMR_STATE_IDLE:
191 mediaStatusChanged(QMediaPlayer::NoMedia);
192 stateChanged(QMediaPlayer::StoppedState);
193 detachVideoOutput();
194 detachInput();
195 break;
196 case MMR_STATE_STOPPED:
197 stateChanged(QMediaPlayer::StoppedState);
198 m_windowGrabber->stop();
199
200 if (m_platformVideoSink)
201 m_platformVideoSink->setVideoFrame({});
202 break;
203 case MMR_STATE_PLAYING:
204 if (event->speed == 0) {
205 stateChanged(QMediaPlayer::PausedState);
206 m_windowGrabber->pause();
207 } else if (state() == QMediaPlayer::PausedState) {
208 m_windowGrabber->resume();
209 stateChanged(QMediaPlayer::PlayingState);
210 } else {
211 m_windowGrabber->start();
212 stateChanged(QMediaPlayer::PlayingState);
213 }
214
215 if (event->speed != m_speed) {
216 m_speed = event->speed;
217
218 if (state() != QMediaPlayer::PausedState)
219 m_configuredSpeed = m_speed;
220
221 playbackRateChanged(::speedToRate(m_speed));
222 }
223 break;
224 }
225}
226
227void QQnxMediaPlayer::handleMmEventStatus(const mmr_event_t *event)
228{
229 if (!event || event->type != MMR_EVENT_STATUS)
230 return;
231
232 if (event->data)
233 handleMmEventStatusData(event->data);
234
235 // update pos
236 if (!event->pos_str || isPendingPositionFlush())
237 return;
238
239 const QByteArray valueBa(event->pos_str);
240
241 bool ok;
242 const qint64 position = valueBa.toLongLong(&ok);
243
244 if (!ok)
245 qCritical("Could not parse position from '%s'", valueBa.constData());
246 else
247 handleMmPositionChanged(position);
248}
249
250void QQnxMediaPlayer::handleMmEventStatusData(const strm_dict_t *data)
251{
252 if (!data)
253 return;
254
255 const auto getValue = [data](const char *key) -> QString {
256 const strm_string_t *value = strm_dict_find_rstr(data, key);
257
258 if (!value)
259 return {};
260
261 return QString::fromUtf8(strm_string_get(value));
262 };
263
264 // update bufferProgress
265 const QString bufferLevel = getValue("bufferlevel");
266
267 if (!bufferLevel.isEmpty()) {
268 const auto & [level, capacity, ok] = ::parseBufferLevel(bufferLevel);
269
270 if (ok)
271 updateBufferLevel(level, capacity);
272 else
273 qCritical("Could not parse buffer capacity from '%s'", qUtf8Printable(bufferLevel));
274 }
275
276 // update MediaStatus
277 const QString bufferStatus = getValue("bufferstatus");
278 const QString suspended = getValue("suspended");
279
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);
286}
287
288void QQnxMediaPlayer::handleMmEventError(const mmr_event_t *event)
289{
290 if (!event)
291 return;
292
293 // When playback is explicitly stopped using mmr_stop(), mm-renderer
294 // generates a STATE event. When the end of media is reached, an ERROR
295 // event is generated and the error code contained in the event information
296 // is set to MMR_ERROR_NONE. When an error causes playback to stop,
297 // the error code is set to something else.
298 if (event->details.error.info.error_code == MMR_ERROR_NONE) {
299 mediaStatusChanged(QMediaPlayer::EndOfMedia);
300 stateChanged(QMediaPlayer::StoppedState);
301 }
302}
303
304void QQnxMediaPlayer::closeConnection()
305{
306 stopMonitoring();
307
308 if (m_context) {
309 mmr_context_destroy(m_context);
310 m_context = nullptr;
311 m_contextName.clear();
312 }
313
314 if (m_connection) {
315 mmr_disconnect(m_connection);
316 m_connection = nullptr;
317 }
318}
319
320QByteArray QQnxMediaPlayer::resourcePathForUrl(const QUrl &url)
321{
322 // If this is a local file, mmrenderer expects the file:// prefix and an absolute path.
323 // We treat URLs without scheme as local files, most likely someone just forgot to set the
324 // file:// prefix when constructing the URL.
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());
329
330 // HTTP or similar URL
331 } else {
332 return url.toEncoded();
333 }
334}
335
336void QQnxMediaPlayer::attach()
337{
338 // Should only be called in detached state
339 if (isInputAttached())
340 return;
341
342 if (!m_media.isValid() || !m_context) {
343 mediaStatusChanged(QMediaPlayer::NoMedia);
344 return;
345 }
346
347 resetMonitoring();
348
349 if (!(attachVideoOutput() && attachAudioOutput() && attachInput())) {
350 detach();
351 return;
352 }
353
354 mediaStatusChanged(QMediaPlayer::LoadedMedia);
355}
356
357bool QQnxMediaPlayer::attachVideoOutput()
358{
359 if (isVideoOutputAttached()) {
360 qWarning() << "QQnxMediaPlayer: Video output already attached!";
361 return true;
362 }
363
364 if (!m_context) {
365 qWarning() << "QQnxMediaPlayer: No media player context!";
366 return false;
367 }
368
369 const QByteArray windowGroupId = m_windowGrabber->windowGroupId();
370 if (windowGroupId.isEmpty()) {
371 qWarning() << "QQnxMediaPlayer: Unable to find window group";
372 return false;
373 }
374
375 static int winIdCounter = 0;
376
377 const QString windowName = QStringLiteral("QQnxVideoSink_%1_%2")
378 .arg(winIdCounter++)
379 .arg(QCoreApplication::applicationPid());
380
381 m_windowGrabber->setWindowId(windowName.toLatin1());
382
383 if (m_platformVideoSink)
384 m_platformVideoSink->withRhi([&](QRhi *rhi) {
385 m_windowGrabber->setRhi(rhi);
386 });
387
388 // Start with an invisible window, because we just want to grab the frames from it.
389 const QString videoDeviceUrl = QStringLiteral("screen:?winid=%1&wingrp=%2&initflags=invisible&nodstviewport=1")
390 .arg(windowName, QString::fromLatin1(windowGroupId));
391
392 m_videoId = mmr_output_attach(m_context, videoDeviceUrl.toLatin1().constData(), "video");
393
394 if (m_videoId == -1) {
395 qWarning() << "mmr_output_attach() for video failed";
396 return false;
397 }
398
399 return true;
400}
401
402bool QQnxMediaPlayer::attachAudioOutput()
403{
404 if (isAudioOutputAttached()) {
405 qWarning() << "QQnxMediaPlayer: Audio output already attached!";
406 return true;
407 }
408
409 const QByteArray defaultAudioDevice = qgetenv("QQNX_RENDERER_DEFAULT_AUDIO_SINK");
410
411 // QNX 8.0+ serves audio through io-snd (ALSA/SALSA), reached as
412 // "alsa:default"; older releases use the QSA "snd:" device.
413 const char *fallbackDevice =
414#if __QNX__ >= 800
415 "alsa:default";
416#else
417 "snd:";
418#endif
419
420 m_audioId = mmr_output_attach(m_context,
421 defaultAudioDevice.isEmpty() ? fallbackDevice : defaultAudioDevice.constData(), "audio");
422
423 if (m_audioId == -1) {
424 emitMmError("mmr_output_attach() for audio failed");
425
426 return false;
427 }
428
429 return true;
430}
431
432bool QQnxMediaPlayer::attachInput()
433{
434 if (isInputAttached())
435 return true;
436
437 const QByteArray resourcePath = resourcePathForUrl(m_media);
438
439 if (resourcePath.isEmpty())
440 return false;
441
442 if (mmr_input_attach(m_context, resourcePath.constData(), "track") != 0) {
443 emitMmError(QStringLiteral("mmr_input_attach() failed for ")
444 + QString::fromUtf8(resourcePath));
445
446 mediaStatusChanged(QMediaPlayer::InvalidMedia);
447
448 return false;
449 }
450
451 m_inputAttached = true;
452
453 return true;
454}
455
456void QQnxMediaPlayer::detach()
457{
458 if (!m_context)
459 return;
460
461 if (isVideoOutputAttached())
462 detachVideoOutput();
463
464 if (isAudioOutputAttached())
465 detachAudioOutput();
466
467 if (isInputAttached())
468 detachInput();
469
470 resetMonitoring();
471}
472
473void QQnxMediaPlayer::detachVideoOutput()
474{
475 m_windowGrabber->stop();
476
477 if (m_platformVideoSink)
478 m_platformVideoSink->setVideoFrame({});
479
480 if (isVideoOutputAttached())
481 mmr_output_detach(m_context, m_videoId);
482
483 m_videoId = -1;
484}
485
486void QQnxMediaPlayer::detachAudioOutput()
487{
488 if (isAudioOutputAttached())
489 mmr_output_detach(m_context, m_audioId);
490
491 m_audioId = -1;
492}
493
494void QQnxMediaPlayer::detachInput()
495{
496 if (isInputAttached())
497 mmr_input_detach(m_context);
498
499 m_inputAttached = false;
500}
501
502bool QQnxMediaPlayer::isVideoOutputAttached() const
503{
504 return m_videoId != -1;
505}
506
507bool QQnxMediaPlayer::isAudioOutputAttached() const
508{
509 return m_audioId != -1;
510}
511
512bool QQnxMediaPlayer::isInputAttached() const
513{
514 return m_inputAttached;
515}
516
517void QQnxMediaPlayer::updateScene(const QSize &size)
518{
519 if (!m_platformVideoSink)
520 return;
521
522 QVideoFrameFormat format(size, QVideoFrameFormat::Format_BGRX8888);
523
524 const QVideoFrame actualFrame = m_windowGrabber->isEglImageSupported()
525 ? QVideoFramePrivate::createFrame(std::make_unique<QnxTextureBuffer>(m_windowGrabber),
526 std::move(format))
527 : QVideoFramePrivate::createFrame(std::make_unique<QnxRasterBuffer>(m_windowGrabber),
528 std::move(format));
529
530 m_platformVideoSink->setVideoFrame(actualFrame);
531}
532
534{
535 return m_metaData.duration();
536}
537
539{
540 return m_position;
541}
542
543void QQnxMediaPlayer::setPosition(qint64 position)
544{
545 if (m_position == position)
546 return;
547
548 m_pendingPosition = position;
549 m_flushPositionTimer.start();
550}
551
552void QQnxMediaPlayer::setPositionInternal(qint64 position)
553{
554 if (!m_context || !m_metaData.isSeekable() || mediaStatus() == QMediaPlayer::NoMedia)
555 return;
556
557 if (mmr_seek(m_context, QString::number(position).toLatin1().constData()) != 0)
558 emitMmError("Seeking failed");
559}
560
561void QQnxMediaPlayer::flushPosition()
562{
563 setPositionInternal(m_pendingPosition);
564}
565
566bool QQnxMediaPlayer::isPendingPositionFlush() const
567{
568 return m_flushPositionTimer.isActive();
569}
570
571void QQnxMediaPlayer::setDeferredSpeedEnabled(bool enabled)
572{
573 m_deferredSpeedEnabled = enabled;
574}
575
576bool QQnxMediaPlayer::isDeferredSpeedEnabled() const
577{
578 return m_deferredSpeedEnabled;
579}
580
581void QQnxMediaPlayer::setVolume(float volume)
582{
583 const int normalizedVolume = ::normalizeVolume(volume);
584
585 if (m_volume == normalizedVolume)
586 return;
587
588 m_volume = normalizedVolume;
589
590 if (!m_muted)
591 updateVolume();
592}
593
594void QQnxMediaPlayer::setMuted(bool muted)
595{
596 if (m_muted == muted)
597 return;
598
599 m_muted = muted;
600
601 updateVolume();
602}
603
604void QQnxMediaPlayer::updateVolume()
605{
606 if (!m_context || m_audioId == -1)
607 return;
608
609 const int volume = m_muted ? 0 : m_volume;
610
611 char buf[] = "100";
612 std::snprintf(buf, sizeof buf, "%d", volume);
613
614 strm_dict_t * dict = strm_dict_new();
615 dict = strm_dict_set(dict, "volume", buf);
616
617 if (mmr_output_parameters(m_context, m_audioId, dict) != 0)
618 emitMmError("mmr_output_parameters: Setting volume failed");
619}
620
621void QQnxMediaPlayer::setAudioOutput(QPlatformAudioOutput *output)
622{
623 QAudioOutput *out = output ? output->q : nullptr;
624 if (m_audioOutput == out)
625 return;
626
627 if (m_audioOutput)
628 disconnect(m_audioOutput.get());
629 m_audioOutput = out;
630 if (m_audioOutput) {
631 connect(out, &QAudioOutput::volumeChanged, this, &QQnxMediaPlayer::setVolume);
632 connect(out, &QAudioOutput::mutedChanged, this, &QQnxMediaPlayer::setMuted);
633 }
634 setVolume(out ? out->volume() : 1.);
635 setMuted(out ? out->isMuted() : true);
636}
637
639{
640 // mm-renderer has buffer properties "status" and "level"
641 // QMediaPlayer's buffer status maps to mm-renderer's buffer level
642 return m_bufferLevel/100.0f;
643}
644
646{
647 return m_metaData.hasAudio();
648}
649
651{
652 return m_metaData.hasVideo();
653}
654
656{
657 return m_metaData.isSeekable();
658}
659
661{
662 // We can't get this information from the mmrenderer API yet, so pretend we can seek everywhere
663 return QMediaTimeRange(0, m_metaData.duration());
664}
665
667{
668 return ::speedToRate(m_speed);
669}
670
672{
673 if (!m_context)
674 return;
675
676 const int speed = ::rateToSpeed(rate);
677
678 if (m_speed == speed)
679 return;
680
681 // defer setting the playback speed for when play() is called to prevent
682 // mm-renderer from inadvertently transitioning into play state
683 if (isDeferredSpeedEnabled() && state() != QMediaPlayer::PlayingState) {
684 m_deferredSpeed = speed;
685 return;
686 }
687
688 if (mmr_speed_set(m_context, speed) != 0)
689 emitMmError("mmr_speed_set failed");
690}
691
693{
694 return m_media;
695}
696
698{
699 // Always 0, we don't support QIODevice streams
700 return 0;
701}
702
703void QQnxMediaPlayer::setMedia(const QUrl &media, QIODevice *stream)
704{
705 Q_UNUSED(stream); // not supported
706
707 stop();
708 detach();
709
710 stateChanged(QMediaPlayer::StoppedState);
711 mediaStatusChanged(QMediaPlayer::LoadingMedia);
712
713 m_media = media;
714
715 updateMetaData(nullptr);
716 attach();
717}
718
720{
721 if (!m_media.isValid() || !m_connection || !m_context || m_audioId == -1) {
722 stateChanged(QMediaPlayer::StoppedState);
723 return;
724 }
725
726 if (state() == QMediaPlayer::PlayingState)
727 return;
728
729 setDeferredSpeedEnabled(false);
730
731 if (m_deferredSpeed) {
732 setPlaybackRate(::speedToRate(*m_deferredSpeed));
733 m_deferredSpeed = {};
734 } else {
735 setPlaybackRate(::speedToRate(m_configuredSpeed));
736 }
737
738 setDeferredSpeedEnabled(true);
739
740 // Un-pause the state when it is paused
741 if (state() == QMediaPlayer::PausedState) {
742 return;
743 }
744
745
746 if (mediaStatus() == QMediaPlayer::EndOfMedia)
747 setPositionInternal(0);
748
749 resetMonitoring();
750 updateVolume();
751
752 if (mmr_play(m_context) != 0) {
753 stateChanged(QMediaPlayer::StoppedState);
754 emitMmError("mmr_play() failed");
755 return;
756 }
757
758 stateChanged(QMediaPlayer::PlayingState);
759}
760
762{
763 if (state() != QMediaPlayer::PlayingState)
764 return;
765
766 setPlaybackRate(0);
767}
768
770{
771 if (!m_context
772 || state() == QMediaPlayer::StoppedState
773 || mediaStatus() == QMediaPlayer::NoMedia)
774 return;
775
776 // mm-renderer does not rewind by default
777 setPositionInternal(0);
778
779 mmr_stop(m_context);
780}
781
782void QQnxMediaPlayer::setVideoSink(QVideoSink *videoSink)
783{
784 m_platformVideoSink =
785 videoSink ? static_cast<QRhiVideoSink *>(videoSink->platformVideoSink()) : nullptr;
786}
787
788void QQnxMediaPlayer::startMonitoring()
789{
790 m_eventThread = new QQnxMediaEventThread(m_context);
791
792 connect(m_eventThread, &QQnxMediaEventThread::eventPending,
793 this, &QQnxMediaPlayer::readEvents);
794
795 m_eventThread->setObjectName(QStringLiteral("MmrEventThread-") + QString::number(m_id));
796 m_eventThread->start();
797}
798
799void QQnxMediaPlayer::stopMonitoring()
800{
801 delete m_eventThread;
802 m_eventThread = nullptr;
803}
804
805void QQnxMediaPlayer::resetMonitoring()
806{
807 m_bufferLevel = 0;
808 m_position = 0;
809 m_speed = 0;
810}
811
812void QQnxMediaPlayer::handleMmPositionChanged(qint64 newPosition)
813{
814 m_position = newPosition;
815
816 if (state() == QMediaPlayer::PausedState)
817 m_windowGrabber->forceUpdate();
818
819 positionChanged(m_position);
820}
821
822void QQnxMediaPlayer::updateBufferLevel(int level, int capacity)
823{
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);
827}
828
829void QQnxMediaPlayer::updateMetaData(const strm_dict *dict)
830{
831 m_metaData.update(dict);
832
833 durationChanged(m_metaData.duration());
834 audioAvailableChanged(m_metaData.hasAudio());
835 videoAvailableChanged(m_metaData.hasVideo());
836 seekableChanged(m_metaData.isSeekable());
837}
838
839void QQnxMediaPlayer::emitMmError(const char *msg)
840{
841 emitMmError(QString::fromUtf8(msg));
842}
843
844void QQnxMediaPlayer::emitMmError(const QString &msg)
845{
846 int errorCode = MMR_ERROR_NONE;
847 const QString errorMessage = mmErrorMessage(msg, m_context, &errorCode);
848 Q_EMIT error(QMediaPlayer::ResourceError, errorMessage);
849}
850
851void QQnxMediaPlayer::emitPError(const QString &msg)
852{
853 const QString errorMessage = QString::fromLatin1("%1: %2").arg(msg).arg(QString::fromUtf8(strerror(errno)));
854 Q_EMIT error(QMediaPlayer::ResourceError, errorMessage);
855}
856
857
858void QQnxMediaPlayer::readEvents()
859{
860 while (const mmr_event_t *event = mmr_event_get(m_context)) {
861 if (event->type == MMR_EVENT_NONE)
862 break;
863
864 switch (event->type) {
865 case MMR_EVENT_STATUS:
866 handleMmEventStatus(event);
867 break;
868 case MMR_EVENT_STATE:
869 handleMmEventState(event);
870 break;
871 case MMR_EVENT_METADATA:
872 updateMetaData(event->data);
873 break;
874 case MMR_EVENT_ERROR:
875 handleMmEventError(event);
876 break;
877 case MMR_EVENT_NONE:
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:
886 break;
887 }
888 }
889
890 if (m_eventThread)
891 m_eventThread->signalRead();
892}
893
894QT_END_NAMESPACE
895
896#include "moc_qqnxmediaplayer_p.cpp"
QObject * parent
Definition qobject.h:74
void pause() override
const QIODevice * mediaStream() const override
bool isSeekable() const override
void play() override
void setPlaybackRate(qreal rate) override
qint64 position() const override
QMediaTimeRange availablePlaybackRanges() const override
bool isAudioAvailable() const override
float bufferProgress() const override
void setPosition(qint64 position) override
qint64 duration() const override
void stop() override
qreal playbackRate() const override
QUrl media() const override
bool isVideoAvailable() const override
void setMedia(const QUrl &media, QIODevice *stream) override
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.
struct strm_dict strm_dict_t
static constexpr int normalizeVolume(float volume)
static std::tuple< int, int, bool > parseBufferLevel(const QString &value)
static constexpr qreal speedToRate(int speed)
static constexpr int rateToSpeed(qreal rate)