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 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 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 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 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 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 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 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 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
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.