/* * Copyright (c) 2021-2024 Huawei Device Co., Ltd. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "frameworks/bridge/declarative_frontend/jsview/js_video.h" #include "base/log/ace_scoring_log.h" #include "bridge/common/utils/engine_helper.h" #include "bridge/declarative_frontend/jsview/js_utils.h" #include "bridge/declarative_frontend/jsview/js_video_controller.h" #include "bridge/declarative_frontend/jsview/models/video_model_impl.h" #include "core/components_ng/base/view_stack_processor.h" #include "core/components_ng/pattern/video/video_model_ng.h" #ifdef SUPPORT_JSSTACK #include "xpower_event_jsvm.h" #endif namespace OHOS::Ace { std::unique_ptr VideoModel::instance_ = nullptr; std::mutex VideoModel::mutex_; VideoModel* VideoModel::GetInstance() { if (!instance_) { std::lock_guard lock(mutex_); if (!instance_) { #ifdef NG_BUILD instance_.reset(new NG::VideoModelNG()); #else if (Container::IsCurrentUseNewPipeline()) { instance_.reset(new NG::VideoModelNG()); } else { instance_.reset(new Framework::VideoModelImpl()); } #endif } } return instance_.get(); } } // namespace OHOS::Ace namespace OHOS::Ace::Framework { void JSVideo::Create(const JSCallbackInfo& info) { if (!info[0]->IsObject()) { return; } JSRef videoObj = JSRef::Cast(info[0]); JSRef srcValue = videoObj->GetProperty("src"); JSRef previewUriValue = videoObj->GetProperty("previewUri"); JSRef currentProgressRateValue = videoObj->GetProperty("currentProgressRate"); auto controllerObj = videoObj->GetProperty("controller"); RefPtr videoController = nullptr; if (controllerObj->IsObject()) { auto* jsVideoController = JSRef::Cast(controllerObj)->Unwrap(); if (jsVideoController) { jsVideoController->SetInstanceId(Container::CurrentId()); videoController = jsVideoController->GetController(); } } VideoModel::GetInstance()->Create(videoController); // Parse the src, if it is invalid, use the empty string. std::string bundleNameSrc; std::string moduleNameSrc; std::string src; int32_t resId = 0; ParseJsMediaWithBundleName(srcValue, src, bundleNameSrc, moduleNameSrc, resId); VideoModel::GetInstance()->SetSrc(src, bundleNameSrc, moduleNameSrc); // Parse the rate, if it is invalid, set it as 1.0. double currentProgressRate = 1.0; ParseJsDouble(currentProgressRateValue, currentProgressRate); VideoModel::GetInstance()->SetProgressRate(currentProgressRate); auto aiOptions = videoObj->GetProperty("imageAIOptions"); if (aiOptions->IsObject()) { ParseImageAIOptions(aiOptions); } std::string previewUri; std::string bundleName; std::string moduleName; GetJsMediaBundleInfo(previewUriValue, bundleName, moduleName); if (previewUriValue->IsUndefined() || previewUriValue->IsNull()) { // When it is undefined, just set the empty image. VideoModel::GetInstance()->SetPosterSourceInfo(previewUri, "", ""); return; } auto noPixMap = ParseJsMedia(previewUriValue, previewUri); if (noPixMap) { // Src is a string or resource VideoModel::GetInstance()->SetPosterSourceInfo(previewUri, bundleName, moduleName); } else { // Src is a pixelmap. #if defined(PIXEL_MAP_SUPPORTED) RefPtr pixMap = CreatePixelMapFromNapiValue(previewUriValue); VideoModel::GetInstance()->SetPosterSourceByPixelMap(pixMap); #endif } } void JSVideo::ParseImageAIOptions(const JSRef& jsValue) { auto engine = EngineHelper::GetCurrentEngine(); CHECK_NULL_VOID(engine); NativeEngine* nativeEngine = engine->GetNativeEngine(); CHECK_NULL_VOID(nativeEngine); panda::Local value = jsValue.Get().GetLocalHandle(); JSValueWrapper valueWrapper = value; ScopeRAII scope(reinterpret_cast(nativeEngine)); napi_value optionsValue = nativeEngine->ValueToNapiValue(valueWrapper); VideoModel::GetInstance()->SetImageAIOptions(optionsValue); } void JSVideo::JsMuted(const JSCallbackInfo& info) { bool muted = false; if (info[0]->IsBoolean()) { muted = info[0]->ToBoolean(); #ifdef SUPPORT_JSSTACK HiviewDFX::ReportXPowerJsStackSysEvent(info.GetVm(), "VOLUME_CHANGE", "SRC=Video"); #endif } VideoModel::GetInstance()->SetMuted(muted); } void JSVideo::JsAutoPlay(const JSCallbackInfo& info) { bool autoPlay = false; if (info[0]->IsBoolean()) { autoPlay = info[0]->ToBoolean(); #ifdef SUPPORT_JSSTACK HiviewDFX::ReportXPowerJsStackSysEvent(info.GetVm(), "STREAM_CHANGE", "SRC=Video"); #endif } VideoModel::GetInstance()->SetAutoPlay(autoPlay); } void JSVideo::JsControls(const JSCallbackInfo& info) { bool controls = true; if (info[0]->IsBoolean()) { controls = info[0]->ToBoolean(); } VideoModel::GetInstance()->SetControls(controls); } void JSVideo::JsLoop(const JSCallbackInfo& info) { bool loop = false; if (info[0]->IsBoolean()) { loop = info[0]->ToBoolean(); } VideoModel::GetInstance()->SetLoop(loop); } void JSVideo::JsObjectFit(const JSCallbackInfo& info) { ImageFit imageFit = ImageFit::COVER; // The default value of Imagefit is FILL, but in the video the default value is COVER. // So the default value need to be converted. if (info[0]->IsUndefined()) { VideoModel::GetInstance()->SetObjectFit(imageFit); return; } if (info[0]->IsNumber()) { imageFit = static_cast(info[0]->ToNumber()); } VideoModel::GetInstance()->SetObjectFit(imageFit); } void JSVideo::JsOnStart(const JSCallbackInfo& info) { if (!info[0]->IsFunction()) { return; } auto jsFunc = AceType::MakeRefPtr(JSRef(), JSRef::Cast(info[0])); WeakPtr targetNode = AceType::WeakClaim(NG::ViewStackProcessor::GetInstance()->GetMainFrameNode()); auto onStart = [execCtx = info.GetExecutionContext(), func = std::move(jsFunc), node = targetNode]( const std::string& param) { JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx); ACE_SCORING_EVENT("Video.onStart"); PipelineContext::SetCallBackNode(node); std::vector keys = { "start" }; func->Execute(keys, param); }; VideoModel::GetInstance()->SetOnStart(std::move(onStart)); } void JSVideo::JsOnPause(const JSCallbackInfo& info) { if (!info[0]->IsFunction()) { return; } auto jsFunc = AceType::MakeRefPtr(JSRef(), JSRef::Cast(info[0])); WeakPtr targetNode = AceType::WeakClaim(NG::ViewStackProcessor::GetInstance()->GetMainFrameNode()); auto onPause = [execCtx = info.GetExecutionContext(), func = std::move(jsFunc), node = targetNode]( const std::string& param) { JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx); ACE_SCORING_EVENT("Video.onPause"); PipelineContext::SetCallBackNode(node); std::vector keys = { "pause" }; func->Execute(keys, param); }; VideoModel::GetInstance()->SetOnPause(std::move(onPause)); } void JSVideo::JsOnFinish(const JSCallbackInfo& info) { if (!info[0]->IsFunction()) { return; } auto jsFunc = AceType::MakeRefPtr(JSRef(), JSRef::Cast(info[0])); WeakPtr targetNode = AceType::WeakClaim(NG::ViewStackProcessor::GetInstance()->GetMainFrameNode()); auto onFinish = [execCtx = info.GetExecutionContext(), func = std::move(jsFunc), node = targetNode]( const std::string& param) { JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx); ACE_SCORING_EVENT("Video.onFinish"); PipelineContext::SetCallBackNode(node); std::vector keys = { "finish" }; func->Execute(keys, param); }; VideoModel::GetInstance()->SetOnFinish(std::move(onFinish)); } void JSVideo::JsOnStop(const JSCallbackInfo& info) { if (!info[0]->IsFunction()) { return; } auto jsFunc = AceType::MakeRefPtr(JSRef(), JSRef::Cast(info[0])); auto targetNode = AceType::WeakClaim(NG::ViewStackProcessor::GetInstance()->GetMainFrameNode()); auto onStop = [execCtx = info.GetExecutionContext(), func = std::move(jsFunc), node = targetNode]( const std::string& param) { JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx); ACE_SCORING_EVENT("Video.onStop"); PipelineContext::SetCallBackNode(node); std::vector keys = { "stop" }; func->Execute(keys, param); }; VideoModel::GetInstance()->SetOnStop(std::move(onStop)); } void JSVideo::JsOnFullscreenChange(const JSCallbackInfo& info) { if (!info[0]->IsFunction()) { return; } auto jsFunc = AceType::MakeRefPtr(JSRef(), JSRef::Cast(info[0])); WeakPtr targetNode = AceType::WeakClaim(NG::ViewStackProcessor::GetInstance()->GetMainFrameNode()); auto OnFullScreenChange = [execCtx = info.GetExecutionContext(), func = std::move(jsFunc), node = targetNode]( const std::string& param) { JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx); ACE_SCORING_EVENT("Video.OnFullScreenChange"); PipelineContext::SetCallBackNode(node); std::vector keys = { "fullscreen" }; func->Execute(keys, param); }; VideoModel::GetInstance()->SetOnFullScreenChange(std::move(OnFullScreenChange)); } void JSVideo::JsOnPrepared(const JSCallbackInfo& info) { if (!info[0]->IsFunction()) { return; } auto jsFunc = AceType::MakeRefPtr(JSRef(), JSRef::Cast(info[0])); WeakPtr targetNode = AceType::WeakClaim(NG::ViewStackProcessor::GetInstance()->GetMainFrameNode()); auto onPrepared = [execCtx = info.GetExecutionContext(), func = std::move(jsFunc), node = targetNode]( const std::string& param) { JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx); ACE_SCORING_EVENT("Video.onPrepared"); PipelineContext::SetCallBackNode(node); std::vector keys = { "duration" }; func->Execute(keys, param); }; VideoModel::GetInstance()->SetOnPrepared(std::move(onPrepared)); } void JSVideo::JsOnSeeking(const JSCallbackInfo& info) { if (!info[0]->IsFunction()) { return; } auto jsFunc = AceType::MakeRefPtr(JSRef(), JSRef::Cast(info[0])); WeakPtr targetNode = AceType::WeakClaim(NG::ViewStackProcessor::GetInstance()->GetMainFrameNode()); auto onSeeking = [execCtx = info.GetExecutionContext(), func = std::move(jsFunc), node = targetNode]( const std::string& param) { JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx); ACE_SCORING_EVENT("Video.onSeeking"); PipelineContext::SetCallBackNode(node); std::vector keys = { "time" }; func->Execute(keys, param); }; VideoModel::GetInstance()->SetOnSeeking(std::move(onSeeking)); } void JSVideo::JsOnSeeked(const JSCallbackInfo& info) { if (!info[0]->IsFunction()) { return; } auto jsFunc = AceType::MakeRefPtr(JSRef(), JSRef::Cast(info[0])); WeakPtr targetNode = AceType::WeakClaim(NG::ViewStackProcessor::GetInstance()->GetMainFrameNode()); auto onSeeked = [execCtx = info.GetExecutionContext(), func = std::move(jsFunc), node = targetNode]( const std::string& param) { JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx); ACE_SCORING_EVENT("Video.onSeeked"); PipelineContext::SetCallBackNode(node); std::vector keys = { "time" }; func->Execute(keys, param); }; VideoModel::GetInstance()->SetOnSeeked(std::move(onSeeked)); } void JSVideo::JsOnUpdate(const JSCallbackInfo& info) { if (!info[0]->IsFunction()) { return; } auto jsFunc = AceType::MakeRefPtr(JSRef(), JSRef::Cast(info[0])); WeakPtr targetNode = AceType::WeakClaim(NG::ViewStackProcessor::GetInstance()->GetMainFrameNode()); auto onUpdate = [execCtx = info.GetExecutionContext(), func = std::move(jsFunc), node = targetNode]( const std::string& param) { JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx); ACE_SCORING_EVENT("Video.onUpdate"); PipelineContext::SetCallBackNode(node); std::vector keys = { "time" }; func->Execute(keys, param); }; VideoModel::GetInstance()->SetOnUpdate(std::move(onUpdate)); } void JSVideo::JsOnError(const JSCallbackInfo& info) { if (!info[0]->IsFunction()) { return; } auto jsFunc = AceType::MakeRefPtr(JSRef(), JSRef::Cast(info[0])); WeakPtr targetNode = AceType::WeakClaim(NG::ViewStackProcessor::GetInstance()->GetMainFrameNode()); auto onError = [execCtx = info.GetExecutionContext(), func = std::move(jsFunc), node = targetNode]( const std::string& param) { JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx); ACE_SCORING_EVENT("Video.onError"); PipelineContext::SetCallBackNode(node); std::vector keys = { "error" }; func->Execute(keys, param); }; VideoModel::GetInstance()->SetOnError(std::move(onError)); } EventMarker JSVideo::GetEventMarker(const JSCallbackInfo& info, const std::vector& keys) { if (!info[0]->IsFunction()) { return EventMarker(); } RefPtr jsFunc = AceType::MakeRefPtr(JSRef(), JSRef::Cast(info[0])); WeakPtr targetNode = AceType::WeakClaim(NG::ViewStackProcessor::GetInstance()->GetMainFrameNode()); auto eventMarker = EventMarker([execCtx = info.GetExecutionContext(), func = std::move(jsFunc), keys, node = targetNode](const std::string& param) { JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx); PipelineContext::SetCallBackNode(node); func->Execute(keys, param); }); return eventMarker; } void JSVideo::EnableAnalyzer(bool enable) { VideoModel::GetInstance()->EnableAnalyzer(enable); } void JSVideo::AnalyzerConfig(const JSCallbackInfo& info) { auto configParams = info[0]; if (configParams->IsNull() || !configParams->IsObject()) { return; } auto engine = EngineHelper::GetCurrentEngine(); CHECK_NULL_VOID(engine); NativeEngine* nativeEngine = engine->GetNativeEngine(); panda::Local value = configParams.Get().GetLocalHandle(); JSValueWrapper valueWrapper = value; ScopeRAII scope(reinterpret_cast(nativeEngine)); napi_value nativeValue = nativeEngine->ValueToNapiValue(valueWrapper); VideoModel::GetInstance()->SetImageAnalyzerConfig(nativeValue); } void JSVideo::JSBind(BindingTarget globalObj) { JSClass::Declare("Video"); MethodOptions opt = MethodOptions::NONE; JSClass::StaticMethod("create", &JSVideo::Create, opt); JSClass::StaticMethod("muted", &JSVideo::JsMuted, opt); JSClass::StaticMethod("autoPlay", &JSVideo::JsAutoPlay, opt); JSClass::StaticMethod("controls", &JSVideo::JsControls, opt); JSClass::StaticMethod("loop", &JSVideo::JsLoop, opt); JSClass::StaticMethod("objectFit", &JSVideo::JsObjectFit, opt); JSClass::StaticMethod("onStart", &JSVideo::JsOnStart); JSClass::StaticMethod("onPause", &JSVideo::JsOnPause); JSClass::StaticMethod("onFinish", &JSVideo::JsOnFinish); JSClass::StaticMethod("onFullscreenChange", &JSVideo::JsOnFullscreenChange); JSClass::StaticMethod("onPrepared", &JSVideo::JsOnPrepared); JSClass::StaticMethod("onSeeking", &JSVideo::JsOnSeeking); JSClass::StaticMethod("onSeeked", &JSVideo::JsOnSeeked); JSClass::StaticMethod("onUpdate", &JSVideo::JsOnUpdate); JSClass::StaticMethod("onError", &JSVideo::JsOnError); JSClass::StaticMethod("onStop", &JSVideo::JsOnStop); JSClass::StaticMethod("enableAnalyzer", &JSVideo::EnableAnalyzer); JSClass::StaticMethod("analyzerConfig", &JSVideo::AnalyzerConfig); JSClass::StaticMethod("onTouch", &JSInteractableView::JsOnTouch); JSClass::StaticMethod("onHover", &JSInteractableView::JsOnHover); JSClass::StaticMethod("onKeyEvent", &JSInteractableView::JsOnKey); JSClass::StaticMethod("onDeleteEvent", &JSInteractableView::JsOnDelete); JSClass::StaticMethod("onClick", &JSInteractableView::JsOnClick); JSClass::StaticMethod("onAttach", &JSInteractableView::JsOnAttach); JSClass::StaticMethod("onAppear", &JSInteractableView::JsOnAppear); JSClass::StaticMethod("onDetach", &JSInteractableView::JsOnDetach); JSClass::StaticMethod("onDisAppear", &JSInteractableView::JsOnDisAppear); JSClass::StaticMethod("remoteMessage", &JSInteractableView::JsCommonRemoteMessage); // override method JSClass::StaticMethod("opacity", &JSViewAbstract::JsOpacityPassThrough); JSClass::StaticMethod("transition", &JSViewAbstract::JsTransitionPassThrough); JSClass::InheritAndBind(globalObj); } } // namespace OHOS::Ace::Framework