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 qWarning() << Q_FUNC_INFO << __LINE__;
339 return;
340 }
341
342 if (isVideoAvailable()) {
343 m_videoOutput->stop();
344 } else {
345 m_audioOutput->stop();
346 }
347
348}
349
351{
352 return isVideoAvailable() && m_videoOutput->isVideoSeekable();
353}
354
355void QWasmMediaPlayer::errorOccured(qint32 code, const QString &message)
356{
357 QString errorString;
358 QMediaPlayer::Error error = QMediaPlayer::ResourceError;
359
360 switch (code) {
361 case QWasmMediaNetworkState::NetworkEmpty: // no data
362 break;
363 case QWasmMediaNetworkState::NetworkIdle:
364 break;
365 case QWasmMediaNetworkState::NetworkLoading:
366 break;
367 case QWasmMediaNetworkState::NetworkNoSource: // no source
368 error = QMediaPlayer::ResourceError;
369 errorString = message;
370 break;
371 };
372
373 emit QPlatformMediaPlayer::error(error, errorString);
374}
375
376void QWasmMediaPlayer::bufferingChanged(qint32 percent)
377{
378 m_buffering = percent != 100;
379 m_bufferPercent = percent;
380
381 updateAvailablePlaybackRanges();
382 emit bufferProgressChanged(bufferProgress());
383}
384
385void QWasmMediaPlayer::videoSizeChanged(qint32 width, qint32 height)
386{
387 QSize newSize(width, height);
388
389 if (width == 0 || height == 0 || newSize == m_videoSize)
390 return;
391
392 m_videoSize = newSize;
393}
394
395void QWasmMediaPlayer::mediaStateChanged(QWasmMediaPlayer::QWasmMediaPlayerState state)
396{
397 m_State = state;
398 QMediaPlayer::PlaybackState m_mediaPlayerState;
399 switch (m_State) {
400 case QWasmMediaPlayer::Started:
401 m_mediaPlayerState = QMediaPlayer::PlayingState;
402 break;
403 case QWasmMediaPlayer::Paused:
404 m_mediaPlayerState = QMediaPlayer::PausedState;
405 break;
406 case QWasmMediaPlayer::Stopped:
407 m_mediaPlayerState = QMediaPlayer::StoppedState;
408 break;
409 default:
410 m_mediaPlayerState = QMediaPlayer::StoppedState;
411 break;
412 };
413
414 QPlatformMediaPlayer::stateChanged(m_mediaPlayerState);
415}
416
417int QWasmMediaPlayer::trackCount(TrackType trackType)
418{
419 Q_UNUSED(trackType)
420 // TODO QTBUG-108517
421 return 0; // tracks.count();
422}
423
424void QWasmMediaPlayer::setPositionChanged(qint64 position)
425{
426 QPlatformMediaPlayer::positionChanged(position);
427}
428
429void QWasmMediaPlayer::setDurationChanged(qint64 duration)
430{
431 QPlatformMediaPlayer::durationChanged(duration);
432}
433
434void QWasmMediaPlayer::videoOutputReady(bool ready)
435{
436 setVideoAvailable(ready);
437
438 if (m_playWhenReady && m_videoOutput->isReady())
439 play();
440}
441
442void QWasmMediaPlayer::setMediaStatus(QMediaPlayer::MediaStatus status)
443{
444 mediaStatusChanged(status);
445
446 switch (status) {
447 case QMediaPlayer::NoMedia:
448 case QMediaPlayer::InvalidMedia:
449 emit durationChanged(0);
450 break;
451 case QMediaPlayer::EndOfMedia:
452 setPositionChanged(position());
453 default:
454 break;
455 };
456}
457
458void QWasmMediaPlayer::setAudioAvailable(bool available)
459{
460 if (m_audioAvailable == available)
461 return;
462
463 m_audioAvailable = available;
464 emit audioAvailableChanged(m_audioAvailable);
465}
466
467void QWasmMediaPlayer::setVideoAvailable(bool available)
468{
469 if (m_videoAvailable == available)
470 return;
471
472 if (!available)
473 m_videoSize = QSize();
474
475 m_videoAvailable = available;
476 emit videoAvailableChanged(m_videoAvailable);
477}
478
479void QWasmMediaPlayer::resetBufferingProgress()
480{
481 m_buffering = false;
482 m_bufferPercent = 0;
483 m_availablePlaybackRange = QMediaTimeRange();
484}
485
486void QWasmMediaPlayer::onMediaStatusChanged(QMediaPlayer::MediaStatus status)
487{
488 setMediaStatus(status);
489}
490
491void QWasmMediaPlayer::videoMetaDataChanged()
492{
493 metaDataChanged();
494}
495
496void QWasmMediaPlayer::seekableMediaChanged(bool seekable)
497{
498 seekableChanged(seekable);
499}
500
501QT_END_NAMESPACE
502
503#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.