2 * Copyright (c) 2021 Samsung Electronics Co., Ltd.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
19 #include "video-view-impl.h"
22 #include <dali/devel-api/actors/actor-devel.h>
23 #include <dali/devel-api/adaptor-framework/window-devel.h>
24 #include <dali/devel-api/scripting/scripting.h>
25 #include <dali/integration-api/debug.h>
26 #include <dali/public-api/adaptor-framework/native-image-source.h>
27 #include <dali/public-api/animation/constraint.h>
28 #include <dali/public-api/object/type-registry-helper.h>
29 #include <dali/public-api/object/type-registry.h>
33 #include <dali-toolkit/internal/controls/control/control-data-impl.h>
34 #include <dali-toolkit/internal/graphics/builtin-shader-extern-gen.h>
35 #include <dali-toolkit/internal/visuals/visual-factory-cache.h>
36 #include <dali-toolkit/public-api/controls/video-view/video-view.h>
37 #include <dali/integration-api/adaptor-framework/adaptor.h>
49 return Toolkit::VideoView::New();
52 DALI_TYPE_REGISTRATION_BEGIN(Toolkit::VideoView, Toolkit::Control, Create);
54 DALI_PROPERTY_REGISTRATION(Toolkit, VideoView, "video", MAP, VIDEO)
55 DALI_PROPERTY_REGISTRATION(Toolkit, VideoView, "looping", BOOLEAN, LOOPING)
56 DALI_PROPERTY_REGISTRATION(Toolkit, VideoView, "muted", BOOLEAN, MUTED)
57 DALI_PROPERTY_REGISTRATION(Toolkit, VideoView, "volume", MAP, VOLUME)
58 DALI_PROPERTY_REGISTRATION(Toolkit, VideoView, "underlay", BOOLEAN, UNDERLAY)
59 DALI_PROPERTY_REGISTRATION(Toolkit, VideoView, "playPosition", INTEGER, PLAY_POSITION)
60 DALI_PROPERTY_REGISTRATION(Toolkit, VideoView, "displayMode", INTEGER, DISPLAY_MODE)
62 DALI_SIGNAL_REGISTRATION(Toolkit, VideoView, "finished", FINISHED_SIGNAL)
64 DALI_ACTION_REGISTRATION(Toolkit, VideoView, "play", ACTION_VIDEOVIEW_PLAY)
65 DALI_ACTION_REGISTRATION(Toolkit, VideoView, "pause", ACTION_VIDEOVIEW_PAUSE)
66 DALI_ACTION_REGISTRATION(Toolkit, VideoView, "stop", ACTION_VIDEOVIEW_STOP)
67 DALI_ACTION_REGISTRATION(Toolkit, VideoView, "forward", ACTION_VIDEOVIEW_FORWARD)
68 DALI_ACTION_REGISTRATION(Toolkit, VideoView, "backward", ACTION_VIDEOVIEW_BACKWARD)
70 DALI_TYPE_REGISTRATION_END()
72 const char* const VOLUME_LEFT("volumeLeft");
73 const char* const VOLUME_RIGHT("volumeRight");
75 // 3.0 TC uses RENDERING_TARGET. It should be removed in next release
76 const char* const RENDERING_TARGET("renderingTarget");
77 const char* const WINDOW_SURFACE_TARGET("windowSurfaceTarget");
78 const char* const NATIVE_IMAGE_TARGET("nativeImageTarget");
80 const char* const CUSTOM_SHADER("shader");
81 const char* const CUSTOM_VERTEX_SHADER("vertexShader");
82 const char* const CUSTOM_FRAGMENT_SHADER("fragmentShader");
83 const char* const DEFAULT_SAMPLER_TYPE_NAME("sampler2D");
84 const char* const CUSTOM_SAMPLER_TYPE_NAME("samplerExternalOES");
88 VideoView::VideoView(Dali::VideoSyncMode syncMode)
89 : Control(ControlBehaviour(ACTOR_BEHAVIOUR_DEFAULT | DISABLE_STYLE_CHANGE_SIGNALS)),
90 mCurrentVideoPlayPosition(0),
98 VideoView::~VideoView()
102 Toolkit::VideoView VideoView::New(VideoSyncMode syncMode)
104 VideoView* impl = new VideoView(syncMode);
105 Toolkit::VideoView handle = Toolkit::VideoView(*impl);
107 impl->mVideoPlayer = Dali::VideoPlayer::New(impl->Self(), syncMode);
112 void VideoView::OnInitialize()
114 mVideoPlayer.FinishedSignal().Connect(this, &VideoView::EmitSignalFinish);
116 DevelControl::SetAccessibilityConstructor(Self(), [](Dali::Actor actor) {
117 return std::unique_ptr<Dali::Accessibility::Accessible>(
118 new Control::Impl::AccessibleImpl(actor, Dali::Accessibility::Role::VIDEO));
122 void VideoView::SetUrl(const std::string& url)
125 mPropertyMap.Clear();
127 mVideoPlayer.SetUrl(mUrl);
130 void VideoView::SetPropertyMap(Property::Map map)
134 Property::Value* target = map.Find(RENDERING_TARGET);
135 std::string targetType;
137 if(target && target->Get(targetType) && targetType == WINDOW_SURFACE_TARGET)
140 SetWindowSurfaceTarget();
142 else if(target && target->Get(targetType) && targetType == NATIVE_IMAGE_TARGET)
145 SetNativeImageTarget();
149 Property::Value* shaderValue;
152 shaderValue = map.Find(CUSTOM_SHADER);
156 Property::Map* shaderMap = shaderValue->GetMap();
159 mEffectPropertyMap = *shaderMap;
164 if(mTextureRenderer && !mEffectPropertyMap.Empty())
166 Dali::Shader shader = CreateShader();
167 mTextureRenderer.SetShader(shader);
173 std::string VideoView::GetUrl()
178 void VideoView::SetLooping(bool looping)
180 mVideoPlayer.SetLooping(looping);
183 bool VideoView::IsLooping()
185 return mVideoPlayer.IsLooping();
188 void VideoView::Play()
194 void VideoView::Pause()
196 mVideoPlayer.Pause();
200 void VideoView::Stop()
206 void VideoView::Forward(int millisecond)
208 int curPos = mVideoPlayer.GetPlayPosition();
210 int nextPos = curPos + millisecond;
212 mVideoPlayer.SetPlayPosition(nextPos);
215 void VideoView::Backward(int millisecond)
217 int curPos = mVideoPlayer.GetPlayPosition();
219 int nextPos = curPos - millisecond;
220 nextPos = (nextPos < 0) ? 0 : nextPos;
222 mVideoPlayer.SetPlayPosition(nextPos);
225 void VideoView::SetMute(bool mute)
227 mVideoPlayer.SetMute(mute);
230 bool VideoView::IsMuted()
232 return mVideoPlayer.IsMuted();
235 void VideoView::SetVolume(float left, float right)
237 mVideoPlayer.SetVolume(left, right);
240 void VideoView::GetVolume(float& left, float& right)
242 mVideoPlayer.GetVolume(left, right);
245 Dali::Toolkit::VideoView::VideoViewSignalType& VideoView::FinishedSignal()
247 return mFinishedSignal;
250 void VideoView::EmitSignalFinish()
252 if(!mFinishedSignal.Empty())
254 Dali::Toolkit::VideoView handle(GetOwner());
255 mFinishedSignal.Emit(handle);
259 bool VideoView::DoAction(BaseObject* object, const std::string& actionName, const Property::Map& attributes)
263 Dali::BaseHandle handle(object);
264 Toolkit::VideoView videoView = Toolkit::VideoView::DownCast(handle);
271 VideoView& impl = GetImpl(videoView);
273 if(strcmp(actionName.c_str(), ACTION_VIDEOVIEW_PLAY) == 0)
278 else if(strcmp(actionName.c_str(), ACTION_VIDEOVIEW_PAUSE) == 0)
283 else if(strcmp(actionName.c_str(), ACTION_VIDEOVIEW_STOP) == 0)
288 else if(strcmp(actionName.c_str(), ACTION_VIDEOVIEW_FORWARD) == 0)
291 if(attributes["videoForward"].Get(millisecond))
293 impl.Forward(millisecond);
297 else if(strcmp(actionName.c_str(), ACTION_VIDEOVIEW_BACKWARD) == 0)
300 if(attributes["videoBackward"].Get(millisecond))
302 impl.Backward(millisecond);
310 bool VideoView::DoConnectSignal(BaseObject* object, ConnectionTrackerInterface* tracker, const std::string& signalName, FunctorDelegate* functor)
312 Dali::BaseHandle handle(object);
314 bool connected(true);
315 Toolkit::VideoView videoView = Toolkit::VideoView::DownCast(handle);
317 if(0 == strcmp(signalName.c_str(), FINISHED_SIGNAL))
319 videoView.FinishedSignal().Connect(tracker, functor);
323 // signalName does not match any signal
330 void VideoView::SetPropertyInternal(Property::Index index, const Property::Value& value)
334 case Toolkit::VideoView::Property::VIDEO:
336 std::string videoUrl;
339 if(value.Get(videoUrl))
343 else if(value.Get(map))
349 case Toolkit::VideoView::Property::LOOPING:
352 if(value.Get(looping))
358 case Toolkit::VideoView::Property::MUTED:
367 case Toolkit::VideoView::Property::VOLUME:
373 Property::Value* volumeLeft = map.Find(VOLUME_LEFT);
374 Property::Value* volumeRight = map.Find(VOLUME_RIGHT);
375 if(volumeLeft && volumeLeft->Get(left) && volumeRight && volumeRight->Get(right))
377 SetVolume(left, right);
382 case Toolkit::VideoView::Property::UNDERLAY:
385 if(value.Get(underlay))
387 SetUnderlay(underlay);
391 case Toolkit::VideoView::Property::PLAY_POSITION:
396 SetPlayPosition(pos);
400 case Toolkit::VideoView::Property::DISPLAY_MODE:
405 SetDisplayMode(mode);
412 void VideoView::SetProperty(BaseObject* object, Property::Index index, const Property::Value& value)
414 Toolkit::VideoView videoView = Toolkit::VideoView::DownCast(Dali::BaseHandle(object));
418 VideoView& impl = GetImpl(videoView);
420 impl.SetPropertyInternal(index, value);
422 if(index != Toolkit::VideoView::Property::UNDERLAY)
425 // These values will be used when underlay mode is changed.
426 impl.mPropertyBackup[index] = value;
431 Property::Value VideoView::GetProperty(BaseObject* object, Property::Index propertyIndex)
433 Property::Value value;
434 Toolkit::VideoView videoView = Toolkit::VideoView::DownCast(Dali::BaseHandle(object));
438 VideoView& impl = GetImpl(videoView);
440 switch(propertyIndex)
442 case Toolkit::VideoView::Property::VIDEO:
444 if(!impl.mUrl.empty())
448 else if(!impl.mPropertyMap.Empty())
450 value = impl.mPropertyMap;
454 case Toolkit::VideoView::Property::LOOPING:
456 value = impl.IsLooping();
459 case Toolkit::VideoView::Property::MUTED:
461 value = impl.IsMuted();
464 case Toolkit::VideoView::Property::VOLUME:
469 impl.GetVolume(left, right);
470 map.Insert(VOLUME_LEFT, left);
471 map.Insert(VOLUME_RIGHT, right);
475 case Toolkit::VideoView::Property::UNDERLAY:
477 value = impl.IsUnderlay();
480 case Toolkit::VideoView::Property::PLAY_POSITION:
482 value = impl.GetPlayPosition();
485 case Toolkit::VideoView::Property::DISPLAY_MODE:
487 value = impl.GetDisplayMode();
496 void VideoView::SetDepthIndex(int depthIndex)
500 mTextureRenderer.SetProperty(Renderer::Property::DEPTH_INDEX, depthIndex);
504 void VideoView::OnSceneConnection(int depth)
506 Control::OnSceneConnection(depth);
510 SetWindowSurfaceTarget();
514 void VideoView::OnSceneDisconnection()
516 Control::OnSceneDisconnection();
519 void VideoView::OnSizeSet(const Vector3& targetSize)
521 if(mIsUnderlay && mSyncMode == Dali::VideoSyncMode::ENABLED)
523 SetFrameRenderCallback();
524 mVideoPlayer.StartSynchronization();
526 Control::OnSizeSet(targetSize);
529 Vector3 VideoView::GetNaturalSize()
532 size.x = mVideoSize.GetWidth();
533 size.y = mVideoSize.GetHeight();
535 if(size.x > 0 && size.y > 0)
537 size.z = std::min(size.x, size.y);
542 return Control::GetNaturalSize();
546 float VideoView::GetHeightForWidth(float width)
548 if(mVideoSize.GetWidth() > 0 && mVideoSize.GetHeight() > 0)
550 return GetHeightForWidthBase(width);
554 return Control::GetHeightForWidthBase(width);
558 float VideoView::GetWidthForHeight(float height)
560 if(mVideoSize.GetWidth() > 0 && mVideoSize.GetHeight() > 0)
562 return GetWidthForHeightBase(height);
566 return Control::GetWidthForHeightBase(height);
570 void VideoView::SetWindowSurfaceTarget()
574 if(!self.GetProperty<bool>(Actor::Property::CONNECTED_TO_SCENE))
576 // When the control is off the stage, it does not have Window.
580 int curPos = mVideoPlayer.GetPlayPosition();
584 mVideoPlayer.Pause();
587 mPositionUpdateNotification = self.AddPropertyNotification(Actor::Property::WORLD_POSITION, StepCondition(1.0f, 1.0f));
588 mSizeUpdateNotification = self.AddPropertyNotification(Actor::Property::SIZE, StepCondition(1.0f, 1.0f));
589 mScaleUpdateNotification = self.AddPropertyNotification(Actor::Property::WORLD_SCALE, StepCondition(0.1f, 1.0f));
590 mPositionUpdateNotification.NotifySignal().Connect(this, &VideoView::UpdateDisplayArea);
591 mSizeUpdateNotification.NotifySignal().Connect(this, &VideoView::UpdateDisplayArea);
592 mScaleUpdateNotification.NotifySignal().Connect(this, &VideoView::UpdateDisplayArea);
596 self.RemoveRenderer(mTextureRenderer);
599 // Note VideoPlayer::SetRenderingTarget resets all the options. (e.g. url, mute, looping)
600 mVideoPlayer.SetRenderingTarget(Dali::Adaptor::Get().GetNativeWindowHandle(self));
602 ApplyBackupProperties();
604 if(!mOverlayRenderer)
606 // For underlay rendering mode, video display area have to be transparent.
607 Geometry geometry = VisualFactoryCache::CreateQuadGeometry();
608 Shader shader = Shader::New(SHADER_VIDEO_VIEW_VERT, SHADER_VIDEO_VIEW_FRAG);
609 mOverlayRenderer = Renderer::New(geometry, shader);
610 mOverlayRenderer.SetProperty(Renderer::Property::BLEND_MODE, BlendMode::OFF);
612 Self().AddRenderer(mOverlayRenderer);
621 mVideoPlayer.SetPlayPosition(curPos);
625 void VideoView::SetNativeImageTarget()
627 if(mVideoPlayer.IsVideoTextureSupported() == false)
629 DALI_LOG_ERROR("Platform doesn't support decoded video frame images\n");
636 mVideoPlayer.Pause();
643 self.RemoveRenderer(mOverlayRenderer);
645 mOverlayRenderer.Reset();
648 self.RemovePropertyNotification(mPositionUpdateNotification);
649 self.RemovePropertyNotification(mSizeUpdateNotification);
650 self.RemovePropertyNotification(mScaleUpdateNotification);
652 int curPos = mVideoPlayer.GetPlayPosition();
655 Dali::NativeImageSourcePtr nativeImageSourcePtr = Dali::NativeImageSource::New(source);
656 mNativeTexture = Dali::Texture::New(*nativeImageSourcePtr);
658 if(!mTextureRenderer)
660 Dali::Geometry geometry = VisualFactoryCache::CreateQuadGeometry();
661 Dali::Shader shader = CreateShader();
662 Dali::TextureSet textureSet = Dali::TextureSet::New();
663 textureSet.SetTexture(0u, mNativeTexture);
665 mTextureRenderer = Renderer::New(geometry, shader);
666 mTextureRenderer.SetTextures(textureSet);
670 Dali::TextureSet textureSet = mTextureRenderer.GetTextures();
671 textureSet.SetTexture(0u, mNativeTexture);
673 Self().AddRenderer(mTextureRenderer);
675 // Note VideoPlayer::SetRenderingTarget resets all the options. (e.g. url, mute, looping)
676 mVideoPlayer.SetRenderingTarget(nativeImageSourcePtr);
678 ApplyBackupProperties();
687 mVideoPlayer.SetPlayPosition(curPos);
691 void VideoView::UpdateDisplayArea(Dali::PropertyNotification& source)
693 // If mSyncMode is enabled, Video player's size and poistion is updated in Video player's constraint.
694 // Because video view and player should be work syncronization.
695 if(!mIsUnderlay || mSyncMode == Dali::VideoSyncMode::ENABLED)
702 bool positionUsesAnchorPoint = self.GetProperty(Actor::Property::POSITION_USES_ANCHOR_POINT).Get<bool>();
703 Vector3 actorSize = self.GetCurrentProperty<Vector3>(Actor::Property::SIZE) * self.GetCurrentProperty<Vector3>(Actor::Property::SCALE);
704 Vector3 anchorPointOffSet = actorSize * (positionUsesAnchorPoint ? self.GetCurrentProperty<Vector3>(Actor::Property::ANCHOR_POINT) : AnchorPoint::TOP_LEFT);
706 Vector2 screenPosition = self.GetProperty(Actor::Property::SCREEN_POSITION).Get<Vector2>();
708 mDisplayArea.x = screenPosition.x - anchorPointOffSet.x;
709 mDisplayArea.y = screenPosition.y - anchorPointOffSet.y;
710 mDisplayArea.width = actorSize.x;
711 mDisplayArea.height = actorSize.y;
713 mVideoPlayer.SetDisplayArea(mDisplayArea);
716 void VideoView::SetUnderlay(bool set)
718 if(set != mIsUnderlay)
724 SetWindowSurfaceTarget();
728 SetNativeImageTarget();
735 bool VideoView::IsUnderlay()
740 void VideoView::SetSWCodec(bool on)
742 // If setting SW or HW type is failed , video-view shows video by default codec type.
743 // The default codec type is selected by platform.
746 mVideoPlayer.SetCodecType(Dali::VideoPlayerPlugin::CodecType::SW);
750 mVideoPlayer.SetCodecType(Dali::VideoPlayerPlugin::CodecType::HW);
754 int VideoView::GetPlayPosition()
756 return mVideoPlayer.GetPlayPosition();
759 void VideoView::SetPlayPosition(int pos)
761 mVideoPlayer.SetPlayPosition(pos);
764 void VideoView::SetDisplayMode(int mode)
766 mVideoPlayer.SetDisplayMode(static_cast<Dali::VideoPlayerPlugin::DisplayMode::Type>(mode));
769 int VideoView::GetDisplayMode() const
771 return static_cast<int>(mVideoPlayer.GetDisplayMode());
774 Any VideoView::GetMediaPlayer()
776 return mVideoPlayer.GetMediaPlayer();
779 void VideoView::OnAnimationFinished(Animation& animation)
782 SetFrameRenderCallback();
785 void VideoView::PlayAnimation(Dali::Animation animation)
787 if(mIsUnderlay && mSyncMode == Dali::VideoSyncMode::ENABLED)
789 mVideoPlayer.StartSynchronization();
790 animation.FinishedSignal().Connect(this, &VideoView::OnAnimationFinished);
795 Dali::Shader VideoView::CreateShader()
797 std::string fragmentShader = "#extension GL_OES_EGL_image_external:require\n";
798 std::string vertexShader;
799 std::string customFragmentShader;
800 bool checkShader = false;
802 if(!mEffectPropertyMap.Empty())
804 Property::Value* vertexShaderValue = mEffectPropertyMap.Find(CUSTOM_VERTEX_SHADER);
805 if(vertexShaderValue)
807 checkShader = GetStringFromProperty(*vertexShaderValue, vertexShader);
810 if(!vertexShaderValue || !checkShader)
812 vertexShader = SHADER_VIDEO_VIEW_TEXTURE_VERT.data();
815 Property::Value* fragmentShaderValue = mEffectPropertyMap.Find(CUSTOM_FRAGMENT_SHADER);
816 if(fragmentShaderValue)
818 checkShader = GetStringFromProperty(*fragmentShaderValue, customFragmentShader);
822 fragmentShader = customFragmentShader;
826 if(!fragmentShaderValue || !checkShader)
828 fragmentShader += SHADER_VIDEO_VIEW_TEXTURE_FRAG.data();
833 vertexShader = SHADER_VIDEO_VIEW_TEXTURE_VERT.data();
834 fragmentShader += SHADER_VIDEO_VIEW_TEXTURE_FRAG.data();
837 return Dali::Shader::New(vertexShader, fragmentShader);
840 bool VideoView::GetStringFromProperty(const Dali::Property::Value& value, std::string& output)
842 bool extracted = false;
843 if(value.Get(output))
851 void VideoView::ApplyBackupProperties()
853 Property::Map::SizeType pos = 0;
854 Property::Map::SizeType count = mPropertyBackup.Count();
856 for(; pos < count; pos++)
858 KeyValuePair property = mPropertyBackup.GetKeyValue(pos);
860 SetPropertyInternal(property.first.indexKey, property.second);
864 void VideoView::FrameRenderCallback(int frameID)
867 if(frameID == mFrameID)
869 mVideoPlayer.FinishSynchronization();
874 void VideoView::SetFrameRenderCallback()
877 DevelWindow::AddFrameRenderedCallback(DevelWindow::Get(Self()),
878 std::unique_ptr<CallbackBase>(MakeCallback(this, &VideoView::FrameRenderCallback)),
882 } // namespace Internal
884 } // namespace Toolkit