1 /*
2 * Copyright (c) 2021-2023 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 videoSrc;
80 ParseJsMedia(srcValue, videoSrc);
81 VideoModel::GetInstance()->SetSrc(videoSrc);
82
83 // Parse the rate, if it is invalid, set it as 1.0.
84 double currentProgressRate = 1.0;
85 ParseJsDouble(currentProgressRateValue, currentProgressRate);
86 VideoModel::GetInstance()->SetProgressRate(currentProgressRate);
87
88 auto aiOptions = videoObj->GetProperty("imageAIOptions");
89 if (aiOptions->IsObject()) {
90 ParseImageAIOptions(aiOptions);
91 }
92
93 std::string previewUri;
94 std::string bundleName;
95 std::string moduleName;
96 GetJsMediaBundleInfo(previewUriValue, bundleName, moduleName);
97 if (previewUriValue->IsUndefined() || previewUriValue->IsNull()) {
98 // When it is undefined, just set the empty image.
99 VideoModel::GetInstance()->SetPosterSourceInfo(previewUri, "", "");
100 return;
101 }
102 auto noPixMap = ParseJsMedia(previewUriValue, previewUri);
103 if (noPixMap) {
104 // Src is a string or resource
105 VideoModel::GetInstance()->SetPosterSourceInfo(previewUri, bundleName, moduleName);
106 } else {
107 // Src is a pixelmap.
108 #if defined(PIXEL_MAP_SUPPORTED)
109 RefPtr<PixelMap> pixMap = CreatePixelMapFromNapiValue(previewUriValue);
110 VideoModel::GetInstance()->SetPosterSourceByPixelMap(pixMap);
111 #endif
112 }
113 }
114
ParseImageAIOptions(const JSRef<JSVal> & jsValue)115 void JSVideo::ParseImageAIOptions(const JSRef<JSVal>& jsValue)
116 {
117 auto engine = EngineHelper::GetCurrentEngine();
118 CHECK_NULL_VOID(engine);
119 NativeEngine* nativeEngine = engine->GetNativeEngine();
120 CHECK_NULL_VOID(nativeEngine);
121 panda::Local<JsiValue> value = jsValue.Get().GetLocalHandle();
122 JSValueWrapper valueWrapper = value;
123 ScopeRAII scope(reinterpret_cast<napi_env>(nativeEngine));
124 napi_value optionsValue = nativeEngine->ValueToNapiValue(valueWrapper);
125 VideoModel::GetInstance()->SetImageAIOptions(optionsValue);
126 }
127
JsMuted(const JSCallbackInfo & info)128 void JSVideo::JsMuted(const JSCallbackInfo& info)
129 {
130 bool muted = false;
131 if (info[0]->IsBoolean()) {
132 muted = info[0]->ToBoolean();
133 #ifdef SUPPORT_JSSTACK
134 HiviewDFX::ReportXPowerJsStackSysEvent(info.GetVm(), "VOLUME_CHANGE", "SRC=Video");
135 #endif
136 }
137 VideoModel::GetInstance()->SetMuted(muted);
138 }
139
JsAutoPlay(const JSCallbackInfo & info)140 void JSVideo::JsAutoPlay(const JSCallbackInfo& info)
141 {
142 bool autoPlay = false;
143 if (info[0]->IsBoolean()) {
144 autoPlay = info[0]->ToBoolean();
145 #ifdef SUPPORT_JSSTACK
146 HiviewDFX::ReportXPowerJsStackSysEvent(info.GetVm(), "STREAM_CHANGE", "SRC=Video");
147 #endif
148 }
149 VideoModel::GetInstance()->SetAutoPlay(autoPlay);
150 }
151
JsControls(const JSCallbackInfo & info)152 void JSVideo::JsControls(const JSCallbackInfo& info)
153 {
154 bool controls = true;
155 if (info[0]->IsBoolean()) {
156 controls = info[0]->ToBoolean();
157 }
158 VideoModel::GetInstance()->SetControls(controls);
159 }
160
JsLoop(const JSCallbackInfo & info)161 void JSVideo::JsLoop(const JSCallbackInfo& info)
162 {
163 bool loop = false;
164 if (info[0]->IsBoolean()) {
165 loop = info[0]->ToBoolean();
166 }
167 VideoModel::GetInstance()->SetLoop(loop);
168 }
169
JsObjectFit(const JSCallbackInfo & info)170 void JSVideo::JsObjectFit(const JSCallbackInfo& info)
171 {
172 ImageFit imageFit = ImageFit::COVER;
173 // The default value of Imagefit is FILL, but in the video the default value is COVER.
174 // So the default value need to be converted.
175 if (info[0]->IsUndefined()) {
176 VideoModel::GetInstance()->SetObjectFit(imageFit);
177 return;
178 }
179 if (info[0]->IsNumber()) {
180 imageFit = static_cast<ImageFit>(info[0]->ToNumber<int>());
181 }
182 VideoModel::GetInstance()->SetObjectFit(imageFit);
183 }
184
JsOnStart(const JSCallbackInfo & info)185 void JSVideo::JsOnStart(const JSCallbackInfo& info)
186 {
187 if (!info[0]->IsFunction()) {
188 return;
189 }
190 auto jsFunc = AceType::MakeRefPtr<JsFunction>(JSRef<JSObject>(), JSRef<JSFunc>::Cast(info[0]));
191 WeakPtr<NG::FrameNode> targetNode = AceType::WeakClaim(NG::ViewStackProcessor::GetInstance()->GetMainFrameNode());
192 auto onStart = [execCtx = info.GetExecutionContext(), func = std::move(jsFunc), node = targetNode](
193 const std::string& param) {
194 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
195 ACE_SCORING_EVENT("Video.onStart");
196 PipelineContext::SetCallBackNode(node);
197 std::vector<std::string> keys = { "start" };
198 func->Execute(keys, param);
199 };
200 VideoModel::GetInstance()->SetOnStart(std::move(onStart));
201 }
202
JsOnPause(const JSCallbackInfo & info)203 void JSVideo::JsOnPause(const JSCallbackInfo& info)
204 {
205 if (!info[0]->IsFunction()) {
206 return;
207 }
208 auto jsFunc = AceType::MakeRefPtr<JsFunction>(JSRef<JSObject>(), JSRef<JSFunc>::Cast(info[0]));
209 WeakPtr<NG::FrameNode> targetNode = AceType::WeakClaim(NG::ViewStackProcessor::GetInstance()->GetMainFrameNode());
210 auto onPause = [execCtx = info.GetExecutionContext(), func = std::move(jsFunc), node = targetNode](
211 const std::string& param) {
212 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
213 ACE_SCORING_EVENT("Video.onPause");
214 PipelineContext::SetCallBackNode(node);
215 std::vector<std::string> keys = { "pause" };
216 func->Execute(keys, param);
217 };
218 VideoModel::GetInstance()->SetOnPause(std::move(onPause));
219 }
220
JsOnFinish(const JSCallbackInfo & info)221 void JSVideo::JsOnFinish(const JSCallbackInfo& info)
222 {
223 if (!info[0]->IsFunction()) {
224 return;
225 }
226 auto jsFunc = AceType::MakeRefPtr<JsFunction>(JSRef<JSObject>(), JSRef<JSFunc>::Cast(info[0]));
227 WeakPtr<NG::FrameNode> targetNode = AceType::WeakClaim(NG::ViewStackProcessor::GetInstance()->GetMainFrameNode());
228 auto onFinish = [execCtx = info.GetExecutionContext(), func = std::move(jsFunc), node = targetNode](
229 const std::string& param) {
230 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
231 ACE_SCORING_EVENT("Video.onFinish");
232 PipelineContext::SetCallBackNode(node);
233 std::vector<std::string> keys = { "finish" };
234 func->Execute(keys, param);
235 };
236 VideoModel::GetInstance()->SetOnFinish(std::move(onFinish));
237 }
238
JsOnStop(const JSCallbackInfo & info)239 void JSVideo::JsOnStop(const JSCallbackInfo& info)
240 {
241 if (!info[0]->IsFunction()) {
242 return;
243 }
244 auto jsFunc = AceType::MakeRefPtr<JsFunction>(JSRef<JSObject>(), JSRef<JSFunc>::Cast(info[0]));
245 auto targetNode = AceType::WeakClaim(NG::ViewStackProcessor::GetInstance()->GetMainFrameNode());
246 auto onStop = [execCtx = info.GetExecutionContext(), func = std::move(jsFunc), node = targetNode](
247 const std::string& param) {
248 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
249 ACE_SCORING_EVENT("Video.onStop");
250 PipelineContext::SetCallBackNode(node);
251 std::vector<std::string> keys = { "stop" };
252 func->Execute(keys, param);
253 };
254 VideoModel::GetInstance()->SetOnStop(std::move(onStop));
255 }
256
JsOnFullscreenChange(const JSCallbackInfo & info)257 void JSVideo::JsOnFullscreenChange(const JSCallbackInfo& info)
258 {
259 if (!info[0]->IsFunction()) {
260 return;
261 }
262 auto jsFunc = AceType::MakeRefPtr<JsFunction>(JSRef<JSObject>(), JSRef<JSFunc>::Cast(info[0]));
263 WeakPtr<NG::FrameNode> targetNode = AceType::WeakClaim(NG::ViewStackProcessor::GetInstance()->GetMainFrameNode());
264 auto OnFullScreenChange = [execCtx = info.GetExecutionContext(), func = std::move(jsFunc), node = targetNode](
265 const std::string& param) {
266 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
267 ACE_SCORING_EVENT("Video.OnFullScreenChange");
268 PipelineContext::SetCallBackNode(node);
269 std::vector<std::string> keys = { "fullscreen" };
270 func->Execute(keys, param);
271 };
272 VideoModel::GetInstance()->SetOnFullScreenChange(std::move(OnFullScreenChange));
273 }
274
JsOnPrepared(const JSCallbackInfo & info)275 void JSVideo::JsOnPrepared(const JSCallbackInfo& info)
276 {
277 if (!info[0]->IsFunction()) {
278 return;
279 }
280 auto jsFunc = AceType::MakeRefPtr<JsFunction>(JSRef<JSObject>(), JSRef<JSFunc>::Cast(info[0]));
281 WeakPtr<NG::FrameNode> targetNode = AceType::WeakClaim(NG::ViewStackProcessor::GetInstance()->GetMainFrameNode());
282 auto onPrepared = [execCtx = info.GetExecutionContext(), func = std::move(jsFunc), node = targetNode](
283 const std::string& param) {
284 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
285 ACE_SCORING_EVENT("Video.onPrepared");
286 PipelineContext::SetCallBackNode(node);
287 std::vector<std::string> keys = { "duration" };
288 func->Execute(keys, param);
289 };
290 VideoModel::GetInstance()->SetOnPrepared(std::move(onPrepared));
291 }
292
JsOnSeeking(const JSCallbackInfo & info)293 void JSVideo::JsOnSeeking(const JSCallbackInfo& info)
294 {
295 if (!info[0]->IsFunction()) {
296 return;
297 }
298 auto jsFunc = AceType::MakeRefPtr<JsFunction>(JSRef<JSObject>(), JSRef<JSFunc>::Cast(info[0]));
299 WeakPtr<NG::FrameNode> targetNode = AceType::WeakClaim(NG::ViewStackProcessor::GetInstance()->GetMainFrameNode());
300 auto onSeeking = [execCtx = info.GetExecutionContext(), func = std::move(jsFunc), node = targetNode](
301 const std::string& param) {
302 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
303 ACE_SCORING_EVENT("Video.onSeeking");
304 PipelineContext::SetCallBackNode(node);
305 std::vector<std::string> keys = { "time" };
306 func->Execute(keys, param);
307 };
308 VideoModel::GetInstance()->SetOnSeeking(std::move(onSeeking));
309 }
310
JsOnSeeked(const JSCallbackInfo & info)311 void JSVideo::JsOnSeeked(const JSCallbackInfo& info)
312 {
313 if (!info[0]->IsFunction()) {
314 return;
315 }
316 auto jsFunc = AceType::MakeRefPtr<JsFunction>(JSRef<JSObject>(), JSRef<JSFunc>::Cast(info[0]));
317 WeakPtr<NG::FrameNode> targetNode = AceType::WeakClaim(NG::ViewStackProcessor::GetInstance()->GetMainFrameNode());
318 auto onSeeked = [execCtx = info.GetExecutionContext(), func = std::move(jsFunc), node = targetNode](
319 const std::string& param) {
320 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
321 ACE_SCORING_EVENT("Video.onSeeked");
322 PipelineContext::SetCallBackNode(node);
323 std::vector<std::string> keys = { "time" };
324 func->Execute(keys, param);
325 };
326 VideoModel::GetInstance()->SetOnSeeked(std::move(onSeeked));
327 }
328
JsOnUpdate(const JSCallbackInfo & info)329 void JSVideo::JsOnUpdate(const JSCallbackInfo& info)
330 {
331 if (!info[0]->IsFunction()) {
332 return;
333 }
334 auto jsFunc = AceType::MakeRefPtr<JsFunction>(JSRef<JSObject>(), JSRef<JSFunc>::Cast(info[0]));
335 WeakPtr<NG::FrameNode> targetNode = AceType::WeakClaim(NG::ViewStackProcessor::GetInstance()->GetMainFrameNode());
336 auto onUpdate = [execCtx = info.GetExecutionContext(), func = std::move(jsFunc), node = targetNode](
337 const std::string& param) {
338 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
339 ACE_SCORING_EVENT("Video.onUpdate");
340 PipelineContext::SetCallBackNode(node);
341 std::vector<std::string> keys = { "time" };
342 func->Execute(keys, param);
343 };
344 VideoModel::GetInstance()->SetOnUpdate(std::move(onUpdate));
345 }
346
JsOnError(const JSCallbackInfo & info)347 void JSVideo::JsOnError(const JSCallbackInfo& info)
348 {
349 if (!info[0]->IsFunction()) {
350 return;
351 }
352 auto jsFunc = AceType::MakeRefPtr<JsFunction>(JSRef<JSObject>(), JSRef<JSFunc>::Cast(info[0]));
353 WeakPtr<NG::FrameNode> targetNode = AceType::WeakClaim(NG::ViewStackProcessor::GetInstance()->GetMainFrameNode());
354 auto onError = [execCtx = info.GetExecutionContext(), func = std::move(jsFunc), node = targetNode](
355 const std::string& param) {
356 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
357 ACE_SCORING_EVENT("Video.onError");
358 PipelineContext::SetCallBackNode(node);
359 std::vector<std::string> keys = { "error" };
360 func->Execute(keys, param);
361 };
362 VideoModel::GetInstance()->SetOnError(std::move(onError));
363 }
364
GetEventMarker(const JSCallbackInfo & info,const std::vector<std::string> & keys)365 EventMarker JSVideo::GetEventMarker(const JSCallbackInfo& info, const std::vector<std::string>& keys)
366 {
367 if (!info[0]->IsFunction()) {
368 return EventMarker();
369 }
370
371 RefPtr<JsFunction> jsFunc = AceType::MakeRefPtr<JsFunction>(JSRef<JSObject>(), JSRef<JSFunc>::Cast(info[0]));
372 WeakPtr<NG::FrameNode> targetNode = AceType::WeakClaim(NG::ViewStackProcessor::GetInstance()->GetMainFrameNode());
373 auto eventMarker = EventMarker([execCtx = info.GetExecutionContext(), func = std::move(jsFunc), keys,
374 node = targetNode](const std::string& param) {
375 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
376 PipelineContext::SetCallBackNode(node);
377 func->Execute(keys, param);
378 });
379 return eventMarker;
380 }
381
EnableAnalyzer(bool enable)382 void JSVideo::EnableAnalyzer(bool enable)
383 {
384 VideoModel::GetInstance()->EnableAnalyzer(enable);
385 }
386
AnalyzerConfig(const JSCallbackInfo & info)387 void JSVideo::AnalyzerConfig(const JSCallbackInfo& info)
388 {
389 auto configParams = info[0];
390 if (configParams->IsNull() || !configParams->IsObject()) {
391 return;
392 }
393 auto engine = EngineHelper::GetCurrentEngine();
394 CHECK_NULL_VOID(engine);
395 NativeEngine* nativeEngine = engine->GetNativeEngine();
396 panda::Local<JsiValue> value = configParams.Get().GetLocalHandle();
397 JSValueWrapper valueWrapper = value;
398 ScopeRAII scope(reinterpret_cast<napi_env>(nativeEngine));
399 napi_value nativeValue = nativeEngine->ValueToNapiValue(valueWrapper);
400 VideoModel::GetInstance()->SetImageAnalyzerConfig(nativeValue);
401 }
402
JSBind(BindingTarget globalObj)403 void JSVideo::JSBind(BindingTarget globalObj)
404 {
405 JSClass<JSVideo>::Declare("Video");
406 MethodOptions opt = MethodOptions::NONE;
407 JSClass<JSVideo>::StaticMethod("create", &JSVideo::Create, opt);
408 JSClass<JSVideo>::StaticMethod("muted", &JSVideo::JsMuted, opt);
409 JSClass<JSVideo>::StaticMethod("autoPlay", &JSVideo::JsAutoPlay, opt);
410 JSClass<JSVideo>::StaticMethod("controls", &JSVideo::JsControls, opt);
411 JSClass<JSVideo>::StaticMethod("loop", &JSVideo::JsLoop, opt);
412 JSClass<JSVideo>::StaticMethod("objectFit", &JSVideo::JsObjectFit, opt);
413
414 JSClass<JSVideo>::StaticMethod("onStart", &JSVideo::JsOnStart);
415 JSClass<JSVideo>::StaticMethod("onPause", &JSVideo::JsOnPause);
416 JSClass<JSVideo>::StaticMethod("onFinish", &JSVideo::JsOnFinish);
417 JSClass<JSVideo>::StaticMethod("onFullscreenChange", &JSVideo::JsOnFullscreenChange);
418 JSClass<JSVideo>::StaticMethod("onPrepared", &JSVideo::JsOnPrepared);
419 JSClass<JSVideo>::StaticMethod("onSeeking", &JSVideo::JsOnSeeking);
420 JSClass<JSVideo>::StaticMethod("onSeeked", &JSVideo::JsOnSeeked);
421 JSClass<JSVideo>::StaticMethod("onUpdate", &JSVideo::JsOnUpdate);
422 JSClass<JSVideo>::StaticMethod("onError", &JSVideo::JsOnError);
423 JSClass<JSVideo>::StaticMethod("onStop", &JSVideo::JsOnStop);
424 JSClass<JSVideo>::StaticMethod("enableAnalyzer", &JSVideo::EnableAnalyzer);
425 JSClass<JSVideo>::StaticMethod("analyzerConfig", &JSVideo::AnalyzerConfig);
426
427 JSClass<JSVideo>::StaticMethod("onTouch", &JSInteractableView::JsOnTouch);
428 JSClass<JSVideo>::StaticMethod("onHover", &JSInteractableView::JsOnHover);
429 JSClass<JSVideo>::StaticMethod("onKeyEvent", &JSInteractableView::JsOnKey);
430 JSClass<JSVideo>::StaticMethod("onDeleteEvent", &JSInteractableView::JsOnDelete);
431 JSClass<JSVideo>::StaticMethod("onClick", &JSInteractableView::JsOnClick);
432 JSClass<JSVideo>::StaticMethod("onAttach", &JSInteractableView::JsOnAttach);
433 JSClass<JSVideo>::StaticMethod("onAppear", &JSInteractableView::JsOnAppear);
434 JSClass<JSVideo>::StaticMethod("onDetach", &JSInteractableView::JsOnDetach);
435 JSClass<JSVideo>::StaticMethod("onDisAppear", &JSInteractableView::JsOnDisAppear);
436 JSClass<JSVideo>::StaticMethod("remoteMessage", &JSInteractableView::JsCommonRemoteMessage);
437 // override method
438 JSClass<JSVideo>::StaticMethod("opacity", &JSViewAbstract::JsOpacityPassThrough);
439 JSClass<JSVideo>::StaticMethod("transition", &JSViewAbstract::JsTransitionPassThrough);
440 JSClass<JSVideo>::InheritAndBind<JSViewAbstract>(globalObj);
441 }
442
443 } // namespace OHOS::Ace::Framework
444