1 /*
2  * Copyright (c) 2021-2024 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include "frameworks/bridge/declarative_frontend/jsview/js_video.h"
17 
18 #include "base/log/ace_scoring_log.h"
19 #include "bridge/common/utils/engine_helper.h"
20 #include "bridge/declarative_frontend/jsview/js_utils.h"
21 #include "bridge/declarative_frontend/jsview/js_video_controller.h"
22 #include "bridge/declarative_frontend/jsview/models/video_model_impl.h"
23 #include "core/components_ng/base/view_stack_processor.h"
24 #include "core/components_ng/pattern/video/video_model_ng.h"
25 #ifdef SUPPORT_JSSTACK
26 #include "xpower_event_jsvm.h"
27 #endif
28 
29 namespace OHOS::Ace {
30 
31 std::unique_ptr<VideoModel> VideoModel::instance_ = nullptr;
32 std::mutex VideoModel::mutex_;
33 
GetInstance()34 VideoModel* VideoModel::GetInstance()
35 {
36     if (!instance_) {
37         std::lock_guard<std::mutex> lock(mutex_);
38         if (!instance_) {
39 #ifdef NG_BUILD
40             instance_.reset(new NG::VideoModelNG());
41 #else
42             if (Container::IsCurrentUseNewPipeline()) {
43                 instance_.reset(new NG::VideoModelNG());
44             } else {
45                 instance_.reset(new Framework::VideoModelImpl());
46             }
47 #endif
48         }
49     }
50     return instance_.get();
51 }
52 
53 } // namespace OHOS::Ace
54 
55 namespace OHOS::Ace::Framework {
56 
Create(const JSCallbackInfo & info)57 void JSVideo::Create(const JSCallbackInfo& info)
58 {
59     if (!info[0]->IsObject()) {
60         return;
61     }
62     JSRef<JSObject> videoObj = JSRef<JSObject>::Cast(info[0]);
63     JSRef<JSVal> srcValue = videoObj->GetProperty("src");
64     JSRef<JSVal> previewUriValue = videoObj->GetProperty("previewUri");
65     JSRef<JSVal> currentProgressRateValue = videoObj->GetProperty("currentProgressRate");
66 
67     auto controllerObj = videoObj->GetProperty("controller");
68     RefPtr<VideoControllerV2> videoController = nullptr;
69     if (controllerObj->IsObject()) {
70         auto* jsVideoController = JSRef<JSObject>::Cast(controllerObj)->Unwrap<JSVideoController>();
71         if (jsVideoController) {
72             jsVideoController->SetInstanceId(Container::CurrentId());
73             videoController = jsVideoController->GetController();
74         }
75     }
76     VideoModel::GetInstance()->Create(videoController);
77 
78     // Parse the src, if it is invalid, use the empty string.
79     std::string bundleNameSrc;
80     std::string moduleNameSrc;
81     std::string src;
82     int32_t resId = 0;
83     ParseJsMediaWithBundleName(srcValue, src, bundleNameSrc, moduleNameSrc, resId);
84     VideoModel::GetInstance()->SetSrc(src, bundleNameSrc, moduleNameSrc);
85 
86     // Parse the rate, if it is invalid, set it as 1.0.
87     double currentProgressRate = 1.0;
88     ParseJsDouble(currentProgressRateValue, currentProgressRate);
89     VideoModel::GetInstance()->SetProgressRate(currentProgressRate);
90 
91     auto aiOptions = videoObj->GetProperty("imageAIOptions");
92     if (aiOptions->IsObject()) {
93         ParseImageAIOptions(aiOptions);
94     }
95 
96     std::string previewUri;
97     std::string bundleName;
98     std::string moduleName;
99     GetJsMediaBundleInfo(previewUriValue, bundleName, moduleName);
100     if (previewUriValue->IsUndefined() || previewUriValue->IsNull()) {
101         // When it is undefined, just set the empty image.
102         VideoModel::GetInstance()->SetPosterSourceInfo(previewUri, "", "");
103         return;
104     }
105     auto noPixMap = ParseJsMedia(previewUriValue, previewUri);
106     if (noPixMap) {
107         // Src is a string or resource
108         VideoModel::GetInstance()->SetPosterSourceInfo(previewUri, bundleName, moduleName);
109     } else {
110         // Src is a pixelmap.
111 #if defined(PIXEL_MAP_SUPPORTED)
112         RefPtr<PixelMap> pixMap = CreatePixelMapFromNapiValue(previewUriValue);
113         VideoModel::GetInstance()->SetPosterSourceByPixelMap(pixMap);
114 #endif
115     }
116 }
117 
ParseImageAIOptions(const JSRef<JSVal> & jsValue)118 void JSVideo::ParseImageAIOptions(const JSRef<JSVal>& jsValue)
119 {
120     auto engine = EngineHelper::GetCurrentEngine();
121     CHECK_NULL_VOID(engine);
122     NativeEngine* nativeEngine = engine->GetNativeEngine();
123     CHECK_NULL_VOID(nativeEngine);
124     panda::Local<JsiValue> value = jsValue.Get().GetLocalHandle();
125     JSValueWrapper valueWrapper = value;
126     ScopeRAII scope(reinterpret_cast<napi_env>(nativeEngine));
127     napi_value optionsValue = nativeEngine->ValueToNapiValue(valueWrapper);
128     VideoModel::GetInstance()->SetImageAIOptions(optionsValue);
129 }
130 
JsMuted(const JSCallbackInfo & info)131 void JSVideo::JsMuted(const JSCallbackInfo& info)
132 {
133     bool muted = false;
134     if (info[0]->IsBoolean()) {
135         muted = info[0]->ToBoolean();
136 #ifdef SUPPORT_JSSTACK
137         HiviewDFX::ReportXPowerJsStackSysEvent(info.GetVm(), "VOLUME_CHANGE", "SRC=Video");
138 #endif
139     }
140     VideoModel::GetInstance()->SetMuted(muted);
141 }
142 
JsAutoPlay(const JSCallbackInfo & info)143 void JSVideo::JsAutoPlay(const JSCallbackInfo& info)
144 {
145     bool autoPlay = false;
146     if (info[0]->IsBoolean()) {
147         autoPlay = info[0]->ToBoolean();
148 #ifdef SUPPORT_JSSTACK
149         HiviewDFX::ReportXPowerJsStackSysEvent(info.GetVm(), "STREAM_CHANGE", "SRC=Video");
150 #endif
151     }
152     VideoModel::GetInstance()->SetAutoPlay(autoPlay);
153 }
154 
JsControls(const JSCallbackInfo & info)155 void JSVideo::JsControls(const JSCallbackInfo& info)
156 {
157     bool controls = true;
158     if (info[0]->IsBoolean()) {
159         controls = info[0]->ToBoolean();
160     }
161     VideoModel::GetInstance()->SetControls(controls);
162 }
163 
JsLoop(const JSCallbackInfo & info)164 void JSVideo::JsLoop(const JSCallbackInfo& info)
165 {
166     bool loop = false;
167     if (info[0]->IsBoolean()) {
168         loop = info[0]->ToBoolean();
169     }
170     VideoModel::GetInstance()->SetLoop(loop);
171 }
172 
JsObjectFit(const JSCallbackInfo & info)173 void JSVideo::JsObjectFit(const JSCallbackInfo& info)
174 {
175     ImageFit imageFit = ImageFit::COVER;
176     // The default value of Imagefit is FILL, but in the video the default value is COVER.
177     // So the default value need to be converted.
178     if (info[0]->IsUndefined()) {
179         VideoModel::GetInstance()->SetObjectFit(imageFit);
180         return;
181     }
182     if (info[0]->IsNumber()) {
183         imageFit = static_cast<ImageFit>(info[0]->ToNumber<int>());
184     }
185     VideoModel::GetInstance()->SetObjectFit(imageFit);
186 }
187 
JsOnStart(const JSCallbackInfo & info)188 void JSVideo::JsOnStart(const JSCallbackInfo& info)
189 {
190     if (!info[0]->IsFunction()) {
191         return;
192     }
193     auto jsFunc = AceType::MakeRefPtr<JsFunction>(JSRef<JSObject>(), JSRef<JSFunc>::Cast(info[0]));
194     WeakPtr<NG::FrameNode> targetNode = AceType::WeakClaim(NG::ViewStackProcessor::GetInstance()->GetMainFrameNode());
195     auto onStart = [execCtx = info.GetExecutionContext(), func = std::move(jsFunc), node = targetNode](
196                        const std::string& param) {
197         JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
198         ACE_SCORING_EVENT("Video.onStart");
199         PipelineContext::SetCallBackNode(node);
200         std::vector<std::string> keys = { "start" };
201         func->Execute(keys, param);
202     };
203     VideoModel::GetInstance()->SetOnStart(std::move(onStart));
204 }
205 
JsOnPause(const JSCallbackInfo & info)206 void JSVideo::JsOnPause(const JSCallbackInfo& info)
207 {
208     if (!info[0]->IsFunction()) {
209         return;
210     }
211     auto jsFunc = AceType::MakeRefPtr<JsFunction>(JSRef<JSObject>(), JSRef<JSFunc>::Cast(info[0]));
212     WeakPtr<NG::FrameNode> targetNode = AceType::WeakClaim(NG::ViewStackProcessor::GetInstance()->GetMainFrameNode());
213     auto onPause = [execCtx = info.GetExecutionContext(), func = std::move(jsFunc), node = targetNode](
214                        const std::string& param) {
215         JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
216         ACE_SCORING_EVENT("Video.onPause");
217         PipelineContext::SetCallBackNode(node);
218         std::vector<std::string> keys = { "pause" };
219         func->Execute(keys, param);
220     };
221     VideoModel::GetInstance()->SetOnPause(std::move(onPause));
222 }
223 
JsOnFinish(const JSCallbackInfo & info)224 void JSVideo::JsOnFinish(const JSCallbackInfo& info)
225 {
226     if (!info[0]->IsFunction()) {
227         return;
228     }
229     auto jsFunc = AceType::MakeRefPtr<JsFunction>(JSRef<JSObject>(), JSRef<JSFunc>::Cast(info[0]));
230     WeakPtr<NG::FrameNode> targetNode = AceType::WeakClaim(NG::ViewStackProcessor::GetInstance()->GetMainFrameNode());
231     auto onFinish = [execCtx = info.GetExecutionContext(), func = std::move(jsFunc), node = targetNode](
232                         const std::string& param) {
233         JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
234         ACE_SCORING_EVENT("Video.onFinish");
235         PipelineContext::SetCallBackNode(node);
236         std::vector<std::string> keys = { "finish" };
237         func->Execute(keys, param);
238     };
239     VideoModel::GetInstance()->SetOnFinish(std::move(onFinish));
240 }
241 
JsOnStop(const JSCallbackInfo & info)242 void JSVideo::JsOnStop(const JSCallbackInfo& info)
243 {
244     if (!info[0]->IsFunction()) {
245         return;
246     }
247     auto jsFunc = AceType::MakeRefPtr<JsFunction>(JSRef<JSObject>(), JSRef<JSFunc>::Cast(info[0]));
248     auto targetNode = AceType::WeakClaim(NG::ViewStackProcessor::GetInstance()->GetMainFrameNode());
249     auto onStop = [execCtx = info.GetExecutionContext(), func = std::move(jsFunc), node = targetNode](
250                         const std::string& param) {
251         JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
252         ACE_SCORING_EVENT("Video.onStop");
253         PipelineContext::SetCallBackNode(node);
254         std::vector<std::string> keys = { "stop" };
255         func->Execute(keys, param);
256     };
257     VideoModel::GetInstance()->SetOnStop(std::move(onStop));
258 }
259 
JsOnFullscreenChange(const JSCallbackInfo & info)260 void JSVideo::JsOnFullscreenChange(const JSCallbackInfo& info)
261 {
262     if (!info[0]->IsFunction()) {
263         return;
264     }
265     auto jsFunc = AceType::MakeRefPtr<JsFunction>(JSRef<JSObject>(), JSRef<JSFunc>::Cast(info[0]));
266     WeakPtr<NG::FrameNode> targetNode = AceType::WeakClaim(NG::ViewStackProcessor::GetInstance()->GetMainFrameNode());
267     auto OnFullScreenChange = [execCtx = info.GetExecutionContext(), func = std::move(jsFunc), node = targetNode](
268                                   const std::string& param) {
269         JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
270         ACE_SCORING_EVENT("Video.OnFullScreenChange");
271         PipelineContext::SetCallBackNode(node);
272         std::vector<std::string> keys = { "fullscreen" };
273         func->Execute(keys, param);
274     };
275     VideoModel::GetInstance()->SetOnFullScreenChange(std::move(OnFullScreenChange));
276 }
277 
JsOnPrepared(const JSCallbackInfo & info)278 void JSVideo::JsOnPrepared(const JSCallbackInfo& info)
279 {
280     if (!info[0]->IsFunction()) {
281         return;
282     }
283     auto jsFunc = AceType::MakeRefPtr<JsFunction>(JSRef<JSObject>(), JSRef<JSFunc>::Cast(info[0]));
284     WeakPtr<NG::FrameNode> targetNode = AceType::WeakClaim(NG::ViewStackProcessor::GetInstance()->GetMainFrameNode());
285     auto onPrepared = [execCtx = info.GetExecutionContext(), func = std::move(jsFunc), node = targetNode](
286                           const std::string& param) {
287         JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
288         ACE_SCORING_EVENT("Video.onPrepared");
289         PipelineContext::SetCallBackNode(node);
290         std::vector<std::string> keys = { "duration" };
291         func->Execute(keys, param);
292     };
293     VideoModel::GetInstance()->SetOnPrepared(std::move(onPrepared));
294 }
295 
JsOnSeeking(const JSCallbackInfo & info)296 void JSVideo::JsOnSeeking(const JSCallbackInfo& info)
297 {
298     if (!info[0]->IsFunction()) {
299         return;
300     }
301     auto jsFunc = AceType::MakeRefPtr<JsFunction>(JSRef<JSObject>(), JSRef<JSFunc>::Cast(info[0]));
302     WeakPtr<NG::FrameNode> targetNode = AceType::WeakClaim(NG::ViewStackProcessor::GetInstance()->GetMainFrameNode());
303     auto onSeeking = [execCtx = info.GetExecutionContext(), func = std::move(jsFunc), node = targetNode](
304                          const std::string& param) {
305         JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
306         ACE_SCORING_EVENT("Video.onSeeking");
307         PipelineContext::SetCallBackNode(node);
308         std::vector<std::string> keys = { "time" };
309         func->Execute(keys, param);
310     };
311     VideoModel::GetInstance()->SetOnSeeking(std::move(onSeeking));
312 }
313 
JsOnSeeked(const JSCallbackInfo & info)314 void JSVideo::JsOnSeeked(const JSCallbackInfo& info)
315 {
316     if (!info[0]->IsFunction()) {
317         return;
318     }
319     auto jsFunc = AceType::MakeRefPtr<JsFunction>(JSRef<JSObject>(), JSRef<JSFunc>::Cast(info[0]));
320     WeakPtr<NG::FrameNode> targetNode = AceType::WeakClaim(NG::ViewStackProcessor::GetInstance()->GetMainFrameNode());
321     auto onSeeked = [execCtx = info.GetExecutionContext(), func = std::move(jsFunc), node = targetNode](
322                         const std::string& param) {
323         JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
324         ACE_SCORING_EVENT("Video.onSeeked");
325         PipelineContext::SetCallBackNode(node);
326         std::vector<std::string> keys = { "time" };
327         func->Execute(keys, param);
328     };
329     VideoModel::GetInstance()->SetOnSeeked(std::move(onSeeked));
330 }
331 
JsOnUpdate(const JSCallbackInfo & info)332 void JSVideo::JsOnUpdate(const JSCallbackInfo& info)
333 {
334     if (!info[0]->IsFunction()) {
335         return;
336     }
337     auto jsFunc = AceType::MakeRefPtr<JsFunction>(JSRef<JSObject>(), JSRef<JSFunc>::Cast(info[0]));
338     WeakPtr<NG::FrameNode> targetNode = AceType::WeakClaim(NG::ViewStackProcessor::GetInstance()->GetMainFrameNode());
339     auto onUpdate = [execCtx = info.GetExecutionContext(), func = std::move(jsFunc), node = targetNode](
340                         const std::string& param) {
341         JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
342         ACE_SCORING_EVENT("Video.onUpdate");
343         PipelineContext::SetCallBackNode(node);
344         std::vector<std::string> keys = { "time" };
345         func->Execute(keys, param);
346     };
347     VideoModel::GetInstance()->SetOnUpdate(std::move(onUpdate));
348 }
349 
JsOnError(const JSCallbackInfo & info)350 void JSVideo::JsOnError(const JSCallbackInfo& info)
351 {
352     if (!info[0]->IsFunction()) {
353         return;
354     }
355     auto jsFunc = AceType::MakeRefPtr<JsFunction>(JSRef<JSObject>(), JSRef<JSFunc>::Cast(info[0]));
356     WeakPtr<NG::FrameNode> targetNode = AceType::WeakClaim(NG::ViewStackProcessor::GetInstance()->GetMainFrameNode());
357     auto onError = [execCtx = info.GetExecutionContext(), func = std::move(jsFunc), node = targetNode](
358                        const std::string& param) {
359         JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
360         ACE_SCORING_EVENT("Video.onError");
361         PipelineContext::SetCallBackNode(node);
362         std::vector<std::string> keys = { "error" };
363         func->Execute(keys, param);
364     };
365     VideoModel::GetInstance()->SetOnError(std::move(onError));
366 }
367 
GetEventMarker(const JSCallbackInfo & info,const std::vector<std::string> & keys)368 EventMarker JSVideo::GetEventMarker(const JSCallbackInfo& info, const std::vector<std::string>& keys)
369 {
370     if (!info[0]->IsFunction()) {
371         return EventMarker();
372     }
373 
374     RefPtr<JsFunction> jsFunc = AceType::MakeRefPtr<JsFunction>(JSRef<JSObject>(), JSRef<JSFunc>::Cast(info[0]));
375     WeakPtr<NG::FrameNode> targetNode = AceType::WeakClaim(NG::ViewStackProcessor::GetInstance()->GetMainFrameNode());
376     auto eventMarker = EventMarker([execCtx = info.GetExecutionContext(), func = std::move(jsFunc), keys,
377                                        node = targetNode](const std::string& param) {
378         JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
379         PipelineContext::SetCallBackNode(node);
380         func->Execute(keys, param);
381     });
382     return eventMarker;
383 }
384 
EnableAnalyzer(bool enable)385 void JSVideo::EnableAnalyzer(bool enable)
386 {
387     VideoModel::GetInstance()->EnableAnalyzer(enable);
388 }
389 
AnalyzerConfig(const JSCallbackInfo & info)390 void JSVideo::AnalyzerConfig(const JSCallbackInfo& info)
391 {
392     auto configParams = info[0];
393     if (configParams->IsNull() || !configParams->IsObject()) {
394         return;
395     }
396     auto engine = EngineHelper::GetCurrentEngine();
397     CHECK_NULL_VOID(engine);
398     NativeEngine* nativeEngine = engine->GetNativeEngine();
399     panda::Local<JsiValue> value = configParams.Get().GetLocalHandle();
400     JSValueWrapper valueWrapper = value;
401     ScopeRAII scope(reinterpret_cast<napi_env>(nativeEngine));
402     napi_value nativeValue = nativeEngine->ValueToNapiValue(valueWrapper);
403     VideoModel::GetInstance()->SetImageAnalyzerConfig(nativeValue);
404 }
405 
JSBind(BindingTarget globalObj)406 void JSVideo::JSBind(BindingTarget globalObj)
407 {
408     JSClass<JSVideo>::Declare("Video");
409     MethodOptions opt = MethodOptions::NONE;
410     JSClass<JSVideo>::StaticMethod("create", &JSVideo::Create, opt);
411     JSClass<JSVideo>::StaticMethod("muted", &JSVideo::JsMuted, opt);
412     JSClass<JSVideo>::StaticMethod("autoPlay", &JSVideo::JsAutoPlay, opt);
413     JSClass<JSVideo>::StaticMethod("controls", &JSVideo::JsControls, opt);
414     JSClass<JSVideo>::StaticMethod("loop", &JSVideo::JsLoop, opt);
415     JSClass<JSVideo>::StaticMethod("objectFit", &JSVideo::JsObjectFit, opt);
416 
417     JSClass<JSVideo>::StaticMethod("onStart", &JSVideo::JsOnStart);
418     JSClass<JSVideo>::StaticMethod("onPause", &JSVideo::JsOnPause);
419     JSClass<JSVideo>::StaticMethod("onFinish", &JSVideo::JsOnFinish);
420     JSClass<JSVideo>::StaticMethod("onFullscreenChange", &JSVideo::JsOnFullscreenChange);
421     JSClass<JSVideo>::StaticMethod("onPrepared", &JSVideo::JsOnPrepared);
422     JSClass<JSVideo>::StaticMethod("onSeeking", &JSVideo::JsOnSeeking);
423     JSClass<JSVideo>::StaticMethod("onSeeked", &JSVideo::JsOnSeeked);
424     JSClass<JSVideo>::StaticMethod("onUpdate", &JSVideo::JsOnUpdate);
425     JSClass<JSVideo>::StaticMethod("onError", &JSVideo::JsOnError);
426     JSClass<JSVideo>::StaticMethod("onStop", &JSVideo::JsOnStop);
427     JSClass<JSVideo>::StaticMethod("enableAnalyzer", &JSVideo::EnableAnalyzer);
428     JSClass<JSVideo>::StaticMethod("analyzerConfig", &JSVideo::AnalyzerConfig);
429 
430     JSClass<JSVideo>::StaticMethod("onTouch", &JSInteractableView::JsOnTouch);
431     JSClass<JSVideo>::StaticMethod("onHover", &JSInteractableView::JsOnHover);
432     JSClass<JSVideo>::StaticMethod("onKeyEvent", &JSInteractableView::JsOnKey);
433     JSClass<JSVideo>::StaticMethod("onDeleteEvent", &JSInteractableView::JsOnDelete);
434     JSClass<JSVideo>::StaticMethod("onClick", &JSInteractableView::JsOnClick);
435     JSClass<JSVideo>::StaticMethod("onAttach", &JSInteractableView::JsOnAttach);
436     JSClass<JSVideo>::StaticMethod("onAppear", &JSInteractableView::JsOnAppear);
437     JSClass<JSVideo>::StaticMethod("onDetach", &JSInteractableView::JsOnDetach);
438     JSClass<JSVideo>::StaticMethod("onDisAppear", &JSInteractableView::JsOnDisAppear);
439     JSClass<JSVideo>::StaticMethod("remoteMessage", &JSInteractableView::JsCommonRemoteMessage);
440     // override method
441     JSClass<JSVideo>::StaticMethod("opacity", &JSViewAbstract::JsOpacityPassThrough);
442     JSClass<JSVideo>::StaticMethod("transition", &JSViewAbstract::JsTransitionPassThrough);
443     JSClass<JSVideo>::InheritAndBind<JSViewAbstract>(globalObj);
444 }
445 
446 } // namespace OHOS::Ace::Framework
447