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");
468 qCDebug(qWasmMediaVideoOutput) << Q_FUNC_INFO;
472 auto timeUpdateCallback = [=](emscripten::val event) {
473 qCDebug(qWasmMediaVideoOutput) <<
"timeupdate";
476 emit progressChanged(event[
"target"][
"currentTime"].as<
double>() * 1000);
478 m_timeUpdateEvent.reset(
new QWasmEventHandler(m_video,
"timeupdate", timeUpdateCallback));
481 auto playCallback = [=](emscripten::val event) {
483 qCDebug(qWasmMediaVideoOutput) <<
"play" << m_video[
"src"].as<std::string>();
487 m_playEvent.reset(
new QWasmEventHandler(m_video,
"play", playCallback));
490 auto endedCallback = [=](emscripten::val event) {
492 qCDebug(qWasmMediaVideoOutput) <<
"ended";
493 m_currentMediaStatus = MediaStatus::EndOfMedia;
494 emit statusChanged(m_currentMediaStatus);
498 m_endedEvent.reset(
new QWasmEventHandler(m_video,
"ended", endedCallback));
501 auto durationChangeCallback = [=](emscripten::val event) {
502 qCDebug(qWasmMediaVideoOutput) <<
"durationChange";
505 qint64 dur = event[
"target"][
"duration"].as<
double>() * 1000;
506 emit durationChanged(dur);
508 m_durationChangeEvent.reset(
509 new QWasmEventHandler(m_video,
"durationchange", durationChangeCallback));
512 auto loadedDataCallback = [=](emscripten::val event) {
514 qCDebug(qWasmMediaVideoOutput) <<
"loaded data";
519 emit seekableChanged(m_isSeekable);
522 m_loadedDataEvent.reset(
new QWasmEventHandler(m_video,
"loadeddata", loadedDataCallback));
525 auto errorCallback = [=](emscripten::val event) {
526 qCDebug(qWasmMediaVideoOutput) <<
"error";
527 if (event.isUndefined() || event.isNull())
529 emit errorOccured(m_video[
"error"][
"code"].as<
int>(),
530 QString::fromStdString(m_video[
"error"][
"message"].as<std::string>()));
532 m_errorChangeEvent.reset(
new QWasmEventHandler(m_video,
"error", errorCallback));
535 auto resizeCallback = [=](emscripten::val event) {
537 qCDebug(qWasmMediaVideoOutput) <<
"resize";
539 updateVideoElementGeometry(
540 QRect(0, 0, m_video[
"videoWidth"].as<
int>(), m_video[
"videoHeight"].as<
int>()));
541 emit sizeChange(m_video[
"videoWidth"].as<
int>(), m_video[
"videoHeight"].as<
int>());
544 m_resizeChangeEvent.reset(
new QWasmEventHandler(m_video,
"resize", resizeCallback));
547 auto loadedMetadataCallback = [=](emscripten::val event) {
549 qCDebug(qWasmMediaVideoOutput) <<
"loaded meta data";
551 emit metaDataLoaded();
553 m_loadedMetadataChangeEvent.reset(
554 new QWasmEventHandler(m_video,
"loadedmetadata", loadedMetadataCallback));
557 auto loadStartCallback = [=](emscripten::val event) {
559 qCDebug(qWasmMediaVideoOutput) <<
"load started";
560 m_currentMediaStatus = MediaStatus::LoadingMedia;
561 emit statusChanged(m_currentMediaStatus);
562 m_shouldStop =
false;
564 m_loadStartChangeEvent.reset(
new QWasmEventHandler(m_video,
"loadstart", loadStartCallback));
568 auto canPlayCallback = [=](emscripten::val event) {
569 if (event.isUndefined() || event.isNull())
571 qCDebug(qWasmMediaVideoOutput) <<
"can play"
572 <<
"m_requestedPosition" << m_requestedPosition;
575 emit readyChanged(
true);
577 m_canPlayChangeEvent.reset(
new QWasmEventHandler(m_video,
"canplay", canPlayCallback));
580 auto canPlayThroughCallback = [=](emscripten::val event) {
582 qCDebug(qWasmMediaVideoOutput) <<
"can play through"
583 <<
"m_shouldStop" << m_shouldStop;
585 if (m_currentMediaStatus == MediaStatus::EndOfMedia)
588 if (m_isSeekable != seekable) {
589 m_isSeekable = seekable;
590 emit seekableChanged(m_isSeekable);
592 if (!m_isSeeking && !m_shouldStop) {
593 emscripten::val timeRanges = m_video[
"buffered"];
594 if ((!timeRanges.isNull() || !timeRanges.isUndefined())
595 && timeRanges[
"length"].as<
int>() == 1) {
596 double buffered = m_video[
"buffered"].call<emscripten::val>(
"end", 0).as<
double>();
597 const double duration = m_video[
"duration"].as<
double>();
599 if (duration == buffered) {
600 m_currentBufferedValue = 100;
601 emit bufferingChanged(m_currentBufferedValue);
604 constexpr int hasEnoughData = 4;
605 if (m_video[
"readyState"].as<
int>() == hasEnoughData) {
606 m_currentMediaStatus = MediaStatus::LoadedMedia;
607 emit statusChanged(m_currentMediaStatus);
611 m_shouldStop =
false;
614 m_canPlayThroughChangeEvent.reset(
615 new QWasmEventHandler(m_video,
"canplaythrough", canPlayThroughCallback));
618 auto seekingCallback = [=](emscripten::val event) {
620 qCDebug(qWasmMediaVideoOutput)
621 <<
"seeking started" << (m_video[
"currentTime"].as<
double>() * 1000);
624 m_seekingChangeEvent.reset(
new QWasmEventHandler(m_video,
"seeking", seekingCallback));
627 auto seekedCallback = [=](emscripten::val event) {
629 qCDebug(qWasmMediaVideoOutput) <<
"seeked" << (m_video[
"currentTime"].as<
double>() * 1000);
630 emit progressChanged(m_video[
"currentTime"].as<
double>() * 1000);
633 m_seekedChangeEvent.reset(
new QWasmEventHandler(m_video,
"seeked", seekedCallback));
636 auto emptiedCallback = [=](emscripten::val event) {
638 qCDebug(qWasmMediaVideoOutput) <<
"emptied";
639 emit readyChanged(
false);
640 m_currentMediaStatus = MediaStatus::EndOfMedia;
641 emit statusChanged(m_currentMediaStatus);
643 m_emptiedChangeEvent.reset(
new QWasmEventHandler(m_video,
"emptied", emptiedCallback));
646 auto stalledCallback = [=](emscripten::val event) {
648 qCDebug(qWasmMediaVideoOutput) <<
"stalled";
649 m_currentMediaStatus = MediaStatus::StalledMedia;
650 emit statusChanged(m_currentMediaStatus);
652 m_stalledChangeEvent.reset(
new QWasmEventHandler(m_video,
"stalled", stalledCallback));
655 auto waitingCallback = [=](emscripten::val event) {
658 qCDebug(qWasmMediaVideoOutput) <<
"waiting";
661 m_waitingChangeEvent.reset(
new QWasmEventHandler(m_video,
"waiting", waitingCallback));
666 auto playingCallback = [=](emscripten::val event) {
668 qCDebug(qWasmMediaVideoOutput) <<
"playing";
672 if (m_toBePaused || !m_shouldStop) {
673 m_toBePaused =
false;
674 QMetaObject::invokeMethod(
this, &QWasmVideoOutput::videoFrameTimerCallback, Qt::QueuedConnection);
677 m_playingChangeEvent.reset(
new QWasmEventHandler(m_video,
"playing", playingCallback));
680 auto progesssCallback = [=](emscripten::val event) {
681 if (event.isUndefined() || event.isNull())
684 const double duration = event[
"target"][
"duration"].as<
double>();
688 emscripten::val timeRanges = event[
"target"][
"buffered"];
690 if ((!timeRanges.isNull() || !timeRanges.isUndefined())
691 && timeRanges[
"length"].as<
int>() == 1) {
692 emscripten::val dVal = timeRanges.call<emscripten::val>(
"end", 0);
693 if (!dVal.isNull() || !dVal.isUndefined()) {
694 double bufferedEnd = dVal.as<
double>();
696 if (duration > 0 && bufferedEnd > 0) {
697 const double bufferedValue = (bufferedEnd / duration * 100);
698 qCDebug(qWasmMediaVideoOutput) <<
"progress buffered";
699 m_currentBufferedValue = bufferedValue;
700 emit bufferingChanged(m_currentBufferedValue);
701 if (bufferedEnd == duration)
702 m_currentMediaStatus = MediaStatus::BufferedMedia;
704 m_currentMediaStatus = MediaStatus::BufferingMedia;
705 emit statusChanged(m_currentMediaStatus);
710 m_progressChangeEvent.reset(
new QWasmEventHandler(m_video,
"progress", progesssCallback));
713 auto pauseCallback = [=](emscripten::val event) {
715 qCDebug(qWasmMediaVideoOutput) <<
"pause";
717 const double currentTime = m_video[
"currentTime"].as<
double>();
718 const double duration = m_video[
"duration"].as<
double>();
719 if ((currentTime > 0 && currentTime < duration) && (!m_shouldStop && m_toBePaused)) {
723 m_video.set(
"currentTime", emscripten::val(0));
727 m_pauseChangeEvent.reset(
new QWasmEventHandler(m_video,
"pause", pauseCallback));
732 emscripten::val window = emscripten::val::global(
"window");
734 auto beforeUnloadCallback = [=](emscripten::val event) {
738 m_video.call<
void>(
"removeAttribute", emscripten::val(
"src"));
739 m_video.call<
void>(
"load");
741 m_beforeUnloadEvent.reset(
new QWasmEventHandler(window,
"beforeunload", beforeUnloadCallback));
860 emscripten::val videoElement = videoOutput->currentVideoElement();
861 emscripten::val oneVideoFrame = val::global(
"VideoFrame").new_(videoElement);
863 if (oneVideoFrame.isNull() || oneVideoFrame.isUndefined()) {
864 qCDebug(qWasmMediaVideoOutput) << Q_FUNC_INFO
865 <<
"ERROR" <<
"failed to construct VideoFrame";
869 emscripten::val options = emscripten::val::object();
870 emscripten::val rectOptions = emscripten::val::object();
872 int displayWidth = oneVideoFrame[
"displayWidth"].as<
int>();
873 int displayHeight = oneVideoFrame[
"displayHeight"].as<
int>();
875 rectOptions.set(
"width", displayWidth);
876 rectOptions.set(
"height", displayHeight);
877 options.set(
"rect", rectOptions);
879 emscripten::val frameBytesAllocationSize = oneVideoFrame.call<emscripten::val>(
"allocationSize", options);
880 emscripten::val frameBuffer =
881 emscripten::val::global(
"Uint8Array").new_(frameBytesAllocationSize);
883 reinterpret_cast<QWasmVideoOutput*>(videoElement[
"data-qvideocontext"].as<quintptr>());
885 qstdweb::PromiseCallbacks copyToCallback;
886 copyToCallback.thenFunc = [wasmVideoOutput, oneVideoFrame, frameBuffer,
887 displayWidth, displayHeight]
888 (emscripten::val frameLayout)
890 if (frameLayout.isNull() || frameLayout.isUndefined()) {
891 qCDebug(qWasmMediaVideoOutput) <<
"theres no frameLayout";
896 const QSize frameSize(displayWidth,
899 QByteArray frameBytes = QByteArray::fromEcmaUint8Array(frameBuffer);
901 QVideoFrameFormat::PixelFormat pixelFormat = fromJsPixelFormat(oneVideoFrame[
"format"].as<std::string>());
902 if (pixelFormat == QVideoFrameFormat::Format_Invalid) {
903 qWarning() <<
"Invalid pixel format";
906 QVideoFrameFormat frameFormat = QVideoFrameFormat(frameSize, pixelFormat);
908 auto buffer = std::make_unique<QMemoryVideoBuffer>(
909 std::move(frameBytes),
910 oneVideoFrame[
"codedWidth"].as<
int>());
913 QVideoFramePrivate::createFrame(std::move(buffer), std::move(frameFormat));
915 if (!wasmVideoOutput) {
916 qCDebug(qWasmMediaVideoOutput) <<
"ERROR:"
917 <<
"data-qvideocontext not found";
920 if (!wasmVideoOutput->m_wasmSink) {
921 qWarning() <<
"ERROR ALERT!! video sink not set";
924 wasmVideoOutput->m_wasmSink->setVideoFrame(vFrame);
925 oneVideoFrame.call<emscripten::val>(
"close");
927 copyToCallback.catchFunc = [&, wasmVideoOutput, oneVideoFrame](emscripten::val error)
929 qCDebug(qWasmMediaVideoOutput) <<
"Error"
930 << QString::fromStdString(error[
"name"].as<std::string>())
931 << QString::fromStdString(error[
"message"].as<std::string>()) ;
933 oneVideoFrame.call<emscripten::val>(
"close");
938 qstdweb::Promise::make(oneVideoFrame, u"copyTo"_s, std::move(copyToCallback), frameBuffer);