66 if (m_video.isUndefined() || m_video.isNull()
69 emit errorOccured(QMediaPlayer::ResourceError, QStringLiteral(
"video surface error"));
72 switch (m_currentVideoMode) {
74 emscripten::val sourceObj = m_video[
"src"];
75 if ((sourceObj.isUndefined() || sourceObj.isNull()) && !m_source.isEmpty()) {
76 m_video.set(
"src", m_source);
79 m_video.call<
void>(
"load");
83 if (!m_cameraIsReady) {
84 m_shouldBeStarted =
true;
88 m_connection = connect(m_mediaInputStream, &JsMediaInputStream::mediaVideoStreamReady,
this,
90 m_video.set(
"srcObject", m_mediaInputStream->getMediaStream());
92 emscripten::val stream = m_video[
"srcObject"];
93 if (stream.isNull() || stream.isUndefined()) {
94 qCDebug(qWasmMediaVideoOutput) <<
"srcObject ERROR";
95 emit errorOccured(QMediaPlayer::ResourceError, QStringLiteral(
"video surface error"));
98 emscripten::val videoTracks = stream.call<emscripten::val>(
"getVideoTracks");
99 if (videoTracks.isNull() || videoTracks.isUndefined()) {
100 qCDebug(qWasmMediaVideoOutput) << Q_FUNC_INFO <<
"videoTracks is null";
101 emit errorOccured(QMediaPlayer::ResourceError,
102 QStringLiteral(
"video surface error"));
105 if (videoTracks[
"length"].as<
int>() == 0) {
106 qCDebug(qWasmMediaVideoOutput) << Q_FUNC_INFO <<
"videoTracks count is 0";
107 emit errorOccured(QMediaPlayer::ResourceError,
108 QStringLiteral(
"video surface error"));
111 emscripten::val videoSettings = videoTracks[0].call<emscripten::val>(
"getSettings");
112 if (!videoSettings.isNull() || !videoSettings.isUndefined()) {
113 const int width = videoSettings[
"width"].as<
int>();
114 const int height = videoSettings[
"height"].as<
int>();
115 updateVideoElementGeometry(QRect(0, 0, width, height));
119 m_video.call<
void>(
"play");
121 if (m_currentVideoMode == QWasmVideoOutput::Camera
122 || m_currentVideoMode == QWasmVideoOutput::SurfaceCapture) {
123 emit readyChanged(
true);
125 videoFrameTimerCallback();
129 m_mediaInputStream->setUseAudio(
false);
130 m_shouldBeStarted =
true;
131 m_mediaInputStream->setStreamDevice(m_cameraId);
136 m_shouldStop =
false;
137 m_toBePaused =
false;
141 m_video.call<
void>(
"play");
466 qCDebug(qWasmMediaVideoOutput) << Q_FUNC_INFO;
470 auto timeUpdateCallback = [=](emscripten::val event) {
471 qCDebug(qWasmMediaVideoOutput) <<
"timeupdate";
474 emit progressChanged(event[
"target"][
"currentTime"].as<
double>() * 1000);
476 m_timeUpdateEvent.reset(
new QWasmEventHandler(m_video,
"timeupdate", timeUpdateCallback));
479 auto playCallback = [=](emscripten::val event) {
481 qCDebug(qWasmMediaVideoOutput) <<
"play" << m_video[
"src"].as<std::string>();
485 m_playEvent.reset(
new QWasmEventHandler(m_video,
"play", playCallback));
488 auto endedCallback = [=](emscripten::val event) {
490 qCDebug(qWasmMediaVideoOutput) <<
"ended";
491 m_currentMediaStatus = MediaStatus::EndOfMedia;
492 emit statusChanged(m_currentMediaStatus);
494 m_endedEvent.reset(
new QWasmEventHandler(m_video,
"ended", endedCallback));
497 auto durationChangeCallback = [=](emscripten::val event) {
498 qCDebug(qWasmMediaVideoOutput) <<
"durationChange";
501 qint64 dur = event[
"target"][
"duration"].as<
double>() * 1000;
502 emit durationChanged(dur);
504 m_durationChangeEvent.reset(
505 new QWasmEventHandler(m_video,
"durationchange", durationChangeCallback));
508 auto loadedDataCallback = [=](emscripten::val event) {
510 qCDebug(qWasmMediaVideoOutput) <<
"loaded data";
515 emit seekableChanged(m_isSeekable);
518 m_loadedDataEvent.reset(
new QWasmEventHandler(m_video,
"loadeddata", loadedDataCallback));
521 auto errorCallback = [=](emscripten::val event) {
522 qCDebug(qWasmMediaVideoOutput) <<
"error";
523 if (event.isUndefined() || event.isNull())
525 emit errorOccured(m_video[
"error"][
"code"].as<
int>(),
526 QString::fromStdString(m_video[
"error"][
"message"].as<std::string>()));
528 m_errorChangeEvent.reset(
new QWasmEventHandler(m_video,
"error", errorCallback));
531 auto resizeCallback = [=](emscripten::val event) {
533 qCDebug(qWasmMediaVideoOutput) <<
"resize";
535 updateVideoElementGeometry(
536 QRect(0, 0, m_video[
"videoWidth"].as<
int>(), m_video[
"videoHeight"].as<
int>()));
537 emit sizeChange(m_video[
"videoWidth"].as<
int>(), m_video[
"videoHeight"].as<
int>());
540 m_resizeChangeEvent.reset(
new QWasmEventHandler(m_video,
"resize", resizeCallback));
543 auto loadedMetadataCallback = [=](emscripten::val event) {
545 qCDebug(qWasmMediaVideoOutput) <<
"loaded meta data";
547 emit metaDataLoaded();
549 m_loadedMetadataChangeEvent.reset(
550 new QWasmEventHandler(m_video,
"loadedmetadata", loadedMetadataCallback));
553 auto loadStartCallback = [=](emscripten::val event) {
555 qCDebug(qWasmMediaVideoOutput) <<
"load started";
556 m_currentMediaStatus = MediaStatus::LoadingMedia;
557 emit statusChanged(m_currentMediaStatus);
558 m_shouldStop =
false;
560 m_loadStartChangeEvent.reset(
new QWasmEventHandler(m_video,
"loadstart", loadStartCallback));
564 auto canPlayCallback = [=](emscripten::val event) {
565 if (event.isUndefined() || event.isNull())
567 qCDebug(qWasmMediaVideoOutput) <<
"can play"
568 <<
"m_requestedPosition" << m_requestedPosition;
571 emit readyChanged(
true);
573 m_canPlayChangeEvent.reset(
new QWasmEventHandler(m_video,
"canplay", canPlayCallback));
576 auto canPlayThroughCallback = [=](emscripten::val event) {
578 qCDebug(qWasmMediaVideoOutput) <<
"can play through"
579 <<
"m_shouldStop" << m_shouldStop;
581 if (m_currentMediaStatus == MediaStatus::EndOfMedia)
584 if (m_isSeekable != seekable) {
585 m_isSeekable = seekable;
586 emit seekableChanged(m_isSeekable);
588 if (!m_isSeeking && !m_shouldStop) {
589 emscripten::val timeRanges = m_video[
"buffered"];
590 if ((!timeRanges.isNull() || !timeRanges.isUndefined())
591 && timeRanges[
"length"].as<
int>() == 1) {
592 double buffered = m_video[
"buffered"].call<emscripten::val>(
"end", 0).as<
double>();
593 const double duration = m_video[
"duration"].as<
double>();
595 if (duration == buffered) {
596 m_currentBufferedValue = 100;
597 emit bufferingChanged(m_currentBufferedValue);
600 constexpr int hasEnoughData = 4;
601 if (m_video[
"readyState"].as<
int>() == hasEnoughData) {
602 m_currentMediaStatus = MediaStatus::LoadedMedia;
603 emit statusChanged(m_currentMediaStatus);
607 m_shouldStop =
false;
610 m_canPlayThroughChangeEvent.reset(
611 new QWasmEventHandler(m_video,
"canplaythrough", canPlayThroughCallback));
614 auto seekingCallback = [=](emscripten::val event) {
616 qCDebug(qWasmMediaVideoOutput)
617 <<
"seeking started" << (m_video[
"currentTime"].as<
double>() * 1000);
620 m_seekingChangeEvent.reset(
new QWasmEventHandler(m_video,
"seeking", seekingCallback));
623 auto seekedCallback = [=](emscripten::val event) {
625 qCDebug(qWasmMediaVideoOutput) <<
"seeked" << (m_video[
"currentTime"].as<
double>() * 1000);
626 emit progressChanged(m_video[
"currentTime"].as<
double>() * 1000);
629 m_seekedChangeEvent.reset(
new QWasmEventHandler(m_video,
"seeked", seekedCallback));
632 auto emptiedCallback = [=](emscripten::val event) {
634 qCDebug(qWasmMediaVideoOutput) <<
"emptied";
635 emit readyChanged(
false);
636 m_currentMediaStatus = MediaStatus::EndOfMedia;
637 emit statusChanged(m_currentMediaStatus);
639 m_emptiedChangeEvent.reset(
new QWasmEventHandler(m_video,
"emptied", emptiedCallback));
642 auto stalledCallback = [=](emscripten::val event) {
644 qCDebug(qWasmMediaVideoOutput) <<
"stalled";
645 m_currentMediaStatus = MediaStatus::StalledMedia;
646 emit statusChanged(m_currentMediaStatus);
648 m_stalledChangeEvent.reset(
new QWasmEventHandler(m_video,
"stalled", stalledCallback));
651 auto waitingCallback = [=](emscripten::val event) {
654 qCDebug(qWasmMediaVideoOutput) <<
"waiting";
657 m_waitingChangeEvent.reset(
new QWasmEventHandler(m_video,
"waiting", waitingCallback));
662 auto playingCallback = [=](emscripten::val event) {
664 qCDebug(qWasmMediaVideoOutput) <<
"playing";
668 if (m_toBePaused || !m_shouldStop) {
669 m_toBePaused =
false;
670 QMetaObject::invokeMethod(
this, &QWasmVideoOutput::videoFrameTimerCallback, Qt::QueuedConnection);
673 m_playingChangeEvent.reset(
new QWasmEventHandler(m_video,
"playing", playingCallback));
676 auto progesssCallback = [=](emscripten::val event) {
677 if (event.isUndefined() || event.isNull())
680 const double duration = event[
"target"][
"duration"].as<
double>();
684 emscripten::val timeRanges = event[
"target"][
"buffered"];
686 if ((!timeRanges.isNull() || !timeRanges.isUndefined())
687 && timeRanges[
"length"].as<
int>() == 1) {
688 emscripten::val dVal = timeRanges.call<emscripten::val>(
"end", 0);
689 if (!dVal.isNull() || !dVal.isUndefined()) {
690 double bufferedEnd = dVal.as<
double>();
692 if (duration > 0 && bufferedEnd > 0) {
693 const double bufferedValue = (bufferedEnd / duration * 100);
694 qCDebug(qWasmMediaVideoOutput) <<
"progress buffered";
695 m_currentBufferedValue = bufferedValue;
696 emit bufferingChanged(m_currentBufferedValue);
697 if (bufferedEnd == duration)
698 m_currentMediaStatus = MediaStatus::BufferedMedia;
700 m_currentMediaStatus = MediaStatus::BufferingMedia;
701 emit statusChanged(m_currentMediaStatus);
706 m_progressChangeEvent.reset(
new QWasmEventHandler(m_video,
"progress", progesssCallback));
709 auto pauseCallback = [=](emscripten::val event) {
711 qCDebug(qWasmMediaVideoOutput) <<
"pause";
713 const double currentTime = m_video[
"currentTime"].as<
double>();
714 const double duration = m_video[
"duration"].as<
double>();
715 if ((currentTime > 0 && currentTime < duration) && (!m_shouldStop && m_toBePaused)) {
719 m_video.set(
"currentTime", emscripten::val(0));
723 m_pauseChangeEvent.reset(
new QWasmEventHandler(m_video,
"pause", pauseCallback));
728 emscripten::val window = emscripten::val::global(
"window");
730 auto beforeUnloadCallback = [=](emscripten::val event) {
734 m_video.call<
void>(
"removeAttribute", emscripten::val(
"src"));
735 m_video.call<
void>(
"load");
737 m_beforeUnloadEvent.reset(
new QWasmEventHandler(window,
"beforeunload", beforeUnloadCallback));
856 emscripten::val videoElement = videoOutput->currentVideoElement();
857 emscripten::val oneVideoFrame = val::global(
"VideoFrame").new_(videoElement);
859 if (oneVideoFrame.isNull() || oneVideoFrame.isUndefined()) {
860 qCDebug(qWasmMediaVideoOutput) << Q_FUNC_INFO
861 <<
"ERROR" <<
"failed to construct VideoFrame";
865 emscripten::val options = emscripten::val::object();
866 emscripten::val rectOptions = emscripten::val::object();
868 int displayWidth = oneVideoFrame[
"displayWidth"].as<
int>();
869 int displayHeight = oneVideoFrame[
"displayHeight"].as<
int>();
871 rectOptions.set(
"width", displayWidth);
872 rectOptions.set(
"height", displayHeight);
873 options.set(
"rect", rectOptions);
875 emscripten::val frameBytesAllocationSize = oneVideoFrame.call<emscripten::val>(
"allocationSize", options);
876 emscripten::val frameBuffer =
877 emscripten::val::global(
"Uint8Array").new_(frameBytesAllocationSize);
879 reinterpret_cast<QWasmVideoOutput*>(videoElement[
"data-qvideocontext"].as<quintptr>());
881 qstdweb::PromiseCallbacks copyToCallback;
882 copyToCallback.thenFunc = [wasmVideoOutput, oneVideoFrame, frameBuffer,
883 displayWidth, displayHeight]
884 (emscripten::val frameLayout)
886 if (frameLayout.isNull() || frameLayout.isUndefined()) {
887 qCDebug(qWasmMediaVideoOutput) <<
"theres no frameLayout";
892 const QSize frameSize(displayWidth,
895 QByteArray frameBytes = QByteArray::fromEcmaUint8Array(frameBuffer);
897 QVideoFrameFormat::PixelFormat pixelFormat = fromJsPixelFormat(oneVideoFrame[
"format"].as<std::string>());
898 if (pixelFormat == QVideoFrameFormat::Format_Invalid) {
899 qWarning() <<
"Invalid pixel format";
902 QVideoFrameFormat frameFormat = QVideoFrameFormat(frameSize, pixelFormat);
904 auto buffer = std::make_unique<QMemoryVideoBuffer>(
905 std::move(frameBytes),
906 oneVideoFrame[
"codedWidth"].as<
int>());
909 QVideoFramePrivate::createFrame(std::move(buffer), std::move(frameFormat));
911 if (!wasmVideoOutput) {
912 qCDebug(qWasmMediaVideoOutput) <<
"ERROR:"
913 <<
"data-qvideocontext not found";
916 if (!wasmVideoOutput->m_wasmSink) {
917 qWarning() <<
"ERROR ALERT!! video sink not set";
920 wasmVideoOutput->m_wasmSink->setVideoFrame(vFrame);
921 oneVideoFrame.call<emscripten::val>(
"close");
923 copyToCallback.catchFunc = [&, wasmVideoOutput, oneVideoFrame](emscripten::val error)
925 qCDebug(qWasmMediaVideoOutput) <<
"Error"
926 << QString::fromStdString(error[
"name"].as<std::string>())
927 << QString::fromStdString(error[
"message"].as<std::string>()) ;
929 oneVideoFrame.call<emscripten::val>(
"close");
934 qstdweb::Promise::make(oneVideoFrame, u"copyTo"_s, std::move(copyToCallback), frameBuffer);