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.get(), &JsMediaInputStream::mediaStreamReady,
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_shouldBeStarted =
false;
120 m_video.call<
void>(
"play");
122 if (m_currentVideoMode == QWasmVideoOutput::Camera
123 || m_currentVideoMode == QWasmVideoOutput::SurfaceCapture) {
124 emit readyChanged(
true);
126 videoFrameTimerCallback();
131 m_mediaInputStream->setStreamDevice(m_cameraId);
136 m_shouldStop =
false;
137 m_toBePaused =
false;
141 m_video.call<
void>(
"play");
465 qCDebug(qWasmMediaVideoOutput) << Q_FUNC_INFO;
469 auto timeUpdateCallback = [=](emscripten::val event) {
470 qCDebug(qWasmMediaVideoOutput) <<
"timeupdate";
473 emit progressChanged(event[
"target"][
"currentTime"].as<
double>() * 1000);
475 m_timeUpdateEvent.reset(
new QWasmEventHandler(m_video,
"timeupdate", timeUpdateCallback));
478 auto playCallback = [=](emscripten::val event) {
480 qCDebug(qWasmMediaVideoOutput) <<
"play" << m_video[
"src"].as<std::string>();
484 m_playEvent.reset(
new QWasmEventHandler(m_video,
"play", playCallback));
487 auto endedCallback = [=](emscripten::val event) {
489 qCDebug(qWasmMediaVideoOutput) <<
"ended";
490 m_currentMediaStatus = MediaStatus::EndOfMedia;
491 emit statusChanged(m_currentMediaStatus);
495 m_endedEvent.reset(
new QWasmEventHandler(m_video,
"ended", endedCallback));
498 auto durationChangeCallback = [=](emscripten::val event) {
499 qCDebug(qWasmMediaVideoOutput) <<
"durationChange";
502 qint64 dur = event[
"target"][
"duration"].as<
double>() * 1000;
503 emit durationChanged(dur);
505 m_durationChangeEvent.reset(
506 new QWasmEventHandler(m_video,
"durationchange", durationChangeCallback));
509 auto loadedDataCallback = [=](emscripten::val event) {
511 qCDebug(qWasmMediaVideoOutput) <<
"loaded data";
516 emit seekableChanged(m_isSeekable);
519 m_loadedDataEvent.reset(
new QWasmEventHandler(m_video,
"loadeddata", loadedDataCallback));
522 auto errorCallback = [=](emscripten::val event) {
523 qCDebug(qWasmMediaVideoOutput) <<
"error";
524 if (event.isUndefined() || event.isNull())
526 emit errorOccured(m_video[
"error"][
"code"].as<
int>(),
527 QString::fromStdString(m_video[
"error"][
"message"].as<std::string>()));
529 m_errorChangeEvent.reset(
new QWasmEventHandler(m_video,
"error", errorCallback));
532 auto resizeCallback = [=](emscripten::val event) {
534 qCDebug(qWasmMediaVideoOutput) <<
"resize";
536 updateVideoElementGeometry(
537 QRect(0, 0, m_video[
"videoWidth"].as<
int>(), m_video[
"videoHeight"].as<
int>()));
538 emit sizeChange(m_video[
"videoWidth"].as<
int>(), m_video[
"videoHeight"].as<
int>());
541 m_resizeChangeEvent.reset(
new QWasmEventHandler(m_video,
"resize", resizeCallback));
544 auto loadedMetadataCallback = [=](emscripten::val event) {
546 qCDebug(qWasmMediaVideoOutput) <<
"loaded meta data";
548 emit metaDataLoaded();
550 m_loadedMetadataChangeEvent.reset(
551 new QWasmEventHandler(m_video,
"loadedmetadata", loadedMetadataCallback));
554 auto loadStartCallback = [=](emscripten::val event) {
556 qCDebug(qWasmMediaVideoOutput) <<
"load started";
557 m_currentMediaStatus = MediaStatus::LoadingMedia;
558 emit statusChanged(m_currentMediaStatus);
559 m_shouldStop =
false;
561 m_loadStartChangeEvent.reset(
new QWasmEventHandler(m_video,
"loadstart", loadStartCallback));
565 auto canPlayCallback = [=](emscripten::val event) {
566 if (event.isUndefined() || event.isNull())
568 qCDebug(qWasmMediaVideoOutput) <<
"can play"
569 <<
"m_requestedPosition" << m_requestedPosition;
572 emit readyChanged(
true);
574 m_canPlayChangeEvent.reset(
new QWasmEventHandler(m_video,
"canplay", canPlayCallback));
577 auto canPlayThroughCallback = [=](emscripten::val event) {
579 qCDebug(qWasmMediaVideoOutput) <<
"can play through"
580 <<
"m_shouldStop" << m_shouldStop;
582 if (m_currentMediaStatus == MediaStatus::EndOfMedia)
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 hasCurrentData = 2;
601 if (m_video[
"readyState"].as<
int>() >= hasCurrentData) {
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));
859 emscripten::val videoElement = videoOutput->currentVideoElement();
860 emscripten::val oneVideoFrame = val::global(
"VideoFrame").new_(videoElement);
862 if (oneVideoFrame.isNull() || oneVideoFrame.isUndefined()) {
863 qCDebug(qWasmMediaVideoOutput) << Q_FUNC_INFO
864 <<
"ERROR" <<
"failed to construct VideoFrame";
868 emscripten::val options = emscripten::val::object();
869 emscripten::val rectOptions = emscripten::val::object();
871 rectOptions.set(
"width",oneVideoFrame[
"displayWidth"].as<
int>());
872 rectOptions.set(
"height", oneVideoFrame[
"displayHeight"].as<
int>());
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, videoElement]
883 (emscripten::val frameLayout)
885 if (frameLayout.isNull() || frameLayout.isUndefined()) {
886 qCDebug(qWasmMediaVideoOutput) <<
"theres no frameLayout";
891 const QSize frameSize(oneVideoFrame[
"displayWidth"].as<
int>(),
892 oneVideoFrame[
"displayHeight"].as<
int>());
894 QByteArray frameBytes = QByteArray::fromEcmaUint8Array(frameBuffer);
896 QVideoFrameFormat::PixelFormat pixelFormat = fromJsPixelFormat(oneVideoFrame[
"format"].as<std::string>());
897 if (pixelFormat == QVideoFrameFormat::Format_Invalid) {
898 qWarning() <<
"Invalid pixel format";
901 QVideoFrameFormat frameFormat = QVideoFrameFormat(frameSize, pixelFormat);
903 auto buffer = std::make_unique<QMemoryVideoBuffer>(
904 std::move(frameBytes),
905 oneVideoFrame[
"codedWidth"].as<
int>());
908 QVideoFramePrivate::createFrame(std::move(buffer), std::move(frameFormat));
910 if (!wasmVideoOutput) {
911 qCDebug(qWasmMediaVideoOutput) <<
"ERROR:"
912 <<
"data-qvideocontext not found";
915 if (!wasmVideoOutput->m_wasmSink) {
916 qWarning() <<
"ERROR ALERT!! video sink not set";
919 wasmVideoOutput->m_wasmSink->setVideoFrame(vFrame);
920 oneVideoFrame.call<emscripten::val>(
"close");
922 copyToCallback.catchFunc = [&, wasmVideoOutput, oneVideoFrame, videoElement](emscripten::val error)
924 qCDebug(qWasmMediaVideoOutput) <<
"Error"
925 << QString::fromStdString(error[
"name"].as<std::string>())
926 << QString::fromStdString(error[
"message"].as<std::string>()) ;
928 oneVideoFrame.call<emscripten::val>(
"close");
933 qstdweb::Promise::make(oneVideoFrame, u"copyTo"_s, std::move(copyToCallback), frameBuffer);