1 /*
2 * Copyright (c) 2022-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 "core/components_ng/pattern/video/video_pattern.h"
17
18 #include "video_node.h"
19
20 #include "base/background_task_helper/background_task_helper.h"
21 #include "base/geometry/dimension.h"
22 #include "base/geometry/ng/size_t.h"
23 #include "base/i18n/localization.h"
24 #include "base/json/json_util.h"
25 #include "base/thread/task_executor.h"
26 #include "base/utils/string_utils.h"
27 #include "base/utils/system_properties.h"
28 #include "base/utils/utils.h"
29 #include "core/common/ace_engine.h"
30 #include "core/common/ai/image_analyzer_manager.h"
31 #include "core/common/udmf/udmf_client.h"
32 #include "core/components/video/video_theme.h"
33 #include "core/components_ng/pattern/slider/slider_pattern.h"
34 #include "core/components_ng/pattern/text/text_pattern.h"
35 #include "core/components_ng/pattern/video/video_full_screen_node.h"
36 #include "core/components_ng/pattern/video/video_full_screen_pattern.h"
37 #include "core/components_ng/property/gradient_property.h"
38
39 #ifdef RENDER_EXTRACT_SUPPORTED
40 #include "core/common/ace_view.h"
41 #endif
42
43 namespace OHOS::Ace::NG {
44 namespace {
45 using HiddenChangeEvent = std::function<void(bool)>;
46 constexpr uint32_t SECONDS_PER_HOUR = 3600;
47 constexpr uint32_t SECONDS_PER_MINUTE = 60;
48 const std::string FORMAT_HH_MM_SS = "%02d:%02d:%02d";
49 const std::string FORMAT_MM_SS = "%02d:%02d";
50 constexpr int32_t MILLISECONDS_TO_SECONDS = 1000;
51 constexpr uint32_t CURRENT_POS = 1;
52 constexpr uint32_t SLIDER_POS = 2;
53 constexpr uint32_t DURATION_POS = 3;
54 constexpr uint32_t FULL_SCREEN_POS = 4;
55 constexpr int32_t AVERAGE_VALUE = 2;
56 constexpr int32_t ANALYZER_DELAY_TIME = 100;
57 constexpr int32_t ANALYZER_CAPTURE_DELAY_TIME = 1000;
58 const Dimension LIFT_HEIGHT = 28.0_vp;
59 const std::string PNG_FILE_EXTENSION = "png";
60 constexpr int32_t MEDIA_TYPE_AUD = 0;
61 constexpr float VOLUME_STEP = 0.05f;
62 const std::unordered_set<ImageFit> EXPORT_IMAGEFIT_SUPPORT_TYPES = {
63 ImageFit::FILL,
64 ImageFit::CONTAIN,
65 ImageFit::COVER,
66 ImageFit::FITWIDTH, //FITWIDTH is used instead of AUTO
67 ImageFit::NONE,
68 ImageFit::SCALE_DOWN,
69 ImageFit::TOP_LEFT,
70 ImageFit::TOP,
71 ImageFit::TOP_END,
72 ImageFit::START,
73 ImageFit::CENTER,
74 ImageFit::END,
75 ImageFit::BOTTOM_START,
76 ImageFit::BOTTOM,
77 ImageFit::BOTTOM_END
78 };
79
80 enum SliderChangeMode {
81 BEGIN = 0,
82 MOVING,
83 END,
84 };
85
IntTimeToText(uint32_t time)86 std::string IntTimeToText(uint32_t time)
87 {
88 auto minutes = (time % SECONDS_PER_HOUR) / SECONDS_PER_MINUTE;
89 auto seconds = time % SECONDS_PER_MINUTE;
90 if (time >= SECONDS_PER_HOUR) {
91 auto hours = time / SECONDS_PER_HOUR;
92 return StringUtils::FormatString(FORMAT_HH_MM_SS.c_str(), hours, minutes, seconds);
93 }
94 return StringUtils::FormatString(FORMAT_MM_SS.c_str(), minutes, seconds);
95 }
96
CalculateFitContain(const SizeF & videoSize,const SizeF & layoutSize)97 SizeF CalculateFitContain(const SizeF& videoSize, const SizeF& layoutSize)
98 {
99 double layoutRatio = NearZero(layoutSize.Height()) ? 0.0 : layoutSize.Width() / layoutSize.Height();
100 double sourceRatio = NearZero(videoSize.Height()) ? layoutRatio : videoSize.Width() / videoSize.Height();
101
102 if (NearZero(layoutRatio) || NearZero(sourceRatio)) {
103 return layoutSize;
104 }
105 if (sourceRatio < layoutRatio) {
106 return { static_cast<float>(sourceRatio) * layoutSize.Height(), layoutSize.Height() };
107 }
108 return { layoutSize.Width(), static_cast<float>(layoutSize.Width() / sourceRatio) };
109 }
110
CalculateFitFill(const SizeF & layoutSize)111 SizeF CalculateFitFill(const SizeF& layoutSize)
112 {
113 return layoutSize;
114 }
115
CalculateFitCover(const SizeF & videoSize,const SizeF & layoutSize)116 SizeF CalculateFitCover(const SizeF& videoSize, const SizeF& layoutSize)
117 {
118 double layoutRatio = NearZero(layoutSize.Height()) ? 0.0 : layoutSize.Width() / layoutSize.Height();
119 double sourceRatio = NearZero(videoSize.Height()) ? layoutRatio : videoSize.Width() / videoSize.Height();
120
121 if (NearZero(layoutRatio) || NearZero(sourceRatio)) {
122 return layoutSize;
123 }
124 if (sourceRatio < layoutRatio) {
125 return { layoutSize.Width(), static_cast<float>(layoutSize.Width() / sourceRatio) };
126 }
127 return { static_cast<float>(layoutSize.Height() * sourceRatio), layoutSize.Height() };
128 }
129
CalculateFitNone(const SizeF & videoSize)130 SizeF CalculateFitNone(const SizeF& videoSize)
131 {
132 return videoSize;
133 }
134
CalculateFitScaleDown(const SizeF & videoSize,const SizeF & layoutSize)135 SizeF CalculateFitScaleDown(const SizeF& videoSize, const SizeF& layoutSize)
136 {
137 if (layoutSize.Width() > videoSize.Width()) {
138 return CalculateFitNone(videoSize);
139 }
140 return CalculateFitContain(videoSize, layoutSize);
141 }
142
MeasureVideoContentLayout(const SizeF & layoutSize,const RefPtr<VideoLayoutProperty> & layoutProperty)143 RectF MeasureVideoContentLayout(const SizeF& layoutSize, const RefPtr<VideoLayoutProperty>& layoutProperty)
144 {
145 if (!layoutProperty || !layoutProperty->HasVideoSize()) {
146 return {0.0f, 0.0f, layoutSize.Width(), layoutSize.Height()};
147 }
148
149 auto videoSize = layoutProperty->GetVideoSizeValue(SizeF(0, 0));
150 auto videoFrameSize = videoSize;
151 auto imageFit = layoutProperty->GetObjectFitValue(ImageFit::COVER);
152 auto rect = RectF();
153 switch (imageFit) {
154 case ImageFit::CONTAIN: case ImageFit::FITWIDTH: //FITWIDTH is used instead of AUTO
155 videoFrameSize = CalculateFitContain(videoSize, layoutSize);
156 rect = RectF{(layoutSize.Width() - videoFrameSize.Width()) / AVERAGE_VALUE,
157 (layoutSize.Height() - videoFrameSize.Height()) / AVERAGE_VALUE,
158 videoFrameSize.Width(), videoFrameSize.Height()};
159 break;
160 case ImageFit::FILL:
161 videoFrameSize = CalculateFitFill(layoutSize);
162 rect = RectF{(layoutSize.Width() - videoFrameSize.Width()) / AVERAGE_VALUE,
163 (layoutSize.Height() - videoFrameSize.Height()) / AVERAGE_VALUE,
164 videoFrameSize.Width(), videoFrameSize.Height()};
165 break;
166 case ImageFit::COVER:
167 videoFrameSize = CalculateFitCover(videoSize, layoutSize);
168 rect = RectF{(layoutSize.Width() - videoFrameSize.Width()) / AVERAGE_VALUE,
169 (layoutSize.Height() - videoFrameSize.Height()) / AVERAGE_VALUE,
170 videoFrameSize.Width(), videoFrameSize.Height()};
171 break;
172 case ImageFit::NONE:
173 videoFrameSize = CalculateFitNone(videoSize);
174 rect = RectF{(layoutSize.Width() - videoFrameSize.Width()) / AVERAGE_VALUE,
175 (layoutSize.Height() - videoFrameSize.Height()) / AVERAGE_VALUE,
176 videoFrameSize.Width(), videoFrameSize.Height()};
177 break;
178 case ImageFit::SCALE_DOWN:
179 videoFrameSize = CalculateFitScaleDown(videoSize, layoutSize);
180 rect = RectF{(layoutSize.Width() - videoFrameSize.Width()) / AVERAGE_VALUE,
181 (layoutSize.Height() - videoFrameSize.Height()) / AVERAGE_VALUE,
182 videoFrameSize.Width(), videoFrameSize.Height()};
183 break;
184 case ImageFit::TOP_LEFT:
185 rect = RectF{0.0f, 0.0f, videoSize.Width(), videoSize.Height()};
186 break;
187 case ImageFit::TOP:
188 rect = RectF{(layoutSize.Width() - videoFrameSize.Width()) / AVERAGE_VALUE, 0.0f,
189 videoSize.Width(), videoSize.Height()};
190 break;
191 case ImageFit::TOP_END:
192 rect = RectF{layoutSize.Width() - videoFrameSize.Width(), 0.0f, videoSize.Width(), videoSize.Height()};
193 break;
194 case ImageFit::START:
195 rect = RectF{0.0f, (layoutSize.Height() - videoFrameSize.Height()) / AVERAGE_VALUE,
196 videoSize.Width(), videoSize.Height()};
197 break;
198 case ImageFit::CENTER:
199 rect = RectF{(layoutSize.Width() - videoFrameSize.Width()) / AVERAGE_VALUE,
200 (layoutSize.Height() - videoFrameSize.Height()) / AVERAGE_VALUE, videoSize.Width(), videoSize.Height()};
201 break;
202 case ImageFit::END:
203 rect = RectF{layoutSize.Width() - videoFrameSize.Width(),
204 (layoutSize.Height() - videoFrameSize.Height()) / AVERAGE_VALUE, videoSize.Width(), videoSize.Height()};
205 break;
206 case ImageFit::BOTTOM_START:
207 rect = RectF{0.0f, layoutSize.Height() - videoFrameSize.Height(), videoSize.Width(), videoSize.Height()};
208 break;
209 case ImageFit::BOTTOM:
210 rect = RectF{(layoutSize.Width() - videoFrameSize.Width()) / AVERAGE_VALUE,
211 layoutSize.Height() - videoFrameSize.Height(), videoSize.Width(), videoSize.Height()};
212 break;
213 case ImageFit::BOTTOM_END:
214 rect = RectF{layoutSize.Width() - videoFrameSize.Width(), layoutSize.Height() - videoFrameSize.Height(),
215 videoSize.Width(), videoSize.Height()};
216 break;
217 default:
218 videoFrameSize = CalculateFitCover(videoSize, layoutSize);
219 rect = RectF{(layoutSize.Width() - videoFrameSize.Width()) / AVERAGE_VALUE,
220 (layoutSize.Height() - videoFrameSize.Height()) / AVERAGE_VALUE,
221 videoFrameSize.Width(), videoFrameSize.Height()};
222 break;
223 }
224 return rect;
225 }
226
RoundValueToPixelGrid(float value,bool isRound,bool forceCeil,bool forceFloor)227 float RoundValueToPixelGrid(float value, bool isRound, bool forceCeil, bool forceFloor)
228 {
229 float fractials = fmod(value, 1.0f);
230 if (fractials < 0.0f) {
231 ++fractials;
232 }
233 if (forceCeil) {
234 return (value - fractials + 1.0f);
235 } else if (forceFloor) {
236 return (value - fractials);
237 } else if (isRound) {
238 if (NearEqual(fractials, 1.0f) || GreatOrEqual(fractials, 0.50f)) {
239 return (value - fractials + 1.0f);
240 } else {
241 return (value - fractials);
242 }
243 }
244 return value;
245 }
246
AdjustPaintRect(float positionX,float positionY,float width,float height,bool isRound)247 RectF AdjustPaintRect(float positionX, float positionY, float width, float height, bool isRound)
248 {
249 RectF rect;
250 float relativeLeft = positionX;
251 float relativeTop = positionY;
252 float nodeWidth = width;
253 float nodeHeight = height;
254 float absoluteRight = relativeLeft + nodeWidth;
255 float absoluteBottom = relativeTop + nodeHeight;
256 float roundToPixelErrorX = 0.0f;
257 float roundToPixelErrorY = 0.0f;
258
259 float nodeLeftI = RoundValueToPixelGrid(relativeLeft, isRound, false, false);
260 float nodeTopI = RoundValueToPixelGrid(relativeTop, isRound, false, false);
261 roundToPixelErrorX += nodeLeftI - relativeLeft;
262 roundToPixelErrorY += nodeTopI - relativeTop;
263 rect.SetLeft(nodeLeftI);
264 rect.SetTop(nodeTopI);
265
266 float nodeWidthI = RoundValueToPixelGrid(absoluteRight, isRound, false, false) - nodeLeftI;
267 float nodeWidthTemp = RoundValueToPixelGrid(nodeWidth, isRound, false, false);
268 roundToPixelErrorX += nodeWidthI - nodeWidth;
269 if (roundToPixelErrorX > 0.5f) {
270 nodeWidthI -= 1.0f;
271 roundToPixelErrorX -= 1.0f;
272 }
273 if (roundToPixelErrorX < -0.5f) {
274 nodeWidthI += 1.0f;
275 roundToPixelErrorX += 1.0f;
276 }
277 if (nodeWidthI < nodeWidthTemp) {
278 roundToPixelErrorX += nodeWidthTemp - nodeWidthI;
279 nodeWidthI = nodeWidthTemp;
280 }
281
282 float nodeHeightI = RoundValueToPixelGrid(absoluteBottom, isRound, false, false) - nodeTopI;
283 float nodeHeightTemp = RoundValueToPixelGrid(nodeHeight, isRound, false, false);
284 roundToPixelErrorY += nodeHeightI - nodeHeight;
285 if (roundToPixelErrorY > 0.5f) {
286 nodeHeightI -= 1.0f;
287 roundToPixelErrorY -= 1.0f;
288 }
289 if (roundToPixelErrorY < -0.5f) {
290 nodeHeightI += 1.0f;
291 roundToPixelErrorY += 1.0f;
292 }
293 if (nodeHeightI < nodeHeightTemp) {
294 roundToPixelErrorY += nodeHeightTemp - nodeHeightI;
295 nodeHeightI = nodeHeightTemp;
296 }
297
298 rect.SetWidth(nodeWidthI);
299 rect.SetHeight(nodeHeightI);
300 return rect;
301 }
302
ConvertToGradient(Color color)303 Gradient ConvertToGradient(Color color)
304 {
305 Gradient gradient;
306 GradientColor gradientColorBegin;
307 gradientColorBegin.SetLinearColor(LinearColor(color));
308 gradientColorBegin.SetDimension(Dimension(0.0f));
309 gradient.AddColor(gradientColorBegin);
310 OHOS::Ace::NG::GradientColor gradientColorEnd;
311 gradientColorEnd.SetLinearColor(LinearColor(color));
312 gradientColorEnd.SetDimension(Dimension(1.0f));
313 gradient.AddColor(gradientColorEnd);
314
315 return gradient;
316 }
317
RegisterMediaPlayerEventImpl(const WeakPtr<VideoPattern> & weak,const RefPtr<MediaPlayer> & mediaPlayer,int32_t instanceId,const SingleTaskExecutor & uiTaskExecutor)318 void RegisterMediaPlayerEventImpl(const WeakPtr<VideoPattern>& weak, const RefPtr<MediaPlayer>& mediaPlayer,
319 int32_t instanceId, const SingleTaskExecutor& uiTaskExecutor)
320 {
321 auto&& positionUpdatedEvent = [weak, uiTaskExecutor, instanceId](uint32_t currentPos) {
322 uiTaskExecutor.PostSyncTask([weak, currentPos, instanceId] {
323 auto video = weak.Upgrade();
324 CHECK_NULL_VOID(video);
325 ContainerScope scope(instanceId);
326 video->OnCurrentTimeChange(currentPos);
327 video->StartUpdateImageAnalyzer();
328 }, "ArkUIVideoCurrentTimeChange");
329 };
330
331 auto&& stateChangedEvent = [weak, uiTaskExecutor, instanceId](PlaybackStatus status) {
332 uiTaskExecutor.PostTask([weak, status, instanceId] {
333 auto video = weak.Upgrade();
334 CHECK_NULL_VOID(video);
335 ContainerScope scope(instanceId);
336 video->OnPlayerStatus(status);
337 }, "ArkUIVideoPlayerStatusChange");
338 };
339
340 auto&& errorEvent = [weak, uiTaskExecutor, instanceId]() {
341 uiTaskExecutor.PostTask([weak, instanceId] {
342 auto video = weak.Upgrade();
343 CHECK_NULL_VOID(video);
344 ContainerScope scope(instanceId);
345 video->OnError("");
346 }, "ArkUIVideoError");
347 };
348
349 auto&& videoErrorEvent = [weak, uiTaskExecutor, instanceId](int32_t code, const std::string& message) {
350 uiTaskExecutor.PostTask([weak, instanceId, code, message] {
351 auto video = weak.Upgrade();
352 CHECK_NULL_VOID(video);
353 ContainerScope scope(instanceId);
354 video->OnError(code, message);
355 }, "ArkUIVideoErrorWithParam");
356 };
357
358 auto&& resolutionChangeEvent = [weak, uiTaskExecutor, instanceId]() {
359 uiTaskExecutor.PostSyncTask([weak, instanceId] {
360 auto video = weak.Upgrade();
361 CHECK_NULL_VOID(video);
362 ContainerScope scope(instanceId);
363 video->OnResolutionChange();
364 }, "ArkUIVideoResolutionChange");
365 };
366
367 auto&& startRenderFrameEvent = [weak, uiTaskExecutor, instanceId]() {
368 uiTaskExecutor.PostSyncTask([weak, instanceId] {
369 auto video = weak.Upgrade();
370 CHECK_NULL_VOID(video);
371 ContainerScope scope(instanceId);
372 video->OnStartRenderFrameCb();
373 }, "ArkUIVideoStartRenderFrame");
374 };
375
376 mediaPlayer->RegisterMediaPlayerEvent(
377 positionUpdatedEvent, stateChangedEvent, errorEvent, resolutionChangeEvent, startRenderFrameEvent);
378 mediaPlayer->RegisterMediaPlayerVideoErrorEvent(videoErrorEvent);
379 }
380
StatusToString(PlaybackStatus status)381 std::string StatusToString(PlaybackStatus status)
382 {
383 switch (status) {
384 case PlaybackStatus::ERROR:
385 return "ERROR";
386 case PlaybackStatus::IDLE:
387 return "IDLE";
388 case PlaybackStatus::INITIALIZED:
389 return "INITIALIZED";
390 case PlaybackStatus::PREPARED:
391 return "PREPARED";
392 case PlaybackStatus::STARTED:
393 return "STARTED";
394 case PlaybackStatus::PAUSED:
395 return "PAUSED";
396 case PlaybackStatus::STOPPED:
397 return "STOPPED";
398 case PlaybackStatus::PLAYBACK_COMPLETE:
399 return "PLAYBACK_COMPLETE";
400 case PlaybackStatus::NONE:
401 return "NONE";
402 default:
403 return "Invalid";
404 }
405 }
406 } // namespace
407
VideoPattern(const RefPtr<VideoControllerV2> & videoController)408 VideoPattern::VideoPattern(const RefPtr<VideoControllerV2>& videoController)
409 : instanceId_(Container::CurrentId()), videoControllerV2_(videoController)
410 {}
411
ResetMediaPlayerOnBg()412 void VideoPattern::ResetMediaPlayerOnBg()
413 {
414 CHECK_NULL_VOID(mediaPlayer_);
415 SetIsPrepared(false);
416 ContainerScope scope(instanceId_);
417 auto host = GetHost();
418 CHECK_NULL_VOID(host);
419 auto context = host->GetContext();
420 CHECK_NULL_VOID(context);
421 VideoSourceInfo videoSrc = {videoSrcInfo_.src_, videoSrcInfo_.bundleName_, videoSrcInfo_.moduleName_};
422
423 auto uiTaskExecutor = SingleTaskExecutor::Make(context->GetTaskExecutor(), TaskExecutor::TaskType::UI);
424 auto bgTaskExecutor = SingleTaskExecutor::Make(context->GetTaskExecutor(), TaskExecutor::TaskType::BACKGROUND);
425 bgTaskExecutor.PostTask(
426 [weak = WeakClaim(this), mediaPlayerWeak = WeakClaim(AceType::RawPtr(mediaPlayer_)),
427 videoSrc, id = instanceId_, showFirstFrame = showFirstFrame_, uiTaskExecutor] {
428 auto mediaPlayer = mediaPlayerWeak.Upgrade();
429 CHECK_NULL_VOID(mediaPlayer);
430 mediaPlayer->ResetMediaPlayer();
431
432 RegisterMediaPlayerEvent(weak, mediaPlayer, videoSrc.src_, id);
433
434 if (!mediaPlayer->SetSource(videoSrc.src_, videoSrc.bundleName_, videoSrc.moduleName_)) {
435 uiTaskExecutor.PostTask([weak]() {
436 auto videoPattern = weak.Upgrade();
437 CHECK_NULL_VOID(videoPattern);
438 videoPattern->FireError(ERROR_CODE_VIDEO_SOURCE_INVALID, "Not a valid source");
439 }, "ArkUIVideoFireError");
440 return;
441 }
442
443 uiTaskExecutor.PostSyncTask([weak, id] {
444 auto videoPattern = weak.Upgrade();
445 CHECK_NULL_VOID(videoPattern);
446 ContainerScope scope(id);
447 videoPattern->PrepareSurface();
448 }, "ArkUIVideoPrepareSurface");
449
450 mediaPlayer->SetRenderFirstFrame(showFirstFrame);
451 if (mediaPlayer->PrepareAsync() != 0) {
452 TAG_LOGE(AceLogTag::ACE_VIDEO, "Player prepare failed");
453 }
454 }, "ArkUIVideoMediaPlayerReset");
455 }
456
ResetStatus()457 void VideoPattern::ResetStatus()
458 {
459 isInitialState_ = true;
460 isPlaying_ = false;
461 #ifndef OHOS_PLATFORM
462 isStop_ = false;
463 #endif
464 }
465
ResetMediaPlayer()466 void VideoPattern::ResetMediaPlayer()
467 {
468 CHECK_NULL_VOID(mediaPlayer_);
469 mediaPlayer_->ResetMediaPlayer();
470 SetIsPrepared(false);
471 if (!SetSourceForMediaPlayer()) {
472 TAG_LOGW(AceLogTag::ACE_VIDEO, "Video set source for mediaPlayer failed.");
473
474 // It need post on ui thread.
475 FireError(ERROR_CODE_VIDEO_SOURCE_INVALID, "Not a valid source");
476 return;
477 }
478
479 mediaPlayer_->SetRenderFirstFrame(showFirstFrame_);
480 RegisterMediaPlayerEvent(WeakClaim(this), mediaPlayer_, videoSrcInfo_.src_, instanceId_);
481 PrepareSurface();
482 if (mediaPlayer_ && mediaPlayer_->PrepareAsync() != 0) {
483 TAG_LOGE(AceLogTag::ACE_VIDEO, "Player prepare failed");
484 }
485 }
486
UpdateMediaPlayerOnBg()487 void VideoPattern::UpdateMediaPlayerOnBg()
488 {
489 PrepareMediaPlayer();
490 UpdateSpeed();
491 UpdateLooping();
492 UpdateMuted();
493 if (isInitialState_ && autoPlay_) {
494 // When video is autoPlay, start playing the video when it is initial state.
495 Start();
496 }
497 }
498
PrepareMediaPlayer()499 void VideoPattern::PrepareMediaPlayer()
500 {
501 auto videoLayoutProperty = GetLayoutProperty<VideoLayoutProperty>();
502 CHECK_NULL_VOID(videoLayoutProperty);
503 // src has not set/changed
504 if (!videoLayoutProperty->HasVideoSource() || videoLayoutProperty->GetVideoSource() == videoSrcInfo_) {
505 TAG_LOGI(AceLogTag::ACE_VIDEO, "Video[%{public}d] source is null or the source has not changed.", hostId_);
506 return;
507 }
508 auto videoSrcInfo = videoLayoutProperty->GetVideoSourceValue(VideoSourceInfo());
509 videoSrcInfo_.src_ = videoSrcInfo.src_;
510 videoSrcInfo_.bundleName_ = videoSrcInfo.bundleName_;
511 videoSrcInfo_.moduleName_ = videoSrcInfo.moduleName_;
512 if (mediaPlayer_ && !mediaPlayer_->IsMediaPlayerValid()) {
513 TAG_LOGI(AceLogTag::ACE_VIDEO, "Video[%{public}d] create MediaPlayer.", hostId_);
514 mediaPlayer_->CreateMediaPlayer();
515 }
516
517 if (mediaPlayer_ && !mediaPlayer_->IsMediaPlayerValid()) {
518 // It need post on ui thread.
519 FireError(ERROR_CODE_VIDEO_CREATE_PLAYER_FAILED, "Failed to create the media player");
520 return;
521 }
522
523 ResetStatus();
524 ResetMediaPlayerOnBg();
525 }
526
SetSourceForMediaPlayer()527 bool VideoPattern::SetSourceForMediaPlayer()
528 {
529 CHECK_NULL_RETURN(mediaPlayer_, false);
530 return mediaPlayer_->SetSource(videoSrcInfo_.src_, videoSrcInfo_.bundleName_, videoSrcInfo_.moduleName_);
531 }
532
RegisterMediaPlayerEvent(const WeakPtr<VideoPattern> & weak,const RefPtr<MediaPlayer> & mediaPlayer,const std::string & videoSrc,int32_t instanceId)533 void VideoPattern::RegisterMediaPlayerEvent(const WeakPtr<VideoPattern>& weak, const RefPtr<MediaPlayer>& mediaPlayer,
534 const std::string& videoSrc, int32_t instanceId)
535 {
536 if (videoSrc.empty() || !mediaPlayer) {
537 TAG_LOGW(AceLogTag::ACE_VIDEO, "Video src is empty or mediaPlayer is null, register mediaPlayerEvent fail");
538 return;
539 }
540
541 auto context = PipelineContext::GetCurrentContext();
542 CHECK_NULL_VOID(context);
543 auto uiTaskExecutor = SingleTaskExecutor::Make(context->GetTaskExecutor(), TaskExecutor::TaskType::UI);
544 RegisterMediaPlayerEventImpl(weak, mediaPlayer, instanceId, uiTaskExecutor);
545
546 auto&& seekDoneEvent = [weak, uiTaskExecutor, instanceId](uint32_t currentPos) {
547 uiTaskExecutor.PostSyncTask(
548 [&weak, currentPos, instanceId] {
549 auto video = weak.Upgrade();
550 CHECK_NULL_VOID(video);
551 ContainerScope scope(instanceId);
552 video->SetIsSeeking(false);
553 video->OnCurrentTimeChange(currentPos);
554 }, "ArkUIVideoSeekDone");
555 };
556 mediaPlayer->RegisterMediaPlayerSeekDoneEvent(std::move(seekDoneEvent));
557
558 #ifdef RENDER_EXTRACT_SUPPORTED
559 auto&& textureRefreshEvent = [weak, uiTaskExecutor](int32_t instanceId, int64_t textureId) {
560 uiTaskExecutor.PostSyncTask(
561 [&weak, instanceId, textureId] {
562 auto video = weak.Upgrade();
563 CHECK_NULL_VOID(video);
564 void* nativeWindow = video->GetNativeWindow(instanceId, textureId);
565 if (!nativeWindow) {
566 LOGE("the native window is nullptr.");
567 return;
568 }
569 video->OnTextureRefresh(nativeWindow);
570 }, "ArkUIVideoTextureRefresh");
571 };
572 mediaPlayer->RegisterTextureEvent(textureRefreshEvent);
573 #endif
574 }
575
576 #ifdef RENDER_EXTRACT_SUPPORTED
GetNativeWindow(int32_t instanceId,int64_t textureId)577 void* VideoPattern::GetNativeWindow(int32_t instanceId, int64_t textureId)
578 {
579 auto container = AceEngine::Get().GetContainer(instanceId);
580 CHECK_NULL_RETURN(container, nullptr);
581 auto nativeView = container->GetAceView();
582 CHECK_NULL_RETURN(nativeView, nullptr);
583 return const_cast<void*>(nativeView->GetNativeWindowById(textureId));
584 }
585
OnTextureRefresh(void * surface)586 void VideoPattern::OnTextureRefresh(void* surface)
587 {
588 CHECK_NULL_VOID(surface);
589 auto renderContextForMediaPlayer = renderContextForMediaPlayerWeakPtr_.Upgrade();
590 CHECK_NULL_VOID(renderContextForMediaPlayer);
591 renderContextForMediaPlayer->MarkNewFrameAvailable(surface);
592 }
593 #endif
594
OnCurrentTimeChange(uint32_t currentPos)595 void VideoPattern::OnCurrentTimeChange(uint32_t currentPos)
596 {
597 if (isPrepared_) {
598 isInitialState_ = isInitialState_ ? currentPos == 0 : false;
599 }
600 if (currentPos == currentPos_ || isStop_) {
601 return;
602 }
603
604 if (duration_ == 0) {
605 int32_t duration = 0;
606 if (mediaPlayer_ && mediaPlayer_->GetDuration(duration) == 0) {
607 duration_ = static_cast<uint32_t>(duration / MILLISECONDS_TO_SECONDS);
608 OnUpdateTime(duration_, DURATION_POS);
609 }
610 }
611
612 OnUpdateTime(currentPos, CURRENT_POS);
613 currentPos_ = isSeeking_ ? currentPos_ : currentPos;
614 auto eventHub = GetOrCreateEventHub<VideoEventHub>();
615 CHECK_NULL_VOID(eventHub);
616 eventHub->FireUpdateEvent(static_cast<double>(currentPos));
617 }
618
ChangePlayerStatus(const PlaybackStatus & status)619 void VideoPattern::ChangePlayerStatus(const PlaybackStatus& status)
620 {
621 auto eventHub = GetOrCreateEventHub<VideoEventHub>();
622 switch (status) {
623 case PlaybackStatus::STARTED:
624 CHECK_NULL_VOID(eventHub);
625 eventHub->FireStartEvent();
626 break;
627 case PlaybackStatus::PAUSED:
628 CHECK_NULL_VOID(eventHub);
629 eventHub->FirePauseEvent();
630 break;
631 case PlaybackStatus::STOPPED:
632 CHECK_NULL_VOID(eventHub);
633 eventHub->FireStopEvent();
634 break;
635 case PlaybackStatus::PREPARED: {
636 ContainerScope scope(instanceId_);
637 if (!mediaPlayer_ || !mediaPlayer_->IsMediaPlayerValid()) {
638 return;
639 }
640 int32_t milliSecondDuration = 0;
641 mediaPlayer_->GetDuration(milliSecondDuration);
642 OnPrepared(milliSecondDuration / MILLISECONDS_TO_SECONDS, 0, true);
643 break;
644 }
645 case PlaybackStatus::PLAYBACK_COMPLETE:
646 OnCompletion();
647 break;
648 default:
649 break;
650 }
651 }
652
OnPlayerStatus(PlaybackStatus status)653 void VideoPattern::OnPlayerStatus(PlaybackStatus status)
654 {
655 TAG_LOGI(AceLogTag::ACE_VIDEO, "Video[%{public}d] Player current status is %{public}s.", hostId_,
656 StatusToString(status).c_str());
657 bool isPlaying = (status == PlaybackStatus::STARTED);
658 if (isPlaying_ != isPlaying) {
659 isPlaying_ = isPlaying;
660 ChangePlayButtonTag();
661 }
662
663 if (isInitialState_) {
664 isInitialState_ = !isPlaying;
665 }
666
667 ChangePlayerStatus(status);
668 }
669
OnError(const std::string & errorId)670 void VideoPattern::OnError(const std::string& errorId)
671 {
672 AddChild();
673 auto host = GetHost();
674 CHECK_NULL_VOID(host);
675 auto pipeline = host->GetContext();
676 CHECK_NULL_VOID(pipeline);
677 pipeline->RequestFrame();
678
679 auto eventHub = GetOrCreateEventHub<VideoEventHub>();
680 CHECK_NULL_VOID(eventHub);
681 eventHub->FireErrorEvent();
682 }
683
OnError(int32_t code,const std::string & message)684 void VideoPattern::OnError(int32_t code, const std::string& message)
685 {
686 AddChild();
687 auto host = GetHost();
688 CHECK_NULL_VOID(host);
689 auto pipeline = host->GetContext();
690 CHECK_NULL_VOID(pipeline);
691 pipeline->RequestFrame();
692
693 auto eventHub = GetOrCreateEventHub<VideoEventHub>();
694 CHECK_NULL_VOID(eventHub);
695 eventHub->FireErrorEvent(code, message);
696 }
697
OnResolutionChange() const698 void VideoPattern::OnResolutionChange() const
699 {
700 if (!mediaPlayer_ || !mediaPlayer_->IsMediaPlayerValid()) {
701 return;
702 }
703 auto host = GetHost();
704 CHECK_NULL_VOID(host);
705 auto videoLayoutProperty = host->GetLayoutProperty<VideoLayoutProperty>();
706 CHECK_NULL_VOID(videoLayoutProperty);
707 auto preVideoSize = videoLayoutProperty->GetVideoSize();
708 if (!preVideoSize.has_value()) {
709 SizeF videoSize = SizeF(
710 static_cast<float>(mediaPlayer_->GetVideoWidth()),
711 static_cast<float>(mediaPlayer_->GetVideoHeight()));
712 videoLayoutProperty->UpdateVideoSize(videoSize);
713 host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
714 }
715 }
716
OnStartRenderFrameCb()717 void VideoPattern::OnStartRenderFrameCb()
718 {
719 isInitialState_ = false;
720 auto host = GetHost();
721 CHECK_NULL_VOID(host);
722 auto video = AceType::DynamicCast<VideoNode>(host);
723 CHECK_NULL_VOID(video);
724 auto image = AceType::DynamicCast<FrameNode>(video->GetPreviewImage());
725 CHECK_NULL_VOID(image);
726 auto posterLayoutProperty = image->GetLayoutProperty<ImageLayoutProperty>();
727 CHECK_NULL_VOID(posterLayoutProperty);
728 posterLayoutProperty->UpdateVisibility(VisibleType::INVISIBLE);
729 image->MarkModifyDone();
730 if (!mediaPlayer_ || !mediaPlayer_->IsMediaPlayerValid()) {
731 return;
732 }
733 auto videoLayoutProperty = host->GetLayoutProperty<VideoLayoutProperty>();
734 CHECK_NULL_VOID(videoLayoutProperty);
735 SizeF videoSize =
736 SizeF(static_cast<float>(mediaPlayer_->GetVideoWidth()), static_cast<float>(mediaPlayer_->GetVideoHeight()));
737 TAG_LOGI(AceLogTag::ACE_VIDEO, "Video[%{public}d] start render frame size:%{public}s", hostId_,
738 videoSize.ToString().c_str());
739 videoLayoutProperty->UpdateVideoSize(videoSize);
740 host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
741 }
742
OnPrepared(uint32_t duration,uint32_t currentPos,bool needFireEvent)743 void VideoPattern::OnPrepared(uint32_t duration, uint32_t currentPos, bool needFireEvent)
744 {
745 auto host = GetHost();
746 CHECK_NULL_VOID(host);
747 CHECK_NULL_VOID(mediaPlayer_);
748
749 duration_ = duration;
750 currentPos_ = currentPos;
751 isInitialState_ = currentPos != 0 ? false : isInitialState_;
752 isPlaying_ = mediaPlayer_->IsPlaying();
753 SetIsSeeking(false);
754 SetIsPrepared(true);
755 OnUpdateTime(duration_, DURATION_POS);
756 OnUpdateTime(currentPos_, CURRENT_POS);
757
758 RefPtr<UINode> controlBar = nullptr;
759 auto children = host->GetChildren();
760 for (const auto& child : children) {
761 if (child->GetTag() == V2::ROW_ETS_TAG) {
762 controlBar = child;
763 break;
764 }
765 }
766 CHECK_NULL_VOID(controlBar);
767 auto sliderNode = DynamicCast<FrameNode>(controlBar->GetChildAtIndex(SLIDER_POS));
768 auto sliderPaintProperty = sliderNode->GetPaintProperty<SliderPaintProperty>();
769 CHECK_NULL_VOID(sliderPaintProperty);
770 sliderPaintProperty->UpdateMin(0.0f);
771 sliderPaintProperty->UpdateMax(static_cast<float>(duration_));
772 sliderNode->MarkModifyDone();
773 auto playBtn = DynamicCast<FrameNode>(controlBar->GetChildAtIndex(0));
774 ChangePlayButtonTag(playBtn);
775
776 if (needFireEvent) {
777 auto eventHub = GetOrCreateEventHub<VideoEventHub>();
778 CHECK_NULL_VOID(eventHub);
779 eventHub->FirePreparedEvent(static_cast<double>(duration_));
780 }
781 TAG_LOGI(AceLogTag::ACE_VIDEO,
782 "Video[%{public}d] duration: %{public}u, loop: %{public}d, muted: %{public}d, Speed: %{public}f", hostId_,
783 duration_, loop_, muted_, progressRate_);
784 UpdateLooping();
785 UpdateSpeed();
786 UpdateMuted();
787
788 checkNeedAutoPlay();
789 }
790
checkNeedAutoPlay()791 void VideoPattern::checkNeedAutoPlay()
792 {
793 if (isStop_) {
794 isStop_ = false;
795 }
796 if (autoPlay_) {
797 Start();
798 }
799 }
800
OnCompletion()801 void VideoPattern::OnCompletion()
802 {
803 isPlaying_ = false;
804 currentPos_ = duration_;
805 OnUpdateTime(currentPos_, CURRENT_POS);
806 auto eventHub = GetOrCreateEventHub<VideoEventHub>();
807 CHECK_NULL_VOID(eventHub);
808 eventHub->FireFinishEvent();
809 }
810
HasPlayer() const811 bool VideoPattern::HasPlayer() const
812 {
813 return mediaPlayer_ != nullptr;
814 }
815
HiddenChange(bool hidden)816 void VideoPattern::HiddenChange(bool hidden)
817 {
818 if (isPlaying_ && hidden && HasPlayer()) {
819 pastPlayingStatus_ = isPlaying_;
820 Pause();
821 return;
822 }
823
824 if (!hidden && pastPlayingStatus_) {
825 pastPlayingStatus_ = false;
826 Start();
827 }
828 }
829
OnVisibleChange(bool isVisible)830 void VideoPattern::OnVisibleChange(bool isVisible)
831 {
832 if (hiddenChangeEvent_) {
833 hiddenChangeEvent_(!isVisible);
834 }
835 }
836
UpdateLooping()837 void VideoPattern::UpdateLooping()
838 {
839 if (mediaPlayer_ && mediaPlayer_->IsMediaPlayerValid()) {
840 ContainerScope scope(instanceId_);
841 auto host = GetHost();
842 CHECK_NULL_VOID(host);
843 auto context = host->GetContext();
844 CHECK_NULL_VOID(context);
845 auto bgTaskExecutor = SingleTaskExecutor::Make(context->GetTaskExecutor(), TaskExecutor::TaskType::BACKGROUND);
846 bgTaskExecutor.PostTask([weak = WeakClaim(RawPtr(mediaPlayer_)), loop = loop_] {
847 auto mediaPlayer = weak.Upgrade();
848 CHECK_NULL_VOID(mediaPlayer);
849 mediaPlayer->SetLooping(loop);
850 }, "ArkUIVideoUpdateLooping");
851 }
852 }
853
SetSurfaceBackgroundColor(Color color)854 void VideoPattern::SetSurfaceBackgroundColor(Color color)
855 {
856 CHECK_NULL_VOID(renderContextForMediaPlayer_);
857 renderContextForMediaPlayer_->UpdateBackgroundColor(color);
858 }
859
UpdateSpeed()860 void VideoPattern::UpdateSpeed()
861 {
862 if (mediaPlayer_ && mediaPlayer_->IsMediaPlayerValid()) {
863 ContainerScope scope(instanceId_);
864 auto host = GetHost();
865 CHECK_NULL_VOID(host);
866 auto context = host->GetContext();
867 CHECK_NULL_VOID(context);
868 auto bgTaskExecutor = SingleTaskExecutor::Make(context->GetTaskExecutor(), TaskExecutor::TaskType::BACKGROUND);
869 bgTaskExecutor.PostTask([weak = WeakClaim(RawPtr(mediaPlayer_)), progress = progressRate_] {
870 auto mediaPlayer = weak.Upgrade();
871 CHECK_NULL_VOID(mediaPlayer);
872 mediaPlayer->SetPlaybackSpeed(static_cast<float>(progress));
873 }, "ArkUIVideoUpdateSpeed");
874 }
875 }
876
UpdateMuted()877 void VideoPattern::UpdateMuted()
878 {
879 if (mediaPlayer_ && mediaPlayer_->IsMediaPlayerValid()) {
880 ContainerScope scope(instanceId_);
881 auto host = GetHost();
882 CHECK_NULL_VOID(host);
883 auto context = host->GetContext();
884 CHECK_NULL_VOID(context);
885 auto bgTaskExecutor = SingleTaskExecutor::Make(context->GetTaskExecutor(), TaskExecutor::TaskType::BACKGROUND);
886 bgTaskExecutor.PostTask(
887 [weak = WeakClaim(RawPtr(mediaPlayer_)), isMuted = muted_, currentVolume = currentVolume_] {
888 auto mediaPlayer = weak.Upgrade();
889 CHECK_NULL_VOID(mediaPlayer);
890 if (isMuted || NearZero(currentVolume)) {
891 mediaPlayer->SetMediaMuted(MEDIA_TYPE_AUD, true);
892 mediaPlayer->SetVolume(0.0f, 0.0f);
893 } else {
894 mediaPlayer->SetMediaMuted(MEDIA_TYPE_AUD, false);
895 mediaPlayer->SetVolume(currentVolume, currentVolume);
896 }
897 },
898 "ArkUIVideoUpdateMuted");
899 }
900 }
901
OnUpdateTime(uint32_t time,int pos) const902 void VideoPattern::OnUpdateTime(uint32_t time, int pos) const
903 {
904 auto host = GetHost();
905 CHECK_NULL_VOID(host);
906 auto layoutProperty = host->GetLayoutProperty<VideoLayoutProperty>();
907 CHECK_NULL_VOID(layoutProperty);
908 bool needControlBar = layoutProperty->GetControlsValue(true);
909 CHECK_NULL_VOID(needControlBar);
910
911 RefPtr<UINode> controlBar = nullptr;
912 auto children = host->GetChildren();
913 for (const auto& child : children) {
914 if (child->GetTag() == V2::ROW_ETS_TAG) {
915 controlBar = child;
916 break;
917 }
918 }
919
920 CHECK_NULL_VOID(controlBar);
921 auto durationNode = DynamicCast<FrameNode>(controlBar->GetChildAtIndex(pos));
922 CHECK_NULL_VOID(durationNode);
923 auto textLayoutProperty = durationNode->GetLayoutProperty<TextLayoutProperty>();
924 CHECK_NULL_VOID(textLayoutProperty);
925 std::string timeText = IntTimeToText(time);
926 textLayoutProperty->UpdateContent(timeText);
927 durationNode->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF);
928 durationNode->MarkModifyDone();
929 // if current status is seeking, no need to update slider's value
930 if (pos == CURRENT_POS && !isSeeking_) {
931 auto sliderNode = DynamicCast<FrameNode>(controlBar->GetChildAtIndex(SLIDER_POS));
932 CHECK_NULL_VOID(sliderNode);
933 auto sliderPattern = sliderNode->GetPattern<SliderPattern>();
934 CHECK_NULL_VOID(sliderPattern);
935 sliderPattern->UpdateValue(static_cast<float>(time));
936 sliderNode->MarkModifyDone();
937 }
938 }
939
PrepareSurface()940 void VideoPattern::PrepareSurface()
941 {
942 if (!mediaPlayer_ || renderSurface_->IsSurfaceValid()) {
943 return;
944 }
945 if (!SystemProperties::GetExtSurfaceEnabled()) {
946 renderSurface_->SetRenderContext(renderContextForMediaPlayer_);
947 }
948 renderSurface_->InitSurface();
949 mediaPlayer_->SetRenderSurface(renderSurface_);
950 if (mediaPlayer_->SetSurface() != 0) {
951 TAG_LOGW(AceLogTag::ACE_VIDEO, "mediaPlayer renderSurface set failed");
952 }
953 }
954
OnAttachToFrameNode()955 void VideoPattern::OnAttachToFrameNode()
956 {
957 // full screen node is not supposed to register js controller event
958 if (!InstanceOf<VideoFullScreenPattern>(this)) {
959 SetMethodCall();
960 }
961 auto host = GetHost();
962 CHECK_NULL_VOID(host);
963 hostId_ = host->GetId();
964 auto pipeline = host->GetContext();
965 CHECK_NULL_VOID(pipeline);
966 pipeline->AddWindowStateChangedCallback(host->GetId());
967 auto renderContext = host->GetRenderContext();
968 CHECK_NULL_VOID(renderContext);
969
970 #ifdef RENDER_EXTRACT_SUPPORTED
971 CHECK_NULL_VOID(renderSurface_);
972 auto contextType = renderSurface_->IsTexture() ?
973 RenderContext::ContextType::HARDWARE_TEXTURE : RenderContext::ContextType::HARDWARE_SURFACE;
974 static RenderContext::ContextParam param = { contextType, "MediaPlayerSurface",
975 RenderContext::PatternType::VIDEO };
976 #else
977 static RenderContext::ContextParam param = { RenderContext::ContextType::HARDWARE_SURFACE, "MediaPlayerSurface",
978 RenderContext::PatternType::VIDEO };
979 #endif
980 renderContextForMediaPlayer_->InitContext(false, param);
981
982 if (SystemProperties::GetExtSurfaceEnabled()) {
983 RegisterRenderContextCallBack();
984 }
985
986 renderContext->UpdateBackgroundColor(Color::BLACK);
987 renderContextForMediaPlayer_->UpdateBackgroundColor(Color::BLACK);
988 renderContext->SetClipToBounds(true);
989 }
990
OnDetachFromFrameNode(FrameNode * frameNode)991 void VideoPattern::OnDetachFromFrameNode(FrameNode* frameNode)
992 {
993 CHECK_NULL_VOID(frameNode);
994 auto id = frameNode->GetId();
995 auto pipeline = frameNode->GetContext();
996 CHECK_NULL_VOID(pipeline);
997 pipeline->RemoveWindowStateChangedCallback(id);
998 }
999
OnDetachFromMainTree()1000 void VideoPattern::OnDetachFromMainTree()
1001 {
1002 auto host = GetHost();
1003 if (host && host->GetNodeStatus() == NodeStatus::BUILDER_NODE_OFF_MAINTREE) {
1004 Pause();
1005 }
1006 }
1007
RegisterRenderContextCallBack()1008 void VideoPattern::RegisterRenderContextCallBack()
1009 {
1010 #ifdef RENDER_EXTRACT_SUPPORTED
1011 renderSurfaceWeakPtr_ = renderSurface_;
1012 renderContextForMediaPlayerWeakPtr_ = renderContextForMediaPlayer_;
1013 auto OnAttachCallBack = [weak = WeakClaim(this)](int64_t textureId, bool isAttach) mutable {
1014 auto videoPattern = weak.Upgrade();
1015 CHECK_NULL_VOID(videoPattern);
1016 if (auto renderSurface = videoPattern->renderSurfaceWeakPtr_.Upgrade(); renderSurface) {
1017 renderSurface->AttachToGLContext(textureId, isAttach);
1018 }
1019 };
1020 renderContextForMediaPlayer_->AddAttachCallBack(OnAttachCallBack);
1021 auto OnUpdateCallBack = [weak = WeakClaim(this)](std::vector<float>& matrix) mutable {
1022 auto videoPattern = weak.Upgrade();
1023 CHECK_NULL_VOID(videoPattern);
1024 if (auto renderSurface = videoPattern->renderSurfaceWeakPtr_.Upgrade(); renderSurface) {
1025 renderSurface->UpdateTextureImage(matrix);
1026 }
1027 };
1028 renderContextForMediaPlayer_->AddUpdateCallBack(OnUpdateCallBack);
1029 #endif
1030 }
1031
OnModifyDone()1032 void VideoPattern::OnModifyDone()
1033 {
1034 Pattern::OnModifyDone();
1035
1036 if (!hiddenChangeEvent_) {
1037 SetHiddenChangeEvent(CreateHiddenChangeEvent());
1038 }
1039
1040 // src has changed
1041 auto layoutProperty = GetLayoutProperty<VideoLayoutProperty>();
1042 #ifdef RENDER_EXTRACT_SUPPORTED
1043 if ((layoutProperty && layoutProperty->HasVideoSource() && layoutProperty->GetVideoSource() != videoSrcInfo_)) {
1044 #else
1045 if (Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_TWELVE) &&
1046 (layoutProperty && layoutProperty->HasVideoSource() && layoutProperty->GetVideoSource() != videoSrcInfo_)) {
1047 #endif
1048 ResetStatus();
1049 }
1050
1051 // update full screen pattern state
1052 UpdateFsState();
1053
1054 // Update the control bar and preview image.
1055 UpdatePreviewImage();
1056 UpdateControllerBar();
1057 auto host = GetHost();
1058 CHECK_NULL_VOID(host);
1059 // Update the media player when video node is not in full screen or current node is full screen node
1060 if (!fullScreenNodeId_.has_value() || InstanceOf<VideoFullScreenNode>(this)) {
1061 ContainerScope scope(instanceId_);
1062 auto pipelineContext = host->GetContext();
1063 CHECK_NULL_VOID(pipelineContext);
1064 auto uiTaskExecutor = SingleTaskExecutor::Make(pipelineContext->GetTaskExecutor(), TaskExecutor::TaskType::UI);
1065 uiTaskExecutor.PostTask([weak = WeakClaim(this)]() {
1066 auto videoPattern = weak.Upgrade();
1067 CHECK_NULL_VOID(videoPattern);
1068 ContainerScope scope(videoPattern->instanceId_);
1069 videoPattern->UpdateMediaPlayerOnBg();
1070 }, "ArkUIVideoUpdateMediaPlayer");
1071 }
1072
1073 if (SystemProperties::GetExtSurfaceEnabled()) {
1074 auto pipelineContext = host->GetContext();
1075 CHECK_NULL_VOID(pipelineContext);
1076 pipelineContext->AddOnAreaChangeNode(host->GetId());
1077 }
1078 auto eventHub = GetOrCreateEventHub<VideoEventHub>();
1079 if (!AceType::InstanceOf<VideoFullScreenPattern>(this)) {
1080 eventHub->SetInspectorId(host->GetInspectorIdValue(""));
1081 }
1082 if (!IsSupportImageAnalyzer()) {
1083 DestroyAnalyzerOverlay();
1084 } else if (isPaused_ && !isPlaying_ && !GetAnalyzerState()) {
1085 StartImageAnalyzer();
1086 }
1087 InitKeyEvent();
1088 }
1089
1090 void VideoPattern::InitKeyEvent()
1091 {
1092 auto host = GetHost();
1093 CHECK_NULL_VOID(host);
1094 auto focusHub = host->GetOrCreateFocusHub();
1095 CHECK_NULL_VOID(focusHub);
1096 auto onKeyEvent = [wp = WeakClaim(this)](const KeyEvent& event) -> bool {
1097 auto pattern = wp.Upgrade();
1098 CHECK_NULL_RETURN(pattern, false);
1099 return pattern->OnKeyEvent(event);
1100 };
1101 focusHub->SetOnKeyEventInternal(std::move(onKeyEvent));
1102 }
1103
1104 bool VideoPattern::OnKeyEvent(const KeyEvent& event)
1105 {
1106 if (isEnableShortcutKey_ && event.action == KeyAction::DOWN) {
1107 TAG_LOGD(AceLogTag::ACE_VIDEO, "video on key event %{public}d", event.code);
1108 if (event.code == KeyCode::KEY_DPAD_LEFT || event.code == KeyCode::KEY_DPAD_RIGHT) {
1109 MoveByStep(event.code == KeyCode::KEY_DPAD_LEFT ? -1 : 1);
1110 return true;
1111 }
1112 if (event.code == KeyCode::KEY_DPAD_DOWN || event.code == KeyCode::KEY_DPAD_UP) {
1113 AdjustVolume(event.code == KeyCode::KEY_DPAD_DOWN ? -1 : 1);
1114 return true;
1115 }
1116 if (event.code == KeyCode::KEY_SPACE) {
1117 OnKeySpaceEvent();
1118 return true;
1119 }
1120 }
1121 return false;
1122 }
1123
1124 bool VideoPattern::HandleSliderKeyEvent(const KeyEventInfo& event)
1125 {
1126 if (isEnableShortcutKey_ && event.GetKeyType() == KeyAction::DOWN) {
1127 TAG_LOGD(AceLogTag::ACE_VIDEO, "slider on key event %{public}d", event.GetKeyCode());
1128 if (event.GetKeyCode() == KeyCode::KEY_SPACE) {
1129 OnKeySpaceEvent();
1130 return true;
1131 }
1132 }
1133 return false;
1134 }
1135
1136 void VideoPattern::OnKeySpaceEvent()
1137 {
1138 if (isPlaying_) {
1139 Pause();
1140 } else {
1141 Start();
1142 }
1143 }
1144
1145 void VideoPattern::MoveByStep(int32_t step)
1146 {
1147 auto targetTime = static_cast<int32_t>(currentPos_) + step;
1148 if (0 <= targetTime && targetTime <= static_cast<int32_t>(duration_)) {
1149 SetCurrentTime(static_cast<float>(targetTime), OHOS::Ace::SeekMode::SEEK_CLOSEST);
1150 }
1151 }
1152
1153 void VideoPattern::AdjustVolume(int32_t step)
1154 {
1155 // the volume ranges from 0 to 1. each step is VOLUME_STEP(0.05).
1156 float targetVolume = currentVolume_ + step * VOLUME_STEP;
1157 if (LessNotEqual(targetVolume, 0.0f) || GreatNotEqual(targetVolume, 1.0f)) {
1158 return;
1159 }
1160 CHECK_NULL_VOID(mediaPlayer_);
1161 if (NearZero(targetVolume)) {
1162 mediaPlayer_->SetMediaMuted(MEDIA_TYPE_AUD, true);
1163 } else {
1164 mediaPlayer_->SetMediaMuted(MEDIA_TYPE_AUD, false);
1165 }
1166 mediaPlayer_->SetVolume(targetVolume, targetVolume);
1167 currentVolume_ = targetVolume;
1168 }
1169
1170 HiddenChangeEvent VideoPattern::CreateHiddenChangeEvent()
1171 {
1172 return [weak = WeakClaim(this)](bool hidden) {
1173 auto videoPattern = weak.Upgrade();
1174 CHECK_NULL_VOID(videoPattern);
1175 auto fullScreenNode = videoPattern->GetFullScreenNode();
1176 if (fullScreenNode) {
1177 auto fullScreenPattern = AceType::DynamicCast<VideoFullScreenPattern>(fullScreenNode->GetPattern());
1178 CHECK_NULL_VOID(fullScreenPattern);
1179 fullScreenPattern->HiddenChange(hidden);
1180 return;
1181 }
1182 videoPattern->HiddenChange(hidden);
1183 };
1184 }
1185
1186 void VideoPattern::UpdatePreviewImage()
1187 {
1188 auto layoutProperty = GetLayoutProperty<VideoLayoutProperty>();
1189 CHECK_NULL_VOID(layoutProperty);
1190 if (!layoutProperty->HasPosterImageInfo()) {
1191 return;
1192 }
1193 auto posterSourceInfo = layoutProperty->GetPosterImageInfo().value();
1194 auto imageFit = layoutProperty->GetObjectFitValue(ImageFit::COVER);
1195 auto host = GetHost();
1196 CHECK_NULL_VOID(host);
1197
1198 auto video = AceType::DynamicCast<VideoNode>(host);
1199 CHECK_NULL_VOID(video);
1200 auto image = AceType::DynamicCast<FrameNode>(video->GetPreviewImage());
1201 CHECK_NULL_VOID(image);
1202
1203 if (showFirstFrame_) {
1204 auto posterLayoutProperty = image->GetLayoutProperty<ImageLayoutProperty>();
1205 CHECK_NULL_VOID(posterLayoutProperty);
1206 posterLayoutProperty->UpdateVisibility(VisibleType::INVISIBLE);
1207 image->MarkModifyDone();
1208 return;
1209 }
1210
1211 if (!isInitialState_) {
1212 auto posterLayoutProperty = image->GetLayoutProperty<ImageLayoutProperty>();
1213 CHECK_NULL_VOID(posterLayoutProperty);
1214 posterLayoutProperty->UpdateVisibility(VisibleType::INVISIBLE);
1215 image->MarkModifyDone();
1216 return;
1217 }
1218
1219 if (!posterSourceInfo.IsValid()) {
1220 auto posterLayoutProperty = image->GetLayoutProperty<ImageLayoutProperty>();
1221 CHECK_NULL_VOID(posterLayoutProperty);
1222 posterLayoutProperty->UpdateVisibility(VisibleType::INVISIBLE);
1223 image->MarkModifyDone();
1224 TAG_LOGI(AceLogTag::ACE_VIDEO, "Src image is not valid.");
1225 return;
1226 }
1227
1228 if (image) {
1229 image->SetDraggable(false);
1230 auto posterLayoutProperty = image->GetLayoutProperty<ImageLayoutProperty>();
1231 CHECK_NULL_VOID(posterLayoutProperty);
1232 posterLayoutProperty->UpdateVisibility(VisibleType::VISIBLE);
1233 posterLayoutProperty->UpdateImageSourceInfo(posterSourceInfo);
1234 if (EXPORT_IMAGEFIT_SUPPORT_TYPES.find(imageFit) == EXPORT_IMAGEFIT_SUPPORT_TYPES.end()) {
1235 imageFit = ImageFit::COVER;
1236 }
1237 posterLayoutProperty->UpdateImageFit(imageFit);
1238 image->MarkModifyDone();
1239 }
1240 }
1241
1242 void VideoPattern::UpdateControllerBar()
1243 {
1244 auto layoutProperty = GetLayoutProperty<VideoLayoutProperty>();
1245 CHECK_NULL_VOID(layoutProperty);
1246 auto host = GetHost();
1247 CHECK_NULL_VOID(host);
1248 auto focusHub = host->GetOrCreateFocusHub();
1249 CHECK_NULL_VOID(focusHub);
1250 auto needControlBar = layoutProperty->GetControlsValue(true);
1251 focusHub->SetFocusType(needControlBar ? FocusType::SCOPE : FocusType::NODE);
1252 auto video = AceType::DynamicCast<VideoNode>(host);
1253 CHECK_NULL_VOID(video);
1254 auto controller = AceType::DynamicCast<FrameNode>(video->GetControllerRow());
1255 CHECK_NULL_VOID(controller);
1256 if (needControlBar) {
1257 auto sliderNode = DynamicCast<FrameNode>(controller->GetChildAtIndex(SLIDER_POS));
1258 CHECK_NULL_VOID(sliderNode);
1259 auto sliderPattern = sliderNode->GetPattern<SliderPattern>();
1260 CHECK_NULL_VOID(sliderPattern);
1261 sliderPattern->UpdateValue(static_cast<float>(currentPos_));
1262 sliderNode->MarkModifyDone();
1263
1264 auto textNode = DynamicCast<FrameNode>(controller->GetChildAtIndex(CURRENT_POS));
1265 CHECK_NULL_VOID(textNode);
1266 auto textLayoutProperty = textNode->GetLayoutProperty<TextLayoutProperty>();
1267 CHECK_NULL_VOID(textLayoutProperty);
1268 std::string label = IntTimeToText(currentPos_);
1269 textLayoutProperty->UpdateContent(label);
1270
1271 auto durationNode = DynamicCast<FrameNode>(controller->GetChildAtIndex(DURATION_POS));
1272 CHECK_NULL_VOID(durationNode);
1273 auto durationTextLayoutProperty = durationNode->GetLayoutProperty<TextLayoutProperty>();
1274 CHECK_NULL_VOID(durationTextLayoutProperty);
1275 std::string durationText = IntTimeToText(duration_);
1276 durationTextLayoutProperty->UpdateContent(durationText);
1277
1278 textNode->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF);
1279 textNode->MarkModifyDone();
1280 durationNode->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF);
1281 durationNode->MarkModifyDone();
1282 auto controllerLayoutProperty = controller->GetLayoutProperty<LinearLayoutProperty>();
1283 controllerLayoutProperty->UpdateVisibility(VisibleType::VISIBLE);
1284 controller->MarkModifyDone();
1285 } else {
1286 auto controllerLayoutProperty = controller->GetLayoutProperty<LinearLayoutProperty>();
1287 controllerLayoutProperty->UpdateVisibility(VisibleType::INVISIBLE);
1288 controller->MarkModifyDone();
1289 }
1290 }
1291
1292 void VideoPattern::UpdateVideoProperty()
1293 {
1294 if (isInitialState_ && autoPlay_) {
1295 Start();
1296 }
1297
1298 UpdateSpeed();
1299 UpdateLooping();
1300 UpdateMuted();
1301 }
1302
1303 void VideoPattern::AddChild()
1304 {
1305 auto host = GetHost();
1306 CHECK_NULL_VOID(host);
1307 auto video = AceType::DynamicCast<VideoNode>(host);
1308 CHECK_NULL_VOID(video);
1309 auto column = AceType::DynamicCast<FrameNode>(video->GetMediaColumn());
1310 CHECK_NULL_VOID(column);
1311 auto renderContext = column->GetRenderContext();
1312 CHECK_NULL_VOID(renderContext);
1313 renderContext->AddChild(renderContextForMediaPlayer_, 0);
1314 }
1315
1316 void VideoPattern::OnRebuildFrame()
1317 {
1318 if (!renderSurface_ || !renderSurface_->IsSurfaceValid()) {
1319 TAG_LOGW(AceLogTag::ACE_VIDEO, "MediaPlayer surface is not valid");
1320 return;
1321 }
1322 AddChild();
1323 }
1324
1325 void VideoPattern::RemoveMediaPlayerSurfaceNode()
1326 {
1327 auto host = GetHost();
1328 CHECK_NULL_VOID(host);
1329 auto video = AceType::DynamicCast<VideoNode>(host);
1330 CHECK_NULL_VOID(video);
1331 auto column = AceType::DynamicCast<FrameNode>(video->GetMediaColumn());
1332 CHECK_NULL_VOID(column);
1333 auto renderContext = column->GetRenderContext();
1334 CHECK_NULL_VOID(renderContext);
1335 renderContext->RemoveChild(renderContextForMediaPlayer_);
1336 }
1337
1338 bool VideoPattern::OnDirtyLayoutWrapperSwap(const RefPtr<LayoutWrapper>& dirty, const DirtySwapConfig& config)
1339 {
1340 if (config.skipMeasure || dirty->SkipMeasureContent()) {
1341 return false;
1342 }
1343 auto geometryNode = dirty->GetGeometryNode();
1344 CHECK_NULL_RETURN(geometryNode, false);
1345 auto videoNodeSize = geometryNode->GetContentSize();
1346 auto layoutProperty = GetLayoutProperty<VideoLayoutProperty>();
1347 CHECK_NULL_RETURN(layoutProperty, false);
1348 auto videoFrameRect = MeasureVideoContentLayout(videoNodeSize, layoutProperty);
1349 auto videoFrameSize = SizeF(videoFrameRect.Width(), videoFrameRect.Height());
1350 // Change the surface layout for drawing video frames
1351 if (renderContextForMediaPlayer_) {
1352 if (Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_TWELVE)) {
1353 auto rect = AdjustPaintRect(videoFrameRect.GetX(), videoFrameRect.GetY(),
1354 videoFrameRect.Width(), videoFrameRect.Height(), true);
1355 renderContextForMediaPlayer_->SetBounds(rect.GetX(), rect.GetY(), rect.Width(), rect.Height());
1356 } else {
1357 renderContextForMediaPlayer_->SetBounds(videoFrameRect.GetX(), videoFrameRect.GetY(),
1358 videoFrameRect.Width(), videoFrameRect.Height());
1359 }
1360 }
1361
1362 if (IsSupportImageAnalyzer()) {
1363 Rect tmpRect;
1364 auto padding = layoutProperty->CreatePaddingAndBorder();
1365 auto imageFit = layoutProperty->GetObjectFitValue(ImageFit::COVER);
1366 if (imageFit == ImageFit::COVER || imageFit == ImageFit::NONE) {
1367 tmpRect = Rect(padding.left.value_or(0), padding.top.value_or(0),
1368 videoNodeSize.Width(), videoNodeSize.Height());
1369 } else {
1370 tmpRect = Rect((videoNodeSize.Width() - videoFrameSize.Width()) / AVERAGE_VALUE + padding.left.value_or(0),
1371 (videoNodeSize.Height() - videoFrameSize.Height()) / AVERAGE_VALUE + padding.top.value_or(0),
1372 videoFrameSize.Width(), videoFrameSize.Height());
1373 }
1374 if (contentRect_ != tmpRect && ShouldUpdateImageAnalyzer()) {
1375 StartUpdateImageAnalyzer();
1376 }
1377 contentRect_ = tmpRect;
1378 UpdateAnalyzerUIConfig(geometryNode);
1379 }
1380
1381 auto host = GetHost();
1382 CHECK_NULL_RETURN(host, false);
1383 host->MarkNeedSyncRenderTree();
1384 auto video = AceType::DynamicCast<VideoNode>(host);
1385 CHECK_NULL_RETURN(video, false);
1386 auto column = AceType::DynamicCast<FrameNode>(video->GetMediaColumn());
1387 CHECK_NULL_RETURN(column, false);
1388 column->GetRenderContext()->SetClipToBounds(true);
1389 return false;
1390 }
1391
1392 void VideoPattern::OnAreaChangedInner()
1393 {
1394 if (SystemProperties::GetExtSurfaceEnabled()) {
1395 auto host = GetHost();
1396 CHECK_NULL_VOID(host);
1397 auto geometryNode = host->GetGeometryNode();
1398 CHECK_NULL_VOID(geometryNode);
1399 auto videoNodeSize = geometryNode->GetContentSize();
1400 auto layoutProperty = GetLayoutProperty<VideoLayoutProperty>();
1401 CHECK_NULL_VOID(layoutProperty);
1402 auto videoFrameRect = MeasureVideoContentLayout(videoNodeSize, layoutProperty);
1403 auto videoFrameSize = SizeF(videoFrameRect.Width(), videoFrameRect.Height());
1404 auto transformRelativeOffset = host->GetTransformRelativeOffset();
1405
1406 Rect rect =
1407 Rect(transformRelativeOffset.GetX() + (videoNodeSize.Width() - videoFrameSize.Width()) / AVERAGE_VALUE,
1408 transformRelativeOffset.GetY() + (videoNodeSize.Height() - videoFrameSize.Height()) / AVERAGE_VALUE,
1409 videoFrameSize.Width(), videoFrameSize.Height());
1410 if (renderSurface_ && (rect != lastBoundsRect_)) {
1411 renderSurface_->SetExtSurfaceBounds(rect.Left(), rect.Top(), rect.Width(), rect.Height());
1412 lastBoundsRect_ = rect;
1413 }
1414 }
1415 }
1416
1417 void VideoPattern::OnColorConfigurationUpdate()
1418 {
1419 ContainerScope scope(instanceId_);
1420 auto host = GetHost();
1421 CHECK_NULL_VOID(host);
1422 auto pipelineContext = PipelineBase::GetCurrentContext();
1423 CHECK_NULL_VOID(pipelineContext);
1424 auto videoTheme = pipelineContext->GetTheme<VideoTheme>();
1425 CHECK_NULL_VOID(videoTheme);
1426 auto renderContext = controlBar_->GetRenderContext();
1427 CHECK_NULL_VOID(renderContext);
1428 renderContext->UpdateBackgroundColor(videoTheme->GetBkgColor());
1429 for (const auto& child : controlBar_->GetChildren()) {
1430 if (child->GetTag() == V2::TEXT_ETS_TAG) {
1431 auto frameNode = AceType::DynamicCast<FrameNode>(child);
1432 if (frameNode) {
1433 auto textLayoutProperty = frameNode->GetLayoutProperty<TextLayoutProperty>();
1434 if (textLayoutProperty) {
1435 auto textStyle = videoTheme->GetTimeTextStyle();
1436 textLayoutProperty->UpdateTextColor(textStyle.GetTextColor());
1437 }
1438 }
1439 }
1440 }
1441 host->SetNeedCallChildrenUpdate(false);
1442 host->MarkModifyDone();
1443 host->MarkDirtyNode();
1444 }
1445
1446 bool VideoPattern::NeedLift() const
1447 {
1448 auto host = GetHost();
1449 CHECK_NULL_RETURN(host, false);
1450 auto renderContext = host->GetRenderContext();
1451 CHECK_NULL_RETURN(renderContext, false);
1452 return IsFullScreen() && renderContext->IsUniRenderEnabled();
1453 }
1454
1455 RefPtr<FrameNode> VideoPattern::CreateControlBar(int32_t nodeId)
1456 {
1457 ContainerScope scope(instanceId_);
1458 auto pipelineContext = PipelineBase::GetCurrentContext();
1459 CHECK_NULL_RETURN(pipelineContext, nullptr);
1460 auto videoTheme = pipelineContext->GetTheme<VideoTheme>();
1461 CHECK_NULL_RETURN(videoTheme, nullptr);
1462 auto controlBar = FrameNode::GetOrCreateFrameNode(
1463 V2::ROW_ETS_TAG, nodeId, []() { return AceType::MakeRefPtr<LinearLayoutPattern>(false); });
1464 CHECK_NULL_RETURN(controlBar, nullptr);
1465 controlBar_ = controlBar;
1466
1467 auto playButton = CreateSVG();
1468 CHECK_NULL_RETURN(playButton, nullptr);
1469 ChangePlayButtonTag(playButton);
1470 controlBar->AddChild(playButton);
1471
1472 auto currentPosText = CreateText(currentPos_);
1473 CHECK_NULL_RETURN(currentPosText, nullptr);
1474 controlBar->AddChild(currentPosText);
1475
1476 auto slider = CreateSlider();
1477 CHECK_NULL_RETURN(slider, nullptr);
1478 controlBar->AddChild(slider);
1479
1480 auto durationText = CreateText(duration_);
1481 CHECK_NULL_RETURN(durationText, nullptr);
1482 controlBar->AddChild(durationText);
1483
1484 auto fullScreenButton = CreateSVG();
1485 CHECK_NULL_RETURN(fullScreenButton, nullptr);
1486 SetFullScreenButtonCallBack(fullScreenButton);
1487 ChangeFullScreenButtonTag(InstanceOf<VideoFullScreenNode>(this), fullScreenButton);
1488 controlBar->AddChild(fullScreenButton);
1489
1490 auto renderContext = controlBar->GetRenderContext();
1491 renderContext->UpdateBackgroundColor(videoTheme->GetBkgColor());
1492 auto controlBarLayoutProperty = controlBar->GetLayoutProperty<LinearLayoutProperty>();
1493 controlBarLayoutProperty->UpdateMainAxisAlign(FlexAlign::SPACE_BETWEEN);
1494 if (NeedLift()) {
1495 PaddingProperty padding;
1496 padding.bottom = CalcLength(LIFT_HEIGHT);
1497 controlBarLayoutProperty->UpdatePadding(padding);
1498 }
1499 return controlBar;
1500 }
1501
1502 RefPtr<FrameNode> VideoPattern::CreateSlider()
1503 {
1504 auto pipelineContext = PipelineBase::GetCurrentContext();
1505 CHECK_NULL_RETURN(pipelineContext, nullptr);
1506 auto videoTheme = pipelineContext->GetTheme<VideoTheme>();
1507 CHECK_NULL_RETURN(videoTheme, nullptr);
1508
1509 auto sliderNode = FrameNode::CreateFrameNode(V2::SLIDER_ETS_TAG, -1, AceType::MakeRefPtr<SliderPattern>());
1510 CHECK_NULL_RETURN(sliderNode, nullptr);
1511 auto sliderLayoutProperty = sliderNode->GetLayoutProperty<SliderLayoutProperty>();
1512 CHECK_NULL_RETURN(sliderLayoutProperty, nullptr);
1513
1514 auto sliderEdge = videoTheme->GetSliderEdge();
1515 PaddingProperty padding;
1516 padding.left = CalcLength(sliderEdge.Left());
1517 padding.right = CalcLength(sliderEdge.Right());
1518 padding.top = CalcLength(sliderEdge.Top());
1519 padding.bottom = CalcLength(sliderEdge.Bottom());
1520 sliderLayoutProperty->UpdatePadding(padding);
1521 sliderLayoutProperty->UpdateLayoutWeight(1.0);
1522
1523 SliderOnChangeEvent sliderOnChangeEvent = [weak = WeakClaim(this)](float value, int32_t mode) {
1524 auto videoPattern = weak.Upgrade();
1525 CHECK_NULL_VOID(videoPattern);
1526 videoPattern->OnSliderChange(value, mode);
1527 };
1528 auto sliderEventHub = sliderNode->GetOrCreateEventHub<SliderEventHub>();
1529 sliderEventHub->SetOnChange(std::move(sliderOnChangeEvent));
1530 auto focusHub = sliderNode->GetOrCreateFocusHub();
1531 CHECK_NULL_RETURN(focusHub, nullptr);
1532 if (InstanceOf<VideoFullScreenPattern>(this)) {
1533 focusHub->SetIsDefaultFocus(true);
1534 }
1535 // slider has registered click event, so it will consume KEY_SPACE event
1536 // video needs register OnKeySpaceEvent extra
1537 focusHub->SetOnKeyCallback([weak = WeakClaim(this)](const KeyEventInfo& keyEvent) -> bool {
1538 auto videoPattern = weak.Upgrade();
1539 CHECK_NULL_RETURN(videoPattern, false);
1540 return videoPattern->HandleSliderKeyEvent(keyEvent);
1541 });
1542
1543 auto sliderPaintProperty = sliderNode->GetPaintProperty<SliderPaintProperty>();
1544 CHECK_NULL_RETURN(sliderPaintProperty, nullptr);
1545 sliderPaintProperty->UpdateMax(static_cast<float>(duration_));
1546 sliderPaintProperty->UpdateSelectGradientColor(ConvertToGradient(videoTheme->GetSelectColor()));
1547 sliderPaintProperty->UpdateSelectIsResourceColor(true);
1548 sliderPaintProperty->UpdateTrackBackgroundColor(ConvertToGradient(videoTheme->GetTrackBgColor()));
1549 sliderPaintProperty->UpdateTrackBackgroundIsResourceColor(true);
1550 sliderPaintProperty->UpdateValue(static_cast<float>(currentPos_));
1551 sliderNode->MarkModifyDone();
1552 return sliderNode;
1553 }
1554
1555 RefPtr<FrameNode> VideoPattern::CreateText(uint32_t time)
1556 {
1557 auto pipelineContext = PipelineBase::GetCurrentContext();
1558 CHECK_NULL_RETURN(pipelineContext, nullptr);
1559 auto videoTheme = pipelineContext->GetTheme<VideoTheme>();
1560 CHECK_NULL_RETURN(videoTheme, nullptr);
1561
1562 auto textNode = FrameNode::CreateFrameNode(V2::TEXT_ETS_TAG, -1, AceType::MakeRefPtr<TextPattern>());
1563 CHECK_NULL_RETURN(textNode, nullptr);
1564 auto textLayoutProperty = textNode->GetLayoutProperty<TextLayoutProperty>();
1565 CHECK_NULL_RETURN(textLayoutProperty, nullptr);
1566 auto videoLayoutProperty = GetLayoutProperty<VideoLayoutProperty>();
1567 CHECK_NULL_RETURN(videoLayoutProperty, nullptr);
1568 std::string label = "";
1569 if (videoLayoutProperty->GetControlsValue(true)) {
1570 label = IntTimeToText(time);
1571 }
1572 textLayoutProperty->UpdateContent(label);
1573 auto textEdge = videoTheme->GetTextEdge();
1574 PaddingProperty padding;
1575 padding.left = CalcLength(textEdge.Left());
1576 padding.right = CalcLength(textEdge.Right());
1577 padding.top = CalcLength(textEdge.Top());
1578 padding.bottom = CalcLength(textEdge.Bottom());
1579 textLayoutProperty->UpdatePadding(padding);
1580 auto textStyle = videoTheme->GetTimeTextStyle();
1581 textLayoutProperty->UpdateFontSize(textStyle.GetFontSize());
1582 textLayoutProperty->UpdateTextColor(textStyle.GetTextColor());
1583 return textNode;
1584 }
1585
1586 RefPtr<FrameNode> VideoPattern::CreateSVG()
1587 {
1588 auto pipelineContext = GetHost()->GetContext();
1589 CHECK_NULL_RETURN(pipelineContext, nullptr);
1590 auto videoTheme = pipelineContext->GetTheme<VideoTheme>();
1591 CHECK_NULL_RETURN(videoTheme, nullptr);
1592
1593 auto svgNode = FrameNode::CreateFrameNode(V2::IMAGE_ETS_TAG, -1, AceType::MakeRefPtr<ImagePattern>());
1594 CHECK_NULL_RETURN(svgNode, nullptr);
1595
1596 auto imageRenderProperty = svgNode->GetPaintPropertyPtr<ImageRenderProperty>();
1597 imageRenderProperty->UpdateSvgFillColor(videoTheme->GetIconColor());
1598 auto renderContext = svgNode->GetRenderContext();
1599 renderContext->UpdateForegroundColor(videoTheme->GetIconColor());
1600
1601 auto svgLayoutProperty = svgNode->GetLayoutProperty<ImageLayoutProperty>();
1602
1603 auto btnEdge = videoTheme->GetBtnEdge();
1604 PaddingProperty padding;
1605 padding.left = CalcLength(btnEdge.Left());
1606 padding.right = CalcLength(btnEdge.Right());
1607 padding.top = CalcLength(btnEdge.Top());
1608 padding.bottom = CalcLength(btnEdge.Bottom());
1609 svgLayoutProperty->UpdatePadding(padding);
1610
1611 auto btnSize = videoTheme->GetBtnSize();
1612 SizeF size { static_cast<float>(btnSize.Width()), static_cast<float>(btnSize.Height()) };
1613 svgLayoutProperty->UpdateMarginSelfIdealSize(size);
1614 auto width = Dimension(btnSize.Width(), DimensionUnit::VP).ConvertToPx();
1615 auto height = Dimension(btnSize.Height(), DimensionUnit::VP).ConvertToPx();
1616 CalcSize idealSize = { CalcLength(width), CalcLength(height) };
1617 MeasureProperty layoutConstraint;
1618 layoutConstraint.selfIdealSize = idealSize;
1619 layoutConstraint.maxSize = idealSize;
1620 svgNode->UpdateLayoutConstraint(layoutConstraint);
1621 return svgNode;
1622 }
1623
1624 void VideoPattern::SetStartImpl(
1625 const RefPtr<VideoController>& videoController, const SingleTaskExecutor& uiTaskExecutor)
1626 {
1627 videoController->SetStartImpl([weak = WeakClaim(this), uiTaskExecutor]() {
1628 uiTaskExecutor.PostTask([weak]() {
1629 auto pattern = weak.Upgrade();
1630 CHECK_NULL_VOID(pattern);
1631 ContainerScope scope(pattern->instanceId_);
1632 auto targetPattern = pattern->GetTargetVideoPattern();
1633 CHECK_NULL_VOID(targetPattern);
1634 targetPattern->Start();
1635 }, "ArkUIVideoStart");
1636 });
1637 }
1638
1639 void VideoPattern::SetPausetImpl(
1640 const RefPtr<VideoController>& videoController, const SingleTaskExecutor& uiTaskExecutor)
1641 {
1642 videoController->SetPausetImpl([weak = WeakClaim(this), uiTaskExecutor]() {
1643 uiTaskExecutor.PostTask([weak]() {
1644 auto pattern = weak.Upgrade();
1645 CHECK_NULL_VOID(pattern);
1646 ContainerScope scope(pattern->instanceId_);
1647 auto targetPattern = pattern->GetTargetVideoPattern();
1648 CHECK_NULL_VOID(targetPattern);
1649 targetPattern->Pause();
1650 }, "ArkUIVideoPause");
1651 });
1652 }
1653
1654 void VideoPattern::SetStopImpl(
1655 const RefPtr<VideoController>& videoController, const SingleTaskExecutor& uiTaskExecutor)
1656 {
1657 videoController->SetStopImpl([weak = WeakClaim(this), uiTaskExecutor]() {
1658 uiTaskExecutor.PostTask([weak]() {
1659 auto pattern = weak.Upgrade();
1660 CHECK_NULL_VOID(pattern);
1661 ContainerScope scope(pattern->instanceId_);
1662 auto targetPattern = pattern->GetTargetVideoPattern();
1663 CHECK_NULL_VOID(targetPattern);
1664 targetPattern->Stop();
1665 }, "ArkUIVideoStop");
1666 });
1667 }
1668
1669 void VideoPattern::SetSeekToImpl(
1670 const RefPtr<VideoController>& videoController, const SingleTaskExecutor& uiTaskExecutor)
1671 {
1672 videoController->SetSeekToImpl([weak = WeakClaim(this), uiTaskExecutor](float pos, SeekMode seekMode) {
1673 uiTaskExecutor.PostTask([weak, pos, seekMode]() {
1674 auto pattern = weak.Upgrade();
1675 CHECK_NULL_VOID(pattern);
1676 ContainerScope scope(pattern->instanceId_);
1677 auto targetPattern = pattern->GetTargetVideoPattern();
1678 CHECK_NULL_VOID(targetPattern);
1679 targetPattern->SetCurrentTime(pos, seekMode);
1680 }, "ArkUIVideoSetCurrentTime");
1681 });
1682 }
1683
1684 void VideoPattern::SetRequestFullscreenImpl(
1685 const RefPtr<VideoController>& videoController, const SingleTaskExecutor& uiTaskExecutor)
1686 {
1687 videoController->SetRequestFullscreenImpl([weak = WeakClaim(this), uiTaskExecutor](bool isFullScreen) {
1688 uiTaskExecutor.PostTask([weak, isFullScreen]() {
1689 auto videoPattern = weak.Upgrade();
1690 CHECK_NULL_VOID(videoPattern);
1691 ContainerScope scope(videoPattern->instanceId_);
1692 if (isFullScreen) {
1693 videoPattern->FullScreen();
1694 } else {
1695 videoPattern->ResetLastBoundsRect();
1696 auto targetPattern = videoPattern->GetTargetVideoPattern();
1697 CHECK_NULL_VOID(targetPattern);
1698 auto fullScreenPattern = AceType::DynamicCast<VideoFullScreenPattern>(targetPattern);
1699 CHECK_NULL_VOID(fullScreenPattern);
1700 fullScreenPattern->ExitFullScreen();
1701 }
1702 }, "ArkUIVideoFullScreen");
1703 });
1704 }
1705
1706 void VideoPattern::SetExitFullscreenImpl(
1707 const RefPtr<VideoController>& videoController, const SingleTaskExecutor& uiTaskExecutor)
1708 {
1709 videoController->SetExitFullscreenImpl([weak = WeakClaim(this), uiTaskExecutor](bool isSync) {
1710 if (isSync) {
1711 auto pattern = weak.Upgrade();
1712 CHECK_NULL_VOID(pattern);
1713 auto targetPattern = pattern->GetTargetVideoPattern();
1714 CHECK_NULL_VOID(targetPattern);
1715 pattern->ResetLastBoundsRect();
1716 auto fullScreenPattern = AceType::DynamicCast<VideoFullScreenPattern>(targetPattern);
1717 fullScreenPattern->ExitFullScreen();
1718 return;
1719 }
1720 uiTaskExecutor.PostTask([weak]() {
1721 auto pattern = weak.Upgrade();
1722 CHECK_NULL_VOID(pattern);
1723 ContainerScope scope(pattern->instanceId_);
1724 pattern->ResetLastBoundsRect();
1725 auto targetPattern = pattern->GetTargetVideoPattern();
1726 CHECK_NULL_VOID(targetPattern);
1727 auto fullScreenPattern = AceType::DynamicCast<VideoFullScreenPattern>(targetPattern);
1728 CHECK_NULL_VOID(fullScreenPattern);
1729 fullScreenPattern->ExitFullScreen();
1730 }, "ArkUIVideoExitFullScreen");
1731 });
1732 }
1733
1734 void VideoPattern::SetResetImpl(
1735 const RefPtr<VideoController>& videoController, const SingleTaskExecutor& uiTaskExecutor)
1736 {
1737 videoController->SetResetImpl([weak = WeakClaim(this), uiTaskExecutor]() {
1738 uiTaskExecutor.PostTask([weak]() {
1739 auto pattern = weak.Upgrade();
1740 CHECK_NULL_VOID(pattern);
1741 auto targetPattern = pattern->GetTargetVideoPattern();
1742 CHECK_NULL_VOID(targetPattern);
1743 targetPattern->ResetMediaPlayer();
1744 }, "ArkUIVideoReset");
1745 });
1746 }
1747
1748 void VideoPattern::SetMethodCall()
1749 {
1750 ContainerScope scope(instanceId_);
1751 auto videoController = AceType::MakeRefPtr<VideoController>();
1752 auto host = GetHost();
1753 CHECK_NULL_VOID(host);
1754 auto context = host->GetContext();
1755 CHECK_NULL_VOID(context);
1756 auto uiTaskExecutor = SingleTaskExecutor::Make(context->GetTaskExecutor(), TaskExecutor::TaskType::UI);
1757
1758 SetStartImpl(videoController, uiTaskExecutor);
1759 SetPausetImpl(videoController, uiTaskExecutor);
1760 SetStopImpl(videoController, uiTaskExecutor);
1761 SetSeekToImpl(videoController, uiTaskExecutor);
1762 SetRequestFullscreenImpl(videoController, uiTaskExecutor);
1763 SetExitFullscreenImpl(videoController, uiTaskExecutor);
1764 SetResetImpl(videoController, uiTaskExecutor);
1765
1766 CHECK_NULL_VOID(videoControllerV2_);
1767 videoControllerV2_->AddVideoController(videoController);
1768 }
1769
1770 void VideoPattern::Start()
1771 {
1772 if (!mediaPlayer_ || !mediaPlayer_->IsMediaPlayerValid()) {
1773 return;
1774 }
1775
1776 if (isStop_ && mediaPlayer_->PrepareAsync() != 0) {
1777 TAG_LOGW(AceLogTag::ACE_VIDEO, "Player has not prepared");
1778 return;
1779 }
1780 ContainerScope scope(instanceId_);
1781 auto host = GetHost();
1782 CHECK_NULL_VOID(host);
1783 auto context = host->GetContext();
1784 CHECK_NULL_VOID(context);
1785
1786 DestroyAnalyzerOverlay();
1787 isPaused_ = false;
1788
1789 auto bgTaskExecutor = SingleTaskExecutor::Make(context->GetTaskExecutor(), TaskExecutor::TaskType::BACKGROUND);
1790 bgTaskExecutor.PostTask(
1791 [weak = WeakClaim(RawPtr(mediaPlayer_)), hostId = hostId_] {
1792 auto mediaPlayer = weak.Upgrade();
1793 CHECK_NULL_VOID(mediaPlayer);
1794 TAG_LOGI(AceLogTag::ACE_VIDEO, "Video[%{public}d] trigger mediaPlayer play", hostId);
1795 mediaPlayer->Play();
1796 },
1797 "ArkUIVideoPlay");
1798 }
1799
1800 void VideoPattern::Pause()
1801 {
1802 if (!mediaPlayer_ || !mediaPlayer_->IsMediaPlayerValid()) {
1803 return;
1804 }
1805 TAG_LOGI(AceLogTag::ACE_VIDEO, "Video[%{public}d] trigger mediaPlayer pause", hostId_);
1806 auto ret = mediaPlayer_->Pause();
1807 if (ret != -1 && !isPaused_) {
1808 isPaused_ = true;
1809 StartImageAnalyzer();
1810 }
1811 }
1812
1813 void VideoPattern::Stop()
1814 {
1815 if (!mediaPlayer_ || !mediaPlayer_->IsMediaPlayerValid()) {
1816 return;
1817 }
1818 TAG_LOGI(AceLogTag::ACE_VIDEO, "Video[%{public}d] trigger mediaPlayer stop", hostId_);
1819 OnCurrentTimeChange(0);
1820 mediaPlayer_->Stop();
1821 isStop_ = true;
1822 SetIsSeeking(false);
1823 }
1824
1825 void VideoPattern::FireError(int32_t code, const std::string& message)
1826 {
1827 ContainerScope scope(instanceId_);
1828 auto host = GetHost();
1829 CHECK_NULL_VOID(host);
1830 auto context = host->GetContext();
1831 CHECK_NULL_VOID(context);
1832
1833 // OnError function must be excuted on ui, so get the uiTaskExecutor.
1834 auto task = [weak = WeakClaim(this), code, message] {
1835 auto videoPattern = weak.Upgrade();
1836 CHECK_NULL_VOID(videoPattern);
1837 ContainerScope scope(videoPattern->instanceId_);
1838 videoPattern->OnError(code, message);
1839 };
1840 auto uiTaskExecutor = SingleTaskExecutor::Make(context->GetTaskExecutor(), TaskExecutor::TaskType::UI);
1841 if (uiTaskExecutor.IsRunOnCurrentThread()) {
1842 task();
1843 } else {
1844 uiTaskExecutor.PostTask(task, "ArkUIVideoError");
1845 }
1846 }
1847
1848 void VideoPattern::ChangePlayButtonTag()
1849 {
1850 ContainerScope scope(instanceId_);
1851 auto host = GetHost();
1852 CHECK_NULL_VOID(host);
1853 auto context = host->GetContext();
1854 CHECK_NULL_VOID(context);
1855 const auto& children = host->GetChildren();
1856 for (const auto& child : children) {
1857 if (child->GetTag() == V2::ROW_ETS_TAG) {
1858 auto playBtn = DynamicCast<FrameNode>(child->GetChildAtIndex(0));
1859 ChangePlayButtonTag(playBtn);
1860 break;
1861 }
1862 }
1863 }
1864
1865 void VideoPattern::ChangePlayButtonTag(RefPtr<FrameNode>& playBtn)
1866 {
1867 CHECK_NULL_VOID(playBtn);
1868 auto playClickCallback = [weak = WeakClaim(this), playing = isPlaying_](GestureEvent& /* info */) {
1869 auto videoPattern = weak.Upgrade();
1870 CHECK_NULL_VOID(videoPattern);
1871 if (playing) {
1872 videoPattern->Pause();
1873 } else {
1874 videoPattern->Start();
1875 }
1876 };
1877 auto playBtnEvent = playBtn->GetOrCreateGestureEventHub();
1878 playBtnEvent->SetUserOnClick(std::move(playClickCallback));
1879 auto svgLayoutProperty = playBtn->GetLayoutProperty<ImageLayoutProperty>();
1880 auto resourceId = isPlaying_ ? InternalResource::ResourceId::PAUSE_SVG : InternalResource::ResourceId::PLAY_SVG;
1881 auto svgSourceInfo = ImageSourceInfo("");
1882 svgSourceInfo.SetResourceId(resourceId);
1883 svgLayoutProperty->UpdateImageSourceInfo(svgSourceInfo);
1884 playBtn->MarkModifyDone();
1885 playBtn->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF);
1886 }
1887
1888 void VideoPattern::SetFullScreenButtonCallBack(RefPtr<FrameNode>& fullScreenBtn)
1889 {
1890 CHECK_NULL_VOID(fullScreenBtn);
1891 auto fsClickCallback = [weak = WeakClaim(this)](GestureEvent& /* info */) {
1892 auto videoPattern = weak.Upgrade();
1893 CHECK_NULL_VOID(videoPattern);
1894 if (InstanceOf<VideoFullScreenPattern>(videoPattern)) {
1895 auto pattern = AceType::DynamicCast<VideoFullScreenPattern>(videoPattern);
1896 CHECK_NULL_VOID(pattern);
1897 videoPattern->ResetLastBoundsRect();
1898 pattern->ExitFullScreen();
1899 } else {
1900 videoPattern->FullScreen();
1901 }
1902 };
1903 auto fullScreenBtnEvent = fullScreenBtn->GetOrCreateGestureEventHub();
1904 fullScreenBtnEvent->SetUserOnClick(std::move(fsClickCallback));
1905 }
1906
1907 void VideoPattern::ChangeFullScreenButtonTag(bool isFullScreen, RefPtr<FrameNode>& fullScreenBtn)
1908 {
1909 CHECK_NULL_VOID(fullScreenBtn);
1910 auto svgLayoutProperty = fullScreenBtn->GetLayoutProperty<ImageLayoutProperty>();
1911 auto resourceId =
1912 isFullScreen ? InternalResource::ResourceId::QUIT_FULLSCREEN_SVG : InternalResource::ResourceId::FULLSCREEN_SVG;
1913 auto svgSourceInfo = ImageSourceInfo("");
1914 svgSourceInfo.SetResourceId(resourceId);
1915 svgLayoutProperty->UpdateImageSourceInfo(svgSourceInfo);
1916 fullScreenBtn->MarkModifyDone();
1917 fullScreenBtn->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF);
1918 }
1919
1920 void VideoPattern::SetCurrentTime(float currentPos, OHOS::Ace::SeekMode seekMode)
1921 {
1922 if (!mediaPlayer_ || !mediaPlayer_->IsMediaPlayerValid() || !isPrepared_) {
1923 return;
1924 }
1925 if (GreatOrEqual(currentPos, 0.0)) {
1926 SetIsSeeking(true);
1927 mediaPlayer_->Seek(static_cast<int32_t>(currentPos * MILLISECONDS_TO_SECONDS), seekMode);
1928 }
1929 }
1930
1931 void VideoPattern::OnSliderChange(float posTime, int32_t mode)
1932 {
1933 SetCurrentTime(posTime, OHOS::Ace::SeekMode::SEEK_CLOSEST);
1934 auto eventHub = GetOrCreateEventHub<VideoEventHub>();
1935 CHECK_NULL_VOID(eventHub);
1936 if (mode == SliderChangeMode::BEGIN || mode == SliderChangeMode::MOVING) {
1937 eventHub->FireSeekingEvent(static_cast<double>(posTime));
1938 } else if (mode == SliderChangeMode::END) {
1939 eventHub->FireSeekedEvent(static_cast<double>(posTime));
1940 }
1941 }
1942
1943 void VideoPattern::OnFullScreenChange(bool isFullScreen)
1944 {
1945 auto eventHub = GetOrCreateEventHub<VideoEventHub>();
1946 CHECK_NULL_VOID(eventHub);
1947 eventHub->FireFullScreenChangeEvent(isFullScreen);
1948 auto host = GetHost();
1949 CHECK_NULL_VOID(host);
1950 const auto& children = host->GetChildren();
1951 for (const auto& child : children) {
1952 if (child->GetTag() == V2::ROW_ETS_TAG) {
1953 auto fsBtn = DynamicCast<FrameNode>(child->GetChildAtIndex(FULL_SCREEN_POS));
1954 ChangeFullScreenButtonTag(isFullScreen, fsBtn);
1955 break;
1956 }
1957 }
1958
1959 if (!isFullScreen && mediaPlayer_ && mediaPlayer_->IsMediaPlayerValid()) {
1960 auto videoLayoutProperty = host->GetLayoutProperty<VideoLayoutProperty>();
1961 CHECK_NULL_VOID(videoLayoutProperty);
1962 SizeF videoSize = SizeF(
1963 static_cast<float>(mediaPlayer_->GetVideoWidth()),
1964 static_cast<float>(mediaPlayer_->GetVideoHeight()));
1965 videoLayoutProperty->UpdateVideoSize(videoSize);
1966 host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
1967 }
1968
1969 if (isEnableAnalyzer_) {
1970 if (!imageAnalyzerManager_) {
1971 EnableAnalyzer(isEnableAnalyzer_);
1972 }
1973 if (imageAnalyzerManager_ && isAnalyzerCreated_) {
1974 StartImageAnalyzer();
1975 }
1976 }
1977
1978 if (!SystemProperties::GetExtSurfaceEnabled()) {
1979 return;
1980 }
1981 if (!fullScreenNodeId_.has_value()) {
1982 SetMediaFullScreen(isFullScreen);
1983 return;
1984 }
1985 auto fullScreenNode = FrameNode::GetFrameNode(V2::VIDEO_ETS_TAG, fullScreenNodeId_.value());
1986 CHECK_NULL_VOID(fullScreenNode);
1987 auto fullScreenPattern = AceType::DynamicCast<VideoFullScreenPattern>(fullScreenNode->GetPattern());
1988 CHECK_NULL_VOID(fullScreenPattern);
1989 fullScreenPattern->SetMediaFullScreen(isFullScreen);
1990 }
1991
1992 void VideoPattern::FullScreen()
1993 {
1994 if (fullScreenNodeId_.has_value()) {
1995 return;
1996 }
1997 ResetLastBoundsRect();
1998 auto host = GetHost();
1999 CHECK_NULL_VOID(host);
2000 auto videoNode = AceType::DynamicCast<VideoNode>(host);
2001 CHECK_NULL_VOID(videoNode);
2002 auto fullScreenPattern = AceType::MakeRefPtr<VideoFullScreenPattern>(videoControllerV2_);
2003 fullScreenPattern->InitFullScreenParam(
2004 AceType::Claim(this), renderSurface_, mediaPlayer_, renderContextForMediaPlayer_);
2005 fullScreenNodeId_ = ElementRegister::GetInstance()->MakeUniqueId();
2006 auto fullScreenNode =
2007 VideoFullScreenNode::CreateFullScreenNode(V2::VIDEO_ETS_TAG, fullScreenNodeId_.value(), fullScreenPattern);
2008 CHECK_NULL_VOID(fullScreenNode);
2009 fullScreenPattern->RequestFullScreen(videoNode);
2010 }
2011
2012 VideoPattern::~VideoPattern()
2013 {
2014 #ifdef RENDER_EXTRACT_SUPPORTED
2015 if (renderContextForMediaPlayer_) {
2016 renderContextForMediaPlayer_->RemoveSurfaceChangedCallBack();
2017 }
2018 #endif
2019 if (IsSupportImageAnalyzer()) {
2020 DestroyAnalyzerOverlay();
2021 }
2022 if (!fullScreenNodeId_.has_value()) {
2023 return;
2024 }
2025 auto fullScreenNode = FrameNode::GetFrameNode(V2::VIDEO_ETS_TAG, fullScreenNodeId_.value());
2026 CHECK_NULL_VOID(fullScreenNode);
2027 auto parent = fullScreenNode->GetParent();
2028 CHECK_NULL_VOID(parent);
2029 parent->RemoveChild(fullScreenNode);
2030 parent->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF);
2031 }
2032
2033 void VideoPattern::RecoverState(const RefPtr<VideoPattern>& videoPattern)
2034 {
2035 CHECK_NULL_VOID(videoPattern);
2036 currentPos_ = videoPattern->GetCurrentPos();
2037 if (mediaPlayer_ && mediaPlayer_->IsMediaPlayerValid() && mediaPlayer_->IsPlaying() != isPlaying_) {
2038 isPlaying_ = mediaPlayer_->IsPlaying();
2039 ChangePlayButtonTag();
2040 }
2041 isInitialState_ = videoPattern->GetInitialState();
2042 auto layoutProperty = videoPattern->GetLayoutProperty<VideoLayoutProperty>();
2043 CHECK_NULL_VOID(layoutProperty);
2044 auto videoSrcInfo = layoutProperty->GetVideoSourceValue(VideoSourceInfo());
2045 videoSrcInfo_.src_ = videoSrcInfo.src_;
2046 videoSrcInfo_.bundleName_ = videoSrcInfo.bundleName_;
2047 videoSrcInfo_.moduleName_ = videoSrcInfo.moduleName_;
2048 isPrepared_ = videoPattern->GetIsPrepared();
2049 isSeeking_ = videoPattern->GetIsSeeking();
2050 isStop_ = videoPattern->GetIsStop();
2051 muted_ = videoPattern->GetMuted();
2052 autoPlay_ = videoPattern->GetAutoPlay();
2053 loop_ = videoPattern->GetLoop();
2054 duration_ = videoPattern->GetDuration();
2055 showFirstFrame_ = videoPattern->showFirstFrame_;
2056 progressRate_ = videoPattern->GetProgressRate();
2057 isAnalyzerCreated_ = videoPattern->GetAnalyzerState();
2058 isEnableAnalyzer_ = videoPattern->isEnableAnalyzer_;
2059 SetShortcutKeyEnabled(videoPattern->GetShortcutKeyEnabled());
2060 SetCurrentVolume(videoPattern->GetCurrentVolume());
2061
2062 fullScreenNodeId_.reset();
2063 RegisterMediaPlayerEvent(WeakClaim(this), mediaPlayer_, videoSrcInfo_.src_, instanceId_);
2064 auto videoNode = GetHost();
2065 CHECK_NULL_VOID(videoNode);
2066 // change event hub to the origin video node
2067 videoPattern->GetOrCreateEventHub<VideoEventHub>()->AttachHost(videoNode);
2068 videoNode->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF_AND_CHILD);
2069 }
2070
2071 void VideoPattern::UpdateFsState()
2072 {
2073 if (!fullScreenNodeId_.has_value()) {
2074 return;
2075 }
2076 auto videoNode = FrameNode::GetFrameNode(V2::VIDEO_ETS_TAG, fullScreenNodeId_.value());
2077 CHECK_NULL_VOID(videoNode);
2078 auto videoPattern = AceType::DynamicCast<VideoFullScreenPattern>(videoNode->GetPattern());
2079 CHECK_NULL_VOID(videoPattern);
2080 // update full screen state
2081 videoPattern->UpdateState();
2082 }
2083
2084 bool VideoPattern::IsFullScreen() const
2085 {
2086 return fullScreenNodeId_.has_value();
2087 }
2088
2089 RefPtr<VideoPattern> VideoPattern::GetTargetVideoPattern()
2090 {
2091 auto isFullScreen = IsFullScreen();
2092 auto patternIsFullScreen = AceType::InstanceOf<VideoFullScreenPattern>(this);
2093 if ((isFullScreen && patternIsFullScreen) || (!isFullScreen && !patternIsFullScreen)) {
2094 return AceType::Claim(this);
2095 }
2096 if (patternIsFullScreen) {
2097 // current is full screen,need to be released
2098 auto fullScreenPattern = AceType::DynamicCast<VideoFullScreenPattern>(this);
2099 CHECK_NULL_RETURN(fullScreenPattern, nullptr);
2100 return fullScreenPattern->GetVideoPattern();
2101 }
2102 // current node is origin video node, need to operate full screen node
2103 auto fullScreenNode = GetFullScreenNode();
2104 CHECK_NULL_RETURN(fullScreenNode, nullptr);
2105 return fullScreenNode->GetPattern<VideoPattern>();
2106 }
2107
2108 void VideoPattern::EnableAnalyzer(bool enable)
2109 {
2110 isEnableAnalyzer_ = enable;
2111 if (!isEnableAnalyzer_) {
2112 DestroyAnalyzerOverlay();
2113 return;
2114 }
2115
2116 CHECK_NULL_VOID(!imageAnalyzerManager_);
2117 auto host = GetHost();
2118 CHECK_NULL_VOID(host);
2119 imageAnalyzerManager_ = std::make_shared<ImageAnalyzerManager>(host, ImageAnalyzerHolder::VIDEO_CUSTOM);
2120 }
2121
2122 void VideoPattern::SetShortcutKeyEnabled(bool isEnableShortcutKey)
2123 {
2124 isEnableShortcutKey_ = isEnableShortcutKey;
2125 }
2126
2127 bool VideoPattern::GetShortcutKeyEnabled() const
2128 {
2129 return isEnableShortcutKey_;
2130 }
2131
2132 void VideoPattern::SetCurrentVolume(float currentVolume)
2133 {
2134 currentVolume_ = currentVolume;
2135 }
2136
2137 float VideoPattern::GetCurrentVolume() const
2138 {
2139 return currentVolume_;
2140 }
2141
2142 void VideoPattern::SetImageAnalyzerConfig(void* config)
2143 {
2144 if (isEnableAnalyzer_) {
2145 CHECK_NULL_VOID(imageAnalyzerManager_);
2146 imageAnalyzerManager_->SetImageAnalyzerConfig(config);
2147 }
2148 }
2149
2150 void VideoPattern::SetImageAIOptions(void* options)
2151 {
2152 if (!imageAnalyzerManager_) {
2153 imageAnalyzerManager_ = std::make_shared<ImageAnalyzerManager>(GetHost(), ImageAnalyzerHolder::VIDEO_CUSTOM);
2154 }
2155 CHECK_NULL_VOID(imageAnalyzerManager_);
2156 imageAnalyzerManager_->SetImageAIOptions(options);
2157 }
2158
2159 bool VideoPattern::IsSupportImageAnalyzer()
2160 {
2161 auto host = GetHost();
2162 CHECK_NULL_RETURN(host, false);
2163 auto layoutProperty = host->GetLayoutProperty<VideoLayoutProperty>();
2164 CHECK_NULL_RETURN(layoutProperty, false);
2165 bool needControlBar = layoutProperty->GetControlsValue(true);
2166 CHECK_NULL_RETURN(imageAnalyzerManager_, false);
2167 return isEnableAnalyzer_ && !needControlBar && imageAnalyzerManager_->IsSupportImageAnalyzerFeature();
2168 }
2169
2170 bool VideoPattern::ShouldUpdateImageAnalyzer()
2171 {
2172 auto layoutProperty = GetLayoutProperty<VideoLayoutProperty>();
2173 CHECK_NULL_RETURN(layoutProperty, false);
2174 const auto& constraint = layoutProperty->GetCalcLayoutConstraint();
2175 if (!constraint || !constraint->selfIdealSize.has_value() || !constraint->selfIdealSize->IsValid()) {
2176 return false;
2177 }
2178 auto selfIdealSize = constraint->selfIdealSize;
2179 if (!selfIdealSize->PercentWidth() && !selfIdealSize->PercentHeight()) {
2180 return false;
2181 }
2182 auto imageFit = layoutProperty->GetObjectFit().value_or(ImageFit::COVER);
2183 if (imageFit != ImageFit::COVER && imageFit != ImageFit::NONE) {
2184 return false;
2185 }
2186 return true;
2187 }
2188
2189 void VideoPattern::StartImageAnalyzer()
2190 {
2191 if (!IsSupportImageAnalyzer() || !imageAnalyzerManager_) {
2192 return;
2193 }
2194
2195 if (imageAnalyzerManager_->IsOverlayCreated()) {
2196 DestroyAnalyzerOverlay();
2197 }
2198
2199 ContainerScope scope(instanceId_);
2200 auto host = GetHost();
2201 CHECK_NULL_VOID(host);
2202 auto context = host->GetContext();
2203 CHECK_NULL_VOID(context);
2204 auto uiTaskExecutor = SingleTaskExecutor::Make(context->GetTaskExecutor(), TaskExecutor::TaskType::UI);
2205 uiTaskExecutor.PostDelayedTask([weak = WeakClaim(this)] {
2206 auto pattern = weak.Upgrade();
2207 CHECK_NULL_VOID(pattern);
2208 pattern->CreateAnalyzerOverlay();
2209 }, ANALYZER_DELAY_TIME, "ArkUIVideoCreateAnalyzerOverlay");
2210 }
2211
2212 void VideoPattern::CreateAnalyzerOverlay()
2213 {
2214 auto host = GetHost();
2215 CHECK_NULL_VOID(host);
2216 host->SetOverlayNode(nullptr);
2217 auto context = host->GetRenderContext();
2218 CHECK_NULL_VOID(context);
2219 auto nailPixelMap = context->GetThumbnailPixelMap();
2220 CHECK_NULL_VOID(nailPixelMap);
2221 auto pixelMap = nailPixelMap->GetCropPixelMap(contentRect_);
2222 CHECK_NULL_VOID(pixelMap);
2223 auto layoutProperty = GetLayoutProperty<VideoLayoutProperty>();
2224 CHECK_NULL_VOID(layoutProperty);
2225 auto padding = layoutProperty->CreatePaddingAndBorder();
2226 OffsetF contentOffset = { contentRect_.Left() - padding.left.value_or(0),
2227 contentRect_.Top() - padding.top.value_or(0) };
2228 CHECK_NULL_VOID(imageAnalyzerManager_);
2229 imageAnalyzerManager_->CreateAnalyzerOverlay(pixelMap, contentOffset);
2230 }
2231
2232 void VideoPattern::StartUpdateImageAnalyzer()
2233 {
2234 CHECK_NULL_VOID(imageAnalyzerManager_);
2235 if (!imageAnalyzerManager_->IsOverlayCreated()) {
2236 return;
2237 }
2238
2239 UpdateOverlayVisibility(VisibleType::GONE);
2240 ContainerScope scope(instanceId_);
2241 auto host = GetHost();
2242 CHECK_NULL_VOID(host);
2243 auto context = host->GetContext();
2244 CHECK_NULL_VOID(context);
2245 auto uiTaskExecutor = SingleTaskExecutor::Make(context->GetTaskExecutor(), TaskExecutor::TaskType::UI);
2246 uiTaskExecutor.PostDelayedTask([weak = WeakClaim(this)] {
2247 auto pattern = weak.Upgrade();
2248 CHECK_NULL_VOID(pattern);
2249 if (!pattern->isContentSizeChanged_) {
2250 return;
2251 }
2252 pattern->UpdateAnalyzerOverlay();
2253 pattern->isContentSizeChanged_ = false;
2254 }, ANALYZER_CAPTURE_DELAY_TIME, "ArkUIVideoUpdateAnalyzerOverlay");
2255 isContentSizeChanged_ = true;
2256 }
2257
2258 void VideoPattern::UpdateAnalyzerOverlay()
2259 {
2260 auto host = GetHost();
2261 CHECK_NULL_VOID(host);
2262 auto context = host->GetRenderContext();
2263 CHECK_NULL_VOID(context);
2264 auto nailPixelMap = context->GetThumbnailPixelMap();
2265 CHECK_NULL_VOID(nailPixelMap);
2266 auto pixelMap = nailPixelMap->GetCropPixelMap(contentRect_);
2267 CHECK_NULL_VOID(pixelMap);
2268 UpdateOverlayVisibility(VisibleType::VISIBLE);
2269
2270 auto layoutProperty = GetLayoutProperty<VideoLayoutProperty>();
2271 CHECK_NULL_VOID(layoutProperty);
2272 auto padding = layoutProperty->CreatePaddingAndBorder();
2273 OffsetF contentOffset = { contentRect_.Left() - padding.left.value_or(0),
2274 contentRect_.Top() - padding.top.value_or(0) };
2275 CHECK_NULL_VOID(imageAnalyzerManager_);
2276 imageAnalyzerManager_->UpdateAnalyzerOverlay(pixelMap, contentOffset);
2277 }
2278
2279 void VideoPattern::UpdateAnalyzerUIConfig(const RefPtr<NG::GeometryNode>& geometryNode)
2280 {
2281 if (IsSupportImageAnalyzer()) {
2282 auto layoutProperty = GetLayoutProperty<VideoLayoutProperty>();
2283 CHECK_NULL_VOID(layoutProperty);
2284 auto padding = layoutProperty->CreatePaddingAndBorder();
2285 OffsetF contentOffset = { contentRect_.Left() - padding.left.value_or(0),
2286 contentRect_.Top() - padding.top.value_or(0) };
2287 PixelMapInfo info = { contentRect_.GetSize().Width(), contentRect_.GetSize().Height(), contentOffset };
2288 CHECK_NULL_VOID(imageAnalyzerManager_);
2289 imageAnalyzerManager_->UpdateAnalyzerUIConfig(geometryNode, info);
2290 }
2291 }
2292
2293 void VideoPattern::DestroyAnalyzerOverlay()
2294 {
2295 CHECK_NULL_VOID(imageAnalyzerManager_);
2296 imageAnalyzerManager_->DestroyAnalyzerOverlay();
2297 }
2298
2299 bool VideoPattern::GetAnalyzerState()
2300 {
2301 CHECK_NULL_RETURN(imageAnalyzerManager_, false);
2302 return imageAnalyzerManager_->IsOverlayCreated();
2303 }
2304
2305 void VideoPattern::UpdateOverlayVisibility(VisibleType type)
2306 {
2307 auto host = GetHost();
2308 CHECK_NULL_VOID(host);
2309 auto overlayNode = host->GetOverlayNode();
2310 CHECK_NULL_VOID(overlayNode);
2311 auto prop = overlayNode->GetLayoutProperty();
2312 CHECK_NULL_VOID(prop);
2313 prop->UpdateVisibility(type);
2314 }
2315
2316 void VideoPattern::OnWindowHide()
2317 {
2318 #if defined(OHOS_PLATFORM)
2319 if (!BackgroundTaskHelper::GetInstance().HasBackgroundTask()) {
2320 autoPlay_ = false;
2321 Pause();
2322 }
2323 #else
2324 Pause();
2325 #endif
2326 }
2327
2328 void VideoPattern::ToJsonValue(std::unique_ptr<JsonValue>& json, const InspectorFilter& filter) const
2329 {
2330 Pattern::ToJsonValue(json, filter);
2331 if (filter.IsFastFilter()) {
2332 return;
2333 }
2334
2335 json->PutExtAttr("enableAnalyzer", isEnableAnalyzer_ ? "true" : "false", filter);
2336 json->PutExtAttr("currentProgressRate", progressRate_, filter);
2337 json->PutExtAttr("surfaceBackgroundColor",
2338 renderContextForMediaPlayer_
2339 ? renderContextForMediaPlayer_->GetBackgroundColorValue(Color::BLACK).ColorToString().c_str()
2340 : "",
2341 filter);
2342 json->PutExtAttr("enableShortcutKey", isEnableShortcutKey_ ? "true" : "false", filter);
2343 }
2344
2345 bool VideoPattern::ParseCommand(const std::string& command)
2346 {
2347 auto json = JsonUtil::ParseJsonString(command);
2348 if (!json || json->IsNull()) {
2349 return false;
2350 }
2351 std::string value = json->GetString("cmd");
2352 return value == "play";
2353 }
2354
2355 int32_t VideoPattern::OnInjectionEvent(const std::string& command)
2356 {
2357 TAG_LOGD(AceLogTag::ACE_VIDEO, "OnInjectionEvent command : %{public}s", command.c_str());
2358 auto host = GetHost();
2359 CHECK_NULL_RETURN(host, RET_FAILED);
2360 auto pattern = host->GetPattern<VideoPattern>();
2361 CHECK_NULL_RETURN(pattern, RET_FAILED);
2362 if (!ParseCommand(command)) {
2363 return RET_FAILED;
2364 }
2365 pattern->Start();
2366 return RET_SUCCESS;
2367 }
2368
2369 void VideoPattern::SetVideoController(const RefPtr<VideoControllerV2>& videoController)
2370 {
2371 if (videoControllerV2_) {
2372 // Video Controller is already attached
2373 return;
2374 }
2375 videoControllerV2_ = videoController;
2376
2377 // if pattern is attached to frame node
2378 auto frameNode = frameNode_.Upgrade();
2379 CHECK_NULL_VOID(frameNode);
2380 // full screen node is not supposed to register js controller event
2381 if (!InstanceOf<VideoFullScreenPattern>(this)) {
2382 SetMethodCall();
2383 }
2384 }
2385
2386 RefPtr<VideoControllerV2> VideoPattern::GetVideoController()
2387 {
2388 return videoControllerV2_;
2389 }
2390 } // namespace OHOS::Ace::NG
2391