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