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