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
qwasmmediaplayer.cpp
Go to the documentation of this file.
1// Copyright (C) 2022 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
5#include <common/qwasmvideooutput_p.h>
6#include <common/qwasmaudiooutput_p.h>
7#include "qaudiooutput.h"
8
9#include <QtCore/qloggingcategory.h>
10#include <QUuid>
11#include <QtGlobal>
12#include <QMimeDatabase>
13#include <QFileInfo>
14
16
17Q_STATIC_LOGGING_CATEGORY(lcMediaPlayer, "qt.multimedia.wasm.mediaplayer");
18
19using namespace Qt::Literals;
20
21QWasmMediaPlayer::QWasmMediaPlayer(QMediaPlayer *parent)
22 : QPlatformMediaPlayer(parent),
23 m_videoOutput(new QWasmVideoOutput),
25{
26 qCDebug(lcMediaPlayer) << Q_FUNC_INFO << this;
27
28}
29
31{
32 delete m_videoOutput;
33}
34
35void QWasmMediaPlayer::initVideo()
36{
37 m_videoOutput->setVideoMode(QWasmVideoOutput::VideoDisplay);
38 QUuid videoElementId = QUuid::createUuid();
39 qCDebug(lcMediaPlayer) << Q_FUNC_INFO << "videoElementId"<< videoElementId << this;
40
41 m_videoOutput->createVideoElement(videoElementId.toString(QUuid::WithoutBraces).toStdString());
42 m_videoOutput->doElementCallbacks();
43 m_videoOutput->createOffscreenElement(QSize(1280, 720));
44 m_videoOutput->updateVideoElementGeometry(QRect(0, 0, 1280, 720)); // initial size 720p standard
45
46 connect(m_videoOutput, &QWasmVideoOutput::bufferingChanged, this,
47 &QWasmMediaPlayer::bufferingChanged);
48 connect(m_videoOutput, &QWasmVideoOutput::errorOccured, this,
49 [=](qint32 code, const QString &message ) {
50 error((QMediaPlayer::Error)code, message);
51 });
52
53 connect(m_videoOutput, &QWasmVideoOutput::stateChanged, this,
54 &QWasmMediaPlayer::mediaStateChanged);
55 connect(m_videoOutput, &QWasmVideoOutput::progressChanged, this,
56 &QWasmMediaPlayer::setPositionChanged);
57 connect(m_videoOutput, &QWasmVideoOutput::durationChanged, this,
58 &QWasmMediaPlayer::setDurationChanged);
59 connect(m_videoOutput, &QWasmVideoOutput::sizeChange, this,
60 &QWasmMediaPlayer::videoSizeChanged);
61 connect(m_videoOutput, &QWasmVideoOutput::readyChanged, this,
62 &QWasmMediaPlayer::videoOutputReady);
63 connect(m_videoOutput, &QWasmVideoOutput::statusChanged, this,
64 &QWasmMediaPlayer::onMediaStatusChanged);
65 connect(m_videoOutput, &QWasmVideoOutput::metaDataLoaded, this,
66 &QWasmMediaPlayer::videoMetaDataChanged);
67 connect(m_videoOutput, &QWasmVideoOutput::seekableChanged, this,
68 &QWasmMediaPlayer::seekableMediaChanged);
69
70 setVideoAvailable(true);
71}
72
73void QWasmMediaPlayer::initAudio()
74{
75 connect(m_audioOutput->q, &QAudioOutput::deviceChanged,
77 connect(m_audioOutput->q, &QAudioOutput::volumeChanged,
78 this, &QWasmMediaPlayer::volumeChanged);
79 connect(m_audioOutput->q, &QAudioOutput::mutedChanged,
80 this, &QWasmMediaPlayer::mutedChanged);
81 connect(m_audioOutput, &QWasmAudioOutput::bufferingChanged, this,
82 &QWasmMediaPlayer::bufferingChanged);
83
84 connect( m_audioOutput, &QWasmAudioOutput::errorOccured, this,
85 [=](qint32 code, const QString &message ) {
86 error((QMediaPlayer::Error)code, message);
87 });
88
89 connect(m_audioOutput, &QWasmAudioOutput::progressChanged, this,
90 &QWasmMediaPlayer::setPositionChanged);
91 connect(m_audioOutput, &QWasmAudioOutput::durationChanged, this,
92 &QWasmMediaPlayer::setDurationChanged);
93 connect(m_audioOutput, &QWasmAudioOutput::statusChanged, this,
94 &QWasmMediaPlayer::onMediaStatusChanged);
95 connect(m_audioOutput, &QWasmAudioOutput::stateChanged, this,
96 &QWasmMediaPlayer::mediaStateChanged);
97 setAudioAvailable(true);
98}
99
100qint64 QWasmMediaPlayer::duration() const
101{
102 return m_videoOutput->getDuration();
103}
104
106{
107 if (mediaStatus() == QMediaPlayer::EndOfMedia)
108 return duration();
109
110 if (m_videoAvailable)
111 return m_videoOutput->getCurrentPosition();
112
113 return 0;
114}
115
116void QWasmMediaPlayer::setPosition(qint64 position)
117{
118 if (!isSeekable())
119 return;
120
121 const int seekPosition = (position > INT_MAX) ? INT_MAX : position;
122
123 if (seekPosition == this->position())
124 return;
125
126 if (mediaStatus() == QMediaPlayer::EndOfMedia)
127 setMediaStatus(QMediaPlayer::LoadedMedia);
128
129 if (m_videoAvailable)
130 return m_videoOutput->seekTo(position);
131
132 emit positionChanged(seekPosition);
133}
134
135void QWasmMediaPlayer::volumeChanged(float gain)
136{
137 if (m_State != QWasmMediaPlayer::Started)
138 return;
139
140 if (m_videoAvailable)
141 m_videoOutput->setVolume(gain);
142}
143
144void QWasmMediaPlayer::mutedChanged(bool muted)
145{
146 if (m_State != QWasmMediaPlayer::Started)
147 return;
148
149 if (m_videoAvailable)
150 m_videoOutput->setMuted(muted);
151}
152
154{
155 return qBound(0.0, (m_bufferPercent * .01), 1.0);
156}
157
159{
160 return m_audioAvailable;
161}
162
164{
165 return m_videoAvailable;
166}
167
169{
170 return m_availablePlaybackRange;
171}
172
173void QWasmMediaPlayer::updateAvailablePlaybackRanges()
174{
175 if (m_buffering) {
176 const qint64 pos = position();
177 const qint64 end = (duration() / 100) * m_bufferPercent;
178 m_availablePlaybackRange.addInterval(pos, end);
179 } else if (isSeekable()) {
180 m_availablePlaybackRange = QMediaTimeRange(0, duration());
181 } else {
182 m_availablePlaybackRange = QMediaTimeRange();
183 }
184}
185
187{
188 if (m_State != QWasmMediaPlayer::Started)
189 return 0;
190
191 if (isVideoAvailable())
192 return m_videoOutput->playbackRate();
193 return 0;
194}
195
197{
199 return;
200
201 m_videoOutput->setPlaybackRate(rate);
202 emit playbackRateChanged(rate);
203}
204
206{
207 return m_mediaContent;
208}
209
211{
212 return m_mediaStream;
213}
214
215void QWasmMediaPlayer::setMedia(const QUrl &mediaContent, QIODevice *stream)
216{
217 qCDebug(lcMediaPlayer) << Q_FUNC_INFO << mediaContent << stream;
218 QMimeDatabase db;
219
220 if (mediaContent.isEmpty()) {
221 if (stream) {
222 m_mediaStream = stream;
223 qCDebug(lcMediaPlayer) << db.mimeTypeForData(stream).name();
224 if (db.mimeTypeForData(stream).name().contains(u"audio"_s)) {
225 setAudioAvailable(true);
226 m_audioOutput->setSource(m_mediaStream);
227 } else { // treat octet-stream as video
228 setVideoAvailable(true);
229 m_videoOutput->setSource(m_mediaStream);
230 }
231 } else {
232
233 setMediaStatus(QMediaPlayer::NoMedia);
234 }
235 } else {
236 if (mediaContent.isLocalFile()) {
237 QString sourceFile = mediaContent.toLocalFile();
238 if (db.mimeTypeForFile(QFileInfo(sourceFile)).name().contains(u"audio"_s)) {
239 setAudioAvailable(true);
240 m_audioOutput->setSource(mediaContent);
241 } else { // treat octet-stream as video
242 setVideoAvailable(true);
243 m_videoOutput->setSource(mediaContent);
244 }
245 } else {
246 // web url
247 if (!m_videoOutput->currentVideoElement().isNull()) {
248 m_videoOutput->setSource(mediaContent);
249 setVideoAvailable(true);
250 } else {
251 m_audioOutput->setSource(mediaContent);
252 setAudioAvailable(true);
253 }
254 }
255 }
256
257 resetBufferingProgress();
258}
259
260void QWasmMediaPlayer::setVideoSink(QVideoSink *sink)
261{
262 if (m_videoSink == sink)
263 return;
264
265 m_videoSink = sink;
266
267 if (!m_videoSink)
268 return;
269
270 initVideo();
271 m_videoOutput->setSurface(sink);
272 setVideoAvailable(true);
273 if (isAudioAvailable() && m_audioOutput)
274 m_audioOutput->setVideoElement(m_videoOutput->currentVideoElement());
275}
276
277void QWasmMediaPlayer::setAudioOutput(QPlatformAudioOutput *output)
278{
279 if (m_audioOutput == output)
280 return;
281
282 if (m_audioOutput)
283 m_audioOutput->q->disconnect(this);
284 m_audioOutput = static_cast<QWasmAudioOutput *>(output);
285 initAudio();
286 setAudioAvailable(true);
287}
288
290{
291 if (m_audioOutput) {
292 m_audioOutput->setAudioDevice(m_audioOutput->q->device());
293 }
294}
295
297{
298 resetCurrentLoop();
299
300 if (isVideoAvailable()) {
301 m_videoOutput->start();
302 m_playWhenReady = true;
303 } else {
304 if (isAudioAvailable()) {
305 m_audioOutput->start();
306 }
307 }
308
309#ifdef DEBUG_AUDIOENGINE
310 QAudioEnginePrivate::checkNoError("play");
311#endif
312}
313
315{
316 if ((m_State
319 return;
320 }
321 if (isVideoAvailable()) {
322 m_videoOutput->pause();
323 } else {
324 m_audioOutput->pause();
325 stateChanged(QMediaPlayer::PausedState);
326 }
327}
328
330{
331 m_playWhenReady = false;
332
334 || m_State == QWasmMediaPlayer::Stopped) {
335 qWarning() << Q_FUNC_INFO << __LINE__;
336 return;
337 }
338
339 if (isVideoAvailable()) {
340 m_videoOutput->stop();
341 } else {
342 m_audioOutput->stop();
343 }
344
345}
346
348{
349 return isVideoAvailable() && m_videoOutput->isVideoSeekable();
350}
351
352void QWasmMediaPlayer::errorOccured(qint32 code, const QString &message)
353{
354 QString errorString;
355 QMediaPlayer::Error error = QMediaPlayer::ResourceError;
356
357 switch (code) {
358 case QWasmMediaNetworkState::NetworkEmpty: // no data
359 break;
360 case QWasmMediaNetworkState::NetworkIdle:
361 break;
362 case QWasmMediaNetworkState::NetworkLoading:
363 break;
364 case QWasmMediaNetworkState::NetworkNoSource: // no source
365 error = QMediaPlayer::ResourceError;
366 errorString = message;
367 break;
368 };
369
370 emit QPlatformMediaPlayer::error(error, errorString);
371}
372
373void QWasmMediaPlayer::bufferingChanged(qint32 percent)
374{
375 m_buffering = percent != 100;
376 m_bufferPercent = percent;
377
378 updateAvailablePlaybackRanges();
379 emit bufferProgressChanged(bufferProgress());
380}
381
382void QWasmMediaPlayer::videoSizeChanged(qint32 width, qint32 height)
383{
384 QSize newSize(width, height);
385
386 if (width == 0 || height == 0 || newSize == m_videoSize)
387 return;
388
389 m_videoSize = newSize;
390}
391
392void QWasmMediaPlayer::mediaStateChanged(QWasmMediaPlayer::QWasmMediaPlayerState state)
393{
394 m_State = state;
395 QMediaPlayer::PlaybackState m_mediaPlayerState;
396 switch (m_State) {
397 case QWasmMediaPlayer::Started:
398 m_mediaPlayerState = QMediaPlayer::PlayingState;
399 break;
400 case QWasmMediaPlayer::Paused:
401 m_mediaPlayerState = QMediaPlayer::PausedState;
402 break;
403 case QWasmMediaPlayer::Stopped:
404 m_mediaPlayerState = QMediaPlayer::StoppedState;
405 break;
406 default:
407 m_mediaPlayerState = QMediaPlayer::StoppedState;
408 break;
409 };
410
411 QPlatformMediaPlayer::stateChanged(m_mediaPlayerState);
412}
413
414int QWasmMediaPlayer::trackCount(TrackType trackType)
415{
416 Q_UNUSED(trackType)
417 // TODO QTBUG-108517
418 return 0; // tracks.count();
419}
420
421void QWasmMediaPlayer::setPositionChanged(qint64 position)
422{
423 QPlatformMediaPlayer::positionChanged(position);
424}
425
426void QWasmMediaPlayer::setDurationChanged(qint64 duration)
427{
428 QPlatformMediaPlayer::durationChanged(duration);
429}
430
431void QWasmMediaPlayer::videoOutputReady(bool ready)
432{
433 setVideoAvailable(ready);
434
435 if (m_playWhenReady && m_videoOutput->isReady())
436 play();
437}
438
439void QWasmMediaPlayer::setMediaStatus(QMediaPlayer::MediaStatus status)
440{
441 mediaStatusChanged(status);
442
443 switch (status) {
444 case QMediaPlayer::NoMedia:
445 case QMediaPlayer::InvalidMedia:
446 emit durationChanged(0);
447 break;
448 case QMediaPlayer::EndOfMedia:
449 setPositionChanged(position());
450 default:
451 break;
452 };
453}
454
455void QWasmMediaPlayer::setAudioAvailable(bool available)
456{
457 if (m_audioAvailable == available)
458 return;
459
460 m_audioAvailable = available;
461 emit audioAvailableChanged(m_audioAvailable);
462}
463
464void QWasmMediaPlayer::setVideoAvailable(bool available)
465{
466 if (m_videoAvailable == available)
467 return;
468
469 if (!available)
470 m_videoSize = QSize();
471
472 m_videoAvailable = available;
473 emit videoAvailableChanged(m_videoAvailable);
474}
475
476void QWasmMediaPlayer::resetBufferingProgress()
477{
478 m_buffering = false;
479 m_bufferPercent = 0;
480 m_availablePlaybackRange = QMediaTimeRange();
481}
482
483void QWasmMediaPlayer::onMediaStatusChanged(QMediaPlayer::MediaStatus status)
484{
485 setMediaStatus(status);
486}
487
488void QWasmMediaPlayer::videoMetaDataChanged()
489{
490 metaDataChanged();
491}
492
493void QWasmMediaPlayer::seekableMediaChanged(bool seekable)
494{
495 seekableChanged(seekable);
496}
497
498QT_END_NAMESPACE
499
500#include "moc_qwasmmediaplayer_p.cpp"
\inmodule QtCore
Definition qsize.h:27
void setVideoSink(QVideoSink *surface) override
void pause() override
qint64 position() const override
void play() override
const QIODevice * mediaStream() const override
QMediaTimeRange availablePlaybackRanges() const override
qreal playbackRate() const override
float bufferProgress() const override
bool isVideoAvailable() const override
~QWasmMediaPlayer() override
void setMedia(const QUrl &mediaContent, QIODevice *stream) override
void setPosition(qint64 position) override
void setPlaybackRate(qreal rate) override
bool isSeekable() const override
bool isAudioAvailable() const override
int trackCount(TrackType trackType) override
void stop() override
QUrl media() const override
void seekableChanged(bool seekable)
void stateChanged(QWasmMediaPlayer::QWasmMediaPlayerState newState)
Combined button and popup list for selecting options.