Added underlay property
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / controls / video-view / video-view-impl.cpp
1 /*
2  * Copyright (c) 2017 Samsung Electronics Co., Ltd.
3  *
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
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  *
16  */
17
18 // CLASS HEADER
19 #include "video-view-impl.h"
20
21 // EXTERNAL INCLUDES
22 #include <cstring>
23 #include <dali/public-api/object/type-registry.h>
24 #include <dali/public-api/object/type-registry-helper.h>
25 #include <dali/public-api/common/stage.h>
26 #include <dali/devel-api/scripting/scripting.h>
27 #include <dali/public-api/adaptor-framework/native-image-source.h>
28 #include <dali/integration-api/adaptors/adaptor.h>
29 #include <dali/integration-api/debug.h>
30 #include <dali/public-api/animation/constraint.h>
31 #include <dali/devel-api/actors/actor-devel.h>
32
33 // INTERNAL INCLUDES
34 #include <dali-toolkit/public-api/controls/video-view/video-view.h>
35 #include <dali-toolkit/public-api/visuals/visual-properties.h>
36 #include <dali-toolkit/devel-api/visual-factory/visual-factory.h>
37 #include <dali-toolkit/internal/visuals/visual-string-constants.h>
38 #include <dali-toolkit/internal/visuals/visual-base-impl.h>
39 #include <dali-toolkit/internal/visuals/visual-factory-impl.h>
40 #include <dali-toolkit/internal/visuals/visual-factory-cache.h>
41
42 namespace Dali
43 {
44
45 namespace Toolkit
46 {
47
48 namespace Internal
49 {
50
51 namespace
52 {
53
54 BaseHandle Create()
55 {
56   return Toolkit::VideoView::New();
57 }
58
59 DALI_TYPE_REGISTRATION_BEGIN( Toolkit::VideoView, Toolkit::Control, Create );
60
61 DALI_PROPERTY_REGISTRATION( Toolkit, VideoView, "video", MAP, VIDEO )
62 DALI_PROPERTY_REGISTRATION( Toolkit, VideoView, "looping", BOOLEAN, LOOPING )
63 DALI_PROPERTY_REGISTRATION( Toolkit, VideoView, "muted", BOOLEAN, MUTED )
64 DALI_PROPERTY_REGISTRATION( Toolkit, VideoView, "volume", MAP, VOLUME )
65 DALI_PROPERTY_REGISTRATION( Toolkit, VideoView, "underlay", BOOLEAN, UNDERLAY )
66
67 DALI_SIGNAL_REGISTRATION( Toolkit, VideoView, "finished", FINISHED_SIGNAL )
68
69 DALI_ACTION_REGISTRATION( Toolkit, VideoView, "play", ACTION_VIDEOVIEW_PLAY )
70 DALI_ACTION_REGISTRATION( Toolkit, VideoView, "pause", ACTION_VIDEOVIEW_PAUSE )
71 DALI_ACTION_REGISTRATION( Toolkit, VideoView, "stop", ACTION_VIDEOVIEW_STOP )
72 DALI_ACTION_REGISTRATION( Toolkit, VideoView, "forward", ACTION_VIDEOVIEW_FORWARD )
73 DALI_ACTION_REGISTRATION( Toolkit, VideoView, "backward", ACTION_VIDEOVIEW_BACKWARD )
74
75 DALI_TYPE_REGISTRATION_END()
76
77 const char* const VOLUME_LEFT( "volumeLeft" );
78 const char* const VOLUME_RIGHT( "volumeRight" );
79
80 // 3.0 TC uses RENDERING_TARGET. It should be removed in next release
81 const char* const RENDERING_TARGET( "renderingTarget" );
82 const char* const WINDOW_SURFACE_TARGET( "windowSurfaceTarget" );
83 const char* const NATIVE_IMAGE_TARGET( "nativeImageTarget" );
84
85 const char* VERTEX_SHADER = DALI_COMPOSE_SHADER(
86   attribute mediump vec2 aPosition;\n
87   uniform mediump mat4 uMvpMatrix;\n
88   uniform mediump vec3 uSize;\n
89   \n
90   void main()\n
91   {\n
92     mediump vec4 vertexPosition = vec4(aPosition, 0.0, 1.0);\n
93     vertexPosition.xyz *= uSize;\n
94     gl_Position = uMvpMatrix * vertexPosition;\n
95   }\n
96 );
97
98 const char* FRAGMENT_SHADER = DALI_COMPOSE_SHADER(
99   uniform lowp vec4 uColor;\n
100   uniform lowp vec3 mixColor;\n
101   uniform lowp float opacity;\n
102   \n
103   void main()\n
104   {\n
105     gl_FragColor = vec4(mixColor, opacity)*uColor;\n
106   }\n
107 );
108
109 struct TriggerFunctor
110 {
111   TriggerFunctor( TriggerEventInterface* notification )
112   : mNotification( notification )
113   {
114   }
115
116   void operator()( bool& current, const PropertyInputContainer& inputs )
117   {
118     if( mNotification != NULL )
119     {
120       mNotification->Trigger();
121     }
122   }
123
124   TriggerEventInterface* mNotification;
125 };
126
127 } // anonymous namepsace
128
129 VideoView::VideoView()
130 : Control( ControlBehaviour( ACTOR_BEHAVIOUR_DEFAULT | DISABLE_STYLE_CHANGE_SIGNALS ) ),
131   mUpdateTriggerPropertyIndex( Property::INVALID_INDEX),
132   mNotification( NULL ),
133   mCurrentVideoPlayPosition( 0 ),
134   mIsPlay( false ),
135   mIsPause( false ),
136   mIsUnderlay( true )
137 {
138   mVideoPlayer = Dali::VideoPlayer::New();
139
140   TriggerEventFactory triggerEventFactory;
141   mNotification = triggerEventFactory.CreateTriggerEvent( MakeCallback(this, &VideoView::UpdateDisplayArea ),
142                                                                TriggerEventInterface::KEEP_ALIVE_AFTER_TRIGGER);
143 }
144
145 VideoView::~VideoView()
146 {
147   if( mNotification != NULL )
148   {
149     delete mNotification;
150   }
151 }
152
153 Toolkit::VideoView VideoView::New()
154 {
155   VideoView* impl = new VideoView();
156   Toolkit::VideoView handle = Toolkit::VideoView( *impl );
157
158   impl->Initialize();
159
160   return handle;
161 }
162
163 void VideoView::OnInitialize()
164 {
165   mUpdateTriggerPropertyIndex = Self().RegisterProperty( "updateTrigger", true );
166   mVideoPlayer.FinishedSignal().Connect( this, &VideoView::EmitSignalFinish );
167   SetWindowSurfaceTarget();
168 }
169
170 void VideoView::SetUrl( const std::string& url )
171 {
172   if( mUrl != url || !mPropertyMap.Empty() )
173   {
174     mUrl = url;
175     mPropertyMap.Clear();
176   }
177
178   if( !mIsUnderlay )
179   {
180     Actor self( Self() );
181     Internal::InitializeVisual( self, mVisual, mNativeImage );
182   }
183
184   mVideoPlayer.SetUrl( mUrl );
185 }
186
187 void VideoView::SetPropertyMap( Property::Map map )
188 {
189   mPropertyMap = map;
190
191   Actor self( Self() );
192   Internal::InitializeVisual( self, mVisual, mPropertyMap );
193
194   Property::Value* widthValue = mPropertyMap.Find( "width" );
195   if( widthValue )
196   {
197     int width;
198     if( widthValue->Get( width ) )
199     {
200       mVideoSize = ImageDimensions( width, mVideoSize.GetHeight() );
201     }
202   }
203
204   Property::Value* heightValue = mPropertyMap.Find( "height" );
205   if( heightValue )
206   {
207     int height;
208     if( heightValue->Get( height ) )
209     {
210       mVideoSize = ImageDimensions( mVideoSize.GetWidth(), height );
211     }
212   }
213
214   Property::Value* target = map.Find( RENDERING_TARGET );
215   std::string targetType;
216
217   if( target && target->Get( targetType ) && targetType == WINDOW_SURFACE_TARGET )
218   {
219     mIsUnderlay = true;
220     SetWindowSurfaceTarget();
221   }
222   else if( target && target->Get( targetType ) && targetType == NATIVE_IMAGE_TARGET )
223   {
224     mIsUnderlay = false;
225     SetNativeImageTarget();
226   }
227
228   RelayoutRequest();
229 }
230
231 std::string VideoView::GetUrl()
232 {
233   return mUrl;
234 }
235
236 void VideoView::SetLooping(bool looping)
237 {
238   mVideoPlayer.SetLooping( looping );
239 }
240
241 bool VideoView::IsLooping()
242 {
243   return mVideoPlayer.IsLooping();
244 }
245
246 void VideoView::Play()
247 {
248   mVideoPlayer.Play();
249   mIsPlay = true;
250   mIsPause = false;
251 }
252
253 void VideoView::Pause()
254 {
255   mVideoPlayer.Pause();
256   mIsPlay = false;
257   mIsPause = true;
258 }
259
260 void VideoView::Stop()
261 {
262   mVideoPlayer.Stop();
263   mIsPlay = false;
264   mIsPause = false;
265 }
266
267 void VideoView::Forward( int millisecond )
268 {
269   int curPos = mVideoPlayer.GetPlayPosition();
270
271   int nextPos = curPos + millisecond;
272
273   mVideoPlayer.SetPlayPosition( nextPos );
274 }
275
276 void VideoView::Backward( int millisecond )
277 {
278   int curPos = mVideoPlayer.GetPlayPosition();
279
280   int nextPos = curPos - millisecond;
281   nextPos = ( nextPos < 0 )? 0: nextPos;
282
283   mVideoPlayer.SetPlayPosition( nextPos );
284 }
285
286 void VideoView::SetMute( bool mute )
287 {
288   mVideoPlayer.SetMute( mute );
289 }
290
291 bool VideoView::IsMuted()
292 {
293   return mVideoPlayer.IsMuted();
294 }
295
296 void VideoView::SetVolume( float left, float right )
297 {
298   mVideoPlayer.SetVolume( left, right );
299 }
300
301 void VideoView::GetVolume( float& left, float& right )
302 {
303   mVideoPlayer.GetVolume( left, right );
304 }
305
306 Dali::Toolkit::VideoView::VideoViewSignalType& VideoView::FinishedSignal()
307 {
308   return mFinishedSignal;
309 }
310
311 void VideoView::EmitSignalFinish()
312 {
313   if ( !mFinishedSignal.Empty() )
314   {
315     Dali::Toolkit::VideoView handle( GetOwner() );
316     mFinishedSignal.Emit( handle );
317   }
318 }
319
320 bool VideoView::DoAction( BaseObject* object, const std::string& actionName, const Property::Map& attributes )
321 {
322   bool ret = false;
323
324   Dali::BaseHandle handle( object );
325   Toolkit::VideoView videoView = Toolkit::VideoView::DownCast( handle );
326
327   if( !videoView )
328   {
329     return ret;
330   }
331
332   VideoView& impl = GetImpl( videoView );
333
334   if( strcmp( actionName.c_str(), ACTION_VIDEOVIEW_PLAY ) == 0 )
335   {
336     impl.Play();
337     ret = true;
338   }
339   else if( strcmp( actionName.c_str(), ACTION_VIDEOVIEW_PAUSE ) == 0 )
340   {
341     impl.Pause();
342     ret = true;
343   }
344   else if( strcmp( actionName.c_str(), ACTION_VIDEOVIEW_STOP ) == 0 )
345   {
346     impl.Stop();
347     ret = true;
348   }
349   else if( strcmp( actionName.c_str(), ACTION_VIDEOVIEW_FORWARD ) == 0 )
350   {
351     int millisecond = 0;
352     if( attributes["videoForward"].Get( millisecond ) )
353     {
354       impl.Forward( millisecond );
355       ret = true;
356     }
357   }
358   else if( strcmp( actionName.c_str(), ACTION_VIDEOVIEW_BACKWARD ) == 0 )
359   {
360     int millisecond = 0;
361     if( attributes["videoBackward"].Get( millisecond ) )
362     {
363       impl.Backward( millisecond );
364       ret = true;
365     }
366   }
367
368   return ret;
369 }
370
371 bool VideoView::DoConnectSignal( BaseObject* object, ConnectionTrackerInterface* tracker, const std::string& signalName, FunctorDelegate* functor )
372 {
373   Dali::BaseHandle handle( object );
374
375   bool connected( true );
376   Toolkit::VideoView videoView = Toolkit::VideoView::DownCast( handle );
377
378   if( 0 == strcmp( signalName.c_str(), FINISHED_SIGNAL ) )
379   {
380     videoView.FinishedSignal().Connect( tracker, functor );
381   }
382   else
383   {
384     // signalName does not match any signal
385     connected = false;
386   }
387
388   return connected;
389 }
390
391 void VideoView::SetProperty( BaseObject* object, Property::Index index, const Property::Value& value )
392 {
393   Toolkit::VideoView videoView = Toolkit::VideoView::DownCast( Dali::BaseHandle( object ) );
394
395   if( videoView )
396   {
397     VideoView& impl = GetImpl( videoView );
398
399     switch( index )
400     {
401       case Toolkit::VideoView::Property::VIDEO:
402       {
403         std::string videoUrl;
404         Property::Map map;
405
406         if( value.Get( videoUrl ) )
407         {
408           impl.SetUrl( videoUrl );
409         }
410         else if( value.Get( map ) )
411         {
412           Property::Value* shaderValue = map.Find( Toolkit::Visual::Property::SHADER, CUSTOM_SHADER );
413
414           if( map.Count() > 1u || !shaderValue )
415           {
416             impl.SetPropertyMap( map );
417           }
418           else if( impl.mVisual && map.Count() == 1u && shaderValue )
419           {
420             Property::Map shaderMap;
421             if( shaderValue->Get( shaderMap ) )
422             {
423               Internal::Visual::Base& visual = Toolkit::GetImplementation( impl.mVisual );
424               visual.SetCustomShader( shaderMap );
425               if( videoView.OnStage() )
426               {
427                 visual.SetOffStage( videoView );
428                 visual.SetOnStage( videoView );
429               }
430             }
431           }
432         }
433         break;
434       }
435       case Toolkit::VideoView::Property::LOOPING:
436       {
437         bool looping;
438         if( value.Get( looping ) )
439         {
440           impl.SetLooping( looping );
441         }
442         break;
443       }
444       case Toolkit::VideoView::Property::MUTED:
445       {
446         bool mute;
447         if( value.Get( mute ) )
448         {
449           impl.SetMute( mute );
450         }
451         break;
452       }
453       case Toolkit::VideoView::Property::VOLUME:
454       {
455         Property::Map map;
456         float left, right;
457         if( value.Get( map ) )
458         {
459           Property::Value* volumeLeft = map.Find( VOLUME_LEFT );
460           Property::Value* volumeRight = map.Find( VOLUME_RIGHT );
461           if( volumeLeft && volumeLeft->Get( left ) && volumeRight && volumeRight->Get( right ) )
462           {
463             impl.SetVolume( left, right );
464           }
465         }
466         break;
467       }
468       case Toolkit::VideoView::Property::UNDERLAY:
469       {
470         bool underlay;
471         if( value.Get( underlay ) )
472         {
473           impl.SetUnderlay( underlay );
474         }
475         break;
476       }
477     }
478   }
479 }
480
481 Property::Value VideoView::GetProperty( BaseObject* object, Property::Index propertyIndex )
482 {
483   Property::Value value;
484   Toolkit::VideoView videoView = Toolkit::VideoView::DownCast( Dali::BaseHandle( object ) );
485
486   if( videoView )
487   {
488     VideoView& impl = GetImpl( videoView );
489
490     switch( propertyIndex )
491     {
492       case Toolkit::VideoView::Property::VIDEO:
493       {
494         if( !impl.mUrl.empty() )
495         {
496           value = impl.mUrl;
497         }
498         else if( !impl.mPropertyMap.Empty() )
499         {
500           value = impl.mPropertyMap;
501         }
502         break;
503       }
504       case Toolkit::VideoView::Property::LOOPING:
505       {
506         value = impl.IsLooping();
507         break;
508       }
509       case Toolkit::VideoView::Property::MUTED:
510       {
511         value = impl.IsMuted();
512         break;
513       }
514       case Toolkit::VideoView::Property::VOLUME:
515       {
516         Property::Map map;
517         float left, right;
518
519         impl.GetVolume( left, right );
520         map.Insert( VOLUME_LEFT, left );
521         map.Insert( VOLUME_RIGHT, right );
522         value = map;
523         break;
524       }
525       case Toolkit::VideoView::Property::UNDERLAY:
526       {
527         value = impl.IsUnderlay();
528         break;
529       }
530     }
531   }
532
533   return value;
534 }
535
536 void VideoView::SetDepthIndex( int depthIndex )
537 {
538   if( mVisual )
539   {
540     mVisual.SetDepthIndex( depthIndex );
541   }
542 }
543
544 void VideoView::OnStageConnection( int depth )
545 {
546   if( mVisual )
547   {
548     CustomActor self = Self();
549     Toolkit::GetImplementation(mVisual).SetOnStage( self );
550   }
551
552   Control::OnStageConnection( depth );
553 }
554
555 void VideoView::OnStageDisconnection()
556 {
557   if( mVisual )
558   {
559     CustomActor self = Self();
560     Toolkit::GetImplementation(mVisual).SetOffStage( self );
561   }
562
563   Control::OnStageDisconnection();
564 }
565
566 Vector3 VideoView::GetNaturalSize()
567 {
568   Vector3 size;
569   size.x = mVideoSize.GetWidth();
570   size.y = mVideoSize.GetHeight();
571
572   if( size.x > 0 && size.y > 0 )
573   {
574     size.z = std::min( size.x, size.y );
575     return size;
576   }
577   else
578   {
579     return Control::GetNaturalSize();
580   }
581 }
582
583 float VideoView::GetHeightForWidth( float width )
584 {
585   if( mVideoSize.GetWidth() > 0 && mVideoSize.GetHeight() > 0 )
586   {
587     return GetHeightForWidthBase( width );
588   }
589   else
590   {
591     return Control::GetHeightForWidthBase( width );
592   }
593 }
594
595 float VideoView::GetWidthForHeight( float height )
596 {
597   if( mVideoSize.GetWidth() > 0 && mVideoSize.GetHeight() > 0 )
598   {
599     return GetWidthForHeightBase( height );
600   }
601   else
602   {
603     return Control::GetWidthForHeightBase( height );
604   }
605 }
606
607 void VideoView::SetWindowSurfaceTarget()
608 {
609   Actor self = Self();
610   int curPos = mVideoPlayer.GetPlayPosition();
611
612   if( mVisual )
613   {
614     Toolkit::GetImplementation(mVisual).SetOffStage(self);
615     mVisual.Reset();
616   }
617
618   Constraint constraint = Constraint::New<bool>( self, mUpdateTriggerPropertyIndex, TriggerFunctor( mNotification ) );
619   constraint.AddSource( Source( self, Actor::Property::POSITION ) );
620   constraint.AddSource( Source( self, Actor::Property::SIZE ) );
621   constraint.Apply();
622
623   mVideoPlayer.SetRenderingTarget( Dali::Adaptor::Get().GetNativeWindowHandle() );
624   mVideoPlayer.SetUrl( mUrl );
625
626   if( !mRenderer )
627   {
628     // For underlay rendering mode, video display area have to be transparent.
629     Geometry geometry = VisualFactoryCache::CreateQuadGeometry();
630     Shader shader = Shader::New( VERTEX_SHADER, FRAGMENT_SHADER );
631     mRenderer = Renderer::New( geometry, shader );
632
633     mRenderer.SetProperty( Renderer::Property::BLEND_MODE, BlendMode::ON );
634     mRenderer.SetProperty( Renderer::Property::BLEND_FACTOR_SRC_RGB, BlendFactor::ONE );
635     mRenderer.SetProperty( Renderer::Property::BLEND_FACTOR_DEST_RGB, BlendFactor::ZERO );
636     mRenderer.SetProperty( Renderer::Property::BLEND_FACTOR_SRC_ALPHA, BlendFactor::ONE );
637     mRenderer.SetProperty( Renderer::Property::BLEND_FACTOR_DEST_ALPHA, BlendFactor::ZERO );
638   }
639   self.AddRenderer( mRenderer );
640
641   UpdateDisplayArea();
642
643   if( mIsPlay )
644   {
645     mVideoPlayer.Play();
646   }
647   if( mIsPause )
648   {
649     mVideoPlayer.Play();
650     mVideoPlayer.Pause();
651   }
652
653   if( curPos > 0 )
654   {
655     mVideoPlayer.SetPlayPosition( curPos );
656   }
657 }
658
659 void VideoView::SetNativeImageTarget()
660 {
661   if( mVideoPlayer.IsVideoTextureSupported() == false )
662   {
663     DALI_LOG_ERROR( "Platform doesn't support decoded video frame images\n" );
664     mIsUnderlay = true;
665     return;
666   }
667
668   Actor self( Self() );
669   int curPos = mVideoPlayer.GetPlayPosition();
670
671   Any source;
672   Dali::NativeImageSourcePtr nativeImageSourcePtr = Dali::NativeImageSource::New( source );
673   mNativeImage = Dali::NativeImage::New( *nativeImageSourcePtr );
674
675   mVideoPlayer.SetRenderingTarget( nativeImageSourcePtr );
676   mVideoPlayer.SetUrl( mUrl );
677
678   Internal::InitializeVisual( self, mVisual, mNativeImage );
679   Self().RemoveRenderer( mRenderer );
680
681   if( mIsPlay )
682   {
683     mVideoPlayer.Play();
684   }
685
686   if( mIsPause )
687   {
688     mVideoPlayer.Play();
689     mVideoPlayer.Pause();
690   }
691   if( curPos > 0 )
692   {
693     mVideoPlayer.SetPlayPosition( curPos );
694   }
695 }
696
697 void VideoView::UpdateDisplayArea()
698 {
699   if( !mIsUnderlay )
700   {
701     return;
702   }
703
704   Actor self( Self() );
705
706   bool positionUsesAnchorPoint = self.GetProperty( DevelActor::Property::POSITION_USES_ANCHOR_POINT ).Get< bool >();
707   Vector3 actorSize = self.GetCurrentSize() * self.GetCurrentScale();
708   Vector3 anchorPointOffSet = actorSize * ( positionUsesAnchorPoint ? self.GetCurrentAnchorPoint() : AnchorPoint::TOP_LEFT );
709
710   Vector2 screenPosition = self.GetProperty( DevelActor::Property::SCREEN_POSITION ).Get< Vector2 >();
711
712   mDisplayArea.x = screenPosition.x - anchorPointOffSet.x;
713   mDisplayArea.y = screenPosition.y - anchorPointOffSet.y;
714   mDisplayArea.width = actorSize.x;
715   mDisplayArea.height = actorSize.y;
716
717   mVideoPlayer.SetDisplayArea( mDisplayArea );
718 }
719
720 void VideoView::SetUnderlay( bool set )
721 {
722   if( set != mIsUnderlay )
723   {
724     mIsUnderlay = set;
725
726     if( mIsUnderlay )
727     {
728       SetWindowSurfaceTarget();
729     }
730     else
731     {
732       SetNativeImageTarget();
733     }
734   }
735 }
736
737 bool VideoView::IsUnderlay()
738 {
739   return mIsUnderlay;
740 }
741
742 } // namespace Internal
743
744 } // namespace toolkit
745
746 } // namespace Dali