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)
585 if (m_isSeekable != seekable) {
586 m_isSeekable = seekable;
587 emit seekableChanged(m_isSeekable);
589 if (!m_isSeeking && !m_shouldStop) {
590 emscripten::val timeRanges = m_video[
"buffered"];
591 if ((!timeRanges.isNull() || !timeRanges.isUndefined())
592 && timeRanges[
"length"].as<
int>() == 1) {
593 double buffered = m_video[
"buffered"].call<emscripten::val>(
"end", 0).as<
double>();
594 const double duration = m_video[
"duration"].as<
double>();
596 if (duration == buffered) {
597 m_currentBufferedValue = 100;
598 emit bufferingChanged(m_currentBufferedValue);
601 constexpr int hasEnoughData = 4;
602 if (m_video[
"readyState"].as<
int>() == hasEnoughData) {
603 m_currentMediaStatus = MediaStatus::LoadedMedia;
604 emit statusChanged(m_currentMediaStatus);
608 m_shouldStop =
false;
611 m_canPlayThroughChangeEvent.reset(
612 new QWasmEventHandler(m_video,
"canplaythrough", canPlayThroughCallback));
615 auto seekingCallback = [=](emscripten::val event) {
617 qCDebug(qWasmMediaVideoOutput)
618 <<
"seeking started" << (m_video[
"currentTime"].as<
double>() * 1000);
621 m_seekingChangeEvent.reset(
new QWasmEventHandler(m_video,
"seeking", seekingCallback));
624 auto seekedCallback = [=](emscripten::val event) {
626 qCDebug(qWasmMediaVideoOutput) <<
"seeked" << (m_video[
"currentTime"].as<
double>() * 1000);
627 emit progressChanged(m_video[
"currentTime"].as<
double>() * 1000);
630 m_seekedChangeEvent.reset(
new QWasmEventHandler(m_video,
"seeked", seekedCallback));
633 auto emptiedCallback = [=](emscripten::val event) {
635 qCDebug(qWasmMediaVideoOutput) <<
"emptied";
636 emit readyChanged(
false);
637 m_currentMediaStatus = MediaStatus::EndOfMedia;
638 emit statusChanged(m_currentMediaStatus);
640 m_emptiedChangeEvent.reset(
new QWasmEventHandler(m_video,
"emptied", emptiedCallback));
643 auto stalledCallback = [=](emscripten::val event) {
645 qCDebug(qWasmMediaVideoOutput) <<
"stalled";
646 m_currentMediaStatus = MediaStatus::StalledMedia;
647 emit statusChanged(m_currentMediaStatus);
649 m_stalledChangeEvent.reset(
new QWasmEventHandler(m_video,
"stalled", stalledCallback));
652 auto waitingCallback = [=](emscripten::val event) {
655 qCDebug(qWasmMediaVideoOutput) <<
"waiting";
658 m_waitingChangeEvent.reset(
new QWasmEventHandler(m_video,
"waiting", waitingCallback));
663 auto playingCallback = [=](emscripten::val event) {
665 qCDebug(qWasmMediaVideoOutput) <<
"playing";
669 if (m_toBePaused || !m_shouldStop) {
670 m_toBePaused =
false;
671 QMetaObject::invokeMethod(
this, &QWasmVideoOutput::videoFrameTimerCallback, Qt::QueuedConnection);
674 m_playingChangeEvent.reset(
new QWasmEventHandler(m_video,
"playing", playingCallback));
677 auto progesssCallback = [=](emscripten::val event) {
678 if (event.isUndefined() || event.isNull())
681 const double duration = event[
"target"][
"duration"].as<
double>();
685 emscripten::val timeRanges = event[
"target"][
"buffered"];
687 if ((!timeRanges.isNull() || !timeRanges.isUndefined())
688 && timeRanges[
"length"].as<
int>() == 1) {
689 emscripten::val dVal = timeRanges.call<emscripten::val>(
"end", 0);
690 if (!dVal.isNull() || !dVal.isUndefined()) {
691 double bufferedEnd = dVal.as<
double>();
693 if (duration > 0 && bufferedEnd > 0) {
694 const double bufferedValue = (bufferedEnd / duration * 100);
695 qCDebug(qWasmMediaVideoOutput) <<
"progress buffered";
696 m_currentBufferedValue = bufferedValue;
697 emit bufferingChanged(m_currentBufferedValue);
698 if (bufferedEnd == duration)
699 m_currentMediaStatus = MediaStatus::BufferedMedia;
701 m_currentMediaStatus = MediaStatus::BufferingMedia;
702 emit statusChanged(m_currentMediaStatus);
707 m_progressChangeEvent.reset(
new QWasmEventHandler(m_video,
"progress", progesssCallback));
710 auto pauseCallback = [=](emscripten::val event) {
712 qCDebug(qWasmMediaVideoOutput) <<
"pause";
714 const double currentTime = m_video[
"currentTime"].as<
double>();
715 const double duration = m_video[
"duration"].as<
double>();
716 if ((currentTime > 0 && currentTime < duration) && (!m_shouldStop && m_toBePaused)) {
720 m_video.set(
"currentTime", emscripten::val(0));
724 m_pauseChangeEvent.reset(
new QWasmEventHandler(m_video,
"pause", pauseCallback));
729 emscripten::val window = emscripten::val::global(
"window");
731 auto beforeUnloadCallback = [=](emscripten::val event) {
735 m_video.call<
void>(
"removeAttribute", emscripten::val(
"src"));
736 m_video.call<
void>(
"load");
738 m_beforeUnloadEvent.reset(
new QWasmEventHandler(window,
"beforeunload", beforeUnloadCallback));
857 emscripten::val videoElement = videoOutput->currentVideoElement();
858 emscripten::val oneVideoFrame = val::global(
"VideoFrame").new_(videoElement);
860 if (oneVideoFrame.isNull() || oneVideoFrame.isUndefined()) {
861 qCDebug(qWasmMediaVideoOutput) << Q_FUNC_INFO
862 <<
"ERROR" <<
"failed to construct VideoFrame";
866 emscripten::val options = emscripten::val::object();
867 emscripten::val rectOptions = emscripten::val::object();
869 int displayWidth = oneVideoFrame[
"displayWidth"].as<
int>();
870 int displayHeight = oneVideoFrame[
"displayHeight"].as<
int>();
872 rectOptions.set(
"width", displayWidth);
873 rectOptions.set(
"height", displayHeight);
874 options.set(
"rect", rectOptions);
876 emscripten::val frameBytesAllocationSize = oneVideoFrame.call<emscripten::val>(
"allocationSize", options);
877 emscripten::val frameBuffer =
878 emscripten::val::global(
"Uint8Array").new_(frameBytesAllocationSize);
880 reinterpret_cast<QWasmVideoOutput*>(videoElement[
"data-qvideocontext"].as<quintptr>());
882 qstdweb::PromiseCallbacks copyToCallback;
883 copyToCallback.thenFunc = [wasmVideoOutput, oneVideoFrame, frameBuffer,
884 displayWidth, displayHeight]
885 (emscripten::val frameLayout)
887 if (frameLayout.isNull() || frameLayout.isUndefined()) {
888 qCDebug(qWasmMediaVideoOutput) <<
"theres no frameLayout";
893 const QSize frameSize(displayWidth,
896 QByteArray frameBytes = QByteArray::fromEcmaUint8Array(frameBuffer);
898 QVideoFrameFormat::PixelFormat pixelFormat = fromJsPixelFormat(oneVideoFrame[
"format"].as<std::string>());
899 if (pixelFormat == QVideoFrameFormat::Format_Invalid) {
900 qWarning() <<
"Invalid pixel format";
903 QVideoFrameFormat frameFormat = QVideoFrameFormat(frameSize, pixelFormat);
905 auto buffer = std::make_unique<QMemoryVideoBuffer>(
906 std::move(frameBytes),
907 oneVideoFrame[
"codedWidth"].as<
int>());
910 QVideoFramePrivate::createFrame(std::move(buffer), std::move(frameFormat));
912 if (!wasmVideoOutput) {
913 qCDebug(qWasmMediaVideoOutput) <<
"ERROR:"
914 <<
"data-qvideocontext not found";
917 if (!wasmVideoOutput->m_wasmSink) {
918 qWarning() <<
"ERROR ALERT!! video sink not set";
921 wasmVideoOutput->m_wasmSink->setVideoFrame(vFrame);
922 oneVideoFrame.call<emscripten::val>(
"close");
924 copyToCallback.catchFunc = [&, wasmVideoOutput, oneVideoFrame](emscripten::val error)
926 qCDebug(qWasmMediaVideoOutput) <<
"Error"
927 << QString::fromStdString(error[
"name"].as<std::string>())
928 << QString::fromStdString(error[
"message"].as<std::string>()) ;
930 oneVideoFrame.call<emscripten::val>(
"close");
935 qstdweb::Promise::make(oneVideoFrame, u"copyTo"_s, std::move(copyToCallback), frameBuffer);