Changed constraint to PropertyNotification for updating position/size
[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 } // anonymous namepsace
110
111 VideoView::VideoView()
112 : Control( ControlBehaviour( ACTOR_BEHAVIOUR_DEFAULT | DISABLE_STYLE_CHANGE_SIGNALS ) ),
113   mCurrentVideoPlayPosition( 0 ),
114   mIsPlay( false ),
115   mIsUnderlay( true )
116 {
117   mVideoPlayer = Dali::VideoPlayer::New();
118 }
119
120 VideoView::~VideoView()
121 {
122 }
123
124 Toolkit::VideoView VideoView::New()
125 {
126   VideoView* impl = new VideoView();
127   Toolkit::VideoView handle = Toolkit::VideoView( *impl );
128
129   impl->Initialize();
130
131   return handle;
132 }
133
134 void VideoView::OnInitialize()
135 {
136   mVideoPlayer.FinishedSignal().Connect( this, &VideoView::EmitSignalFinish );
137   SetWindowSurfaceTarget();
138 }
139
140 void VideoView::SetUrl( const std::string& url )
141 {
142   if( mUrl != url || !mPropertyMap.Empty() )
143   {
144     mUrl = url;
145     mPropertyMap.Clear();
146   }
147
148   if( !mIsUnderlay )
149   {
150     Actor self( Self() );
151     Internal::InitializeVisual( self, mVisual, mNativeImage );
152   }
153
154   mVideoPlayer.SetUrl( mUrl );
155 }
156
157 void VideoView::SetPropertyMap( Property::Map map )
158 {
159   mPropertyMap = map;
160
161   Actor self( Self() );
162   Internal::InitializeVisual( self, mVisual, mPropertyMap );
163
164   Property::Value* widthValue = mPropertyMap.Find( "width" );
165   if( widthValue )
166   {
167     int width;
168     if( widthValue->Get( width ) )
169     {
170       mVideoSize = ImageDimensions( width, mVideoSize.GetHeight() );
171     }
172   }
173
174   Property::Value* heightValue = mPropertyMap.Find( "height" );
175   if( heightValue )
176   {
177     int height;
178     if( heightValue->Get( height ) )
179     {
180       mVideoSize = ImageDimensions( mVideoSize.GetWidth(), height );
181     }
182   }
183
184   Property::Value* target = map.Find( RENDERING_TARGET );
185   std::string targetType;
186
187   if( target && target->Get( targetType ) && targetType == WINDOW_SURFACE_TARGET )
188   {
189     mIsUnderlay = true;
190     SetWindowSurfaceTarget();
191   }
192   else if( target && target->Get( targetType ) && targetType == NATIVE_IMAGE_TARGET )
193   {
194     mIsUnderlay = false;
195     SetNativeImageTarget();
196   }
197
198   RelayoutRequest();
199 }
200
201 std::string VideoView::GetUrl()
202 {
203   return mUrl;
204 }
205
206 void VideoView::SetLooping(bool looping)
207 {
208   mVideoPlayer.SetLooping( looping );
209 }
210
211 bool VideoView::IsLooping()
212 {
213   return mVideoPlayer.IsLooping();
214 }
215
216 void VideoView::Play()
217 {
218   if( mIsUnderlay )
219   {
220     Self().AddRenderer( mRenderer );
221   }
222
223   mVideoPlayer.Play();
224   mIsPlay = true;
225 }
226
227 void VideoView::Pause()
228 {
229   mVideoPlayer.Pause();
230   mIsPlay = false;
231 }
232
233 void VideoView::Stop()
234 {
235   mVideoPlayer.Stop();
236   mIsPlay = false;
237 }
238
239 void VideoView::Forward( int millisecond )
240 {
241   int curPos = mVideoPlayer.GetPlayPosition();
242
243   int nextPos = curPos + millisecond;
244
245   mVideoPlayer.SetPlayPosition( nextPos );
246 }
247
248 void VideoView::Backward( int millisecond )
249 {
250   int curPos = mVideoPlayer.GetPlayPosition();
251
252   int nextPos = curPos - millisecond;
253   nextPos = ( nextPos < 0 )? 0: nextPos;
254
255   mVideoPlayer.SetPlayPosition( nextPos );
256 }
257
258 void VideoView::SetMute( bool mute )
259 {
260   mVideoPlayer.SetMute( mute );
261 }
262
263 bool VideoView::IsMuted()
264 {
265   return mVideoPlayer.IsMuted();
266 }
267
268 void VideoView::SetVolume( float left, float right )
269 {
270   mVideoPlayer.SetVolume( left, right );
271 }
272
273 void VideoView::GetVolume( float& left, float& right )
274 {
275   mVideoPlayer.GetVolume( left, right );
276 }
277
278 Dali::Toolkit::VideoView::VideoViewSignalType& VideoView::FinishedSignal()
279 {
280   return mFinishedSignal;
281 }
282
283 void VideoView::EmitSignalFinish()
284 {
285   if( mIsUnderlay )
286   {
287     Self().RemoveRenderer( mRenderer );
288   }
289
290   if ( !mFinishedSignal.Empty() )
291   {
292     Dali::Toolkit::VideoView handle( GetOwner() );
293     mFinishedSignal.Emit( handle );
294   }
295 }
296
297 bool VideoView::DoAction( BaseObject* object, const std::string& actionName, const Property::Map& attributes )
298 {
299   bool ret = false;
300
301   Dali::BaseHandle handle( object );
302   Toolkit::VideoView videoView = Toolkit::VideoView::DownCast( handle );
303
304   if( !videoView )
305   {
306     return ret;
307   }
308
309   VideoView& impl = GetImpl( videoView );
310
311   if( strcmp( actionName.c_str(), ACTION_VIDEOVIEW_PLAY ) == 0 )
312   {
313     impl.Play();
314     ret = true;
315   }
316   else if( strcmp( actionName.c_str(), ACTION_VIDEOVIEW_PAUSE ) == 0 )
317   {
318     impl.Pause();
319     ret = true;
320   }
321   else if( strcmp( actionName.c_str(), ACTION_VIDEOVIEW_STOP ) == 0 )
322   {
323     impl.Stop();
324     ret = true;
325   }
326   else if( strcmp( actionName.c_str(), ACTION_VIDEOVIEW_FORWARD ) == 0 )
327   {
328     int millisecond = 0;
329     if( attributes["videoForward"].Get( millisecond ) )
330     {
331       impl.Forward( millisecond );
332       ret = true;
333     }
334   }
335   else if( strcmp( actionName.c_str(), ACTION_VIDEOVIEW_BACKWARD ) == 0 )
336   {
337     int millisecond = 0;
338     if( attributes["videoBackward"].Get( millisecond ) )
339     {
340       impl.Backward( millisecond );
341       ret = true;
342     }
343   }
344
345   return ret;
346 }
347
348 bool VideoView::DoConnectSignal( BaseObject* object, ConnectionTrackerInterface* tracker, const std::string& signalName, FunctorDelegate* functor )
349 {
350   Dali::BaseHandle handle( object );
351
352   bool connected( true );
353   Toolkit::VideoView videoView = Toolkit::VideoView::DownCast( handle );
354
355   if( 0 == strcmp( signalName.c_str(), FINISHED_SIGNAL ) )
356   {
357     videoView.FinishedSignal().Connect( tracker, functor );
358   }
359   else
360   {
361     // signalName does not match any signal
362     connected = false;
363   }
364
365   return connected;
366 }
367
368 void VideoView::SetProperty( BaseObject* object, Property::Index index, const Property::Value& value )
369 {
370   Toolkit::VideoView videoView = Toolkit::VideoView::DownCast( Dali::BaseHandle( object ) );
371
372   if( videoView )
373   {
374     VideoView& impl = GetImpl( videoView );
375
376     switch( index )
377     {
378       case Toolkit::VideoView::Property::VIDEO:
379       {
380         std::string videoUrl;
381         Property::Map map;
382
383         if( value.Get( videoUrl ) )
384         {
385           impl.SetUrl( videoUrl );
386         }
387         else if( value.Get( map ) )
388         {
389           Property::Value* shaderValue = map.Find( Toolkit::Visual::Property::SHADER, CUSTOM_SHADER );
390
391           if( map.Count() > 1u || !shaderValue )
392           {
393             impl.SetPropertyMap( map );
394           }
395           else if( impl.mVisual && map.Count() == 1u && shaderValue )
396           {
397             Property::Map shaderMap;
398             if( shaderValue->Get( shaderMap ) )
399             {
400               Internal::Visual::Base& visual = Toolkit::GetImplementation( impl.mVisual );
401               visual.SetCustomShader( shaderMap );
402               if( videoView.OnStage() )
403               {
404                 visual.SetOffStage( videoView );
405                 visual.SetOnStage( videoView );
406               }
407             }
408           }
409         }
410         break;
411       }
412       case Toolkit::VideoView::Property::LOOPING:
413       {
414         bool looping;
415         if( value.Get( looping ) )
416         {
417           impl.SetLooping( looping );
418         }
419         break;
420       }
421       case Toolkit::VideoView::Property::MUTED:
422       {
423         bool mute;
424         if( value.Get( mute ) )
425         {
426           impl.SetMute( mute );
427         }
428         break;
429       }
430       case Toolkit::VideoView::Property::VOLUME:
431       {
432         Property::Map map;
433         float left, right;
434         if( value.Get( map ) )
435         {
436           Property::Value* volumeLeft = map.Find( VOLUME_LEFT );
437           Property::Value* volumeRight = map.Find( VOLUME_RIGHT );
438           if( volumeLeft && volumeLeft->Get( left ) && volumeRight && volumeRight->Get( right ) )
439           {
440             impl.SetVolume( left, right );
441           }
442         }
443         break;
444       }
445       case Toolkit::VideoView::Property::UNDERLAY:
446       {
447         bool underlay;
448         if( value.Get( underlay ) )
449         {
450           impl.SetUnderlay( underlay );
451         }
452         break;
453       }
454     }
455   }
456 }
457
458 Property::Value VideoView::GetProperty( BaseObject* object, Property::Index propertyIndex )
459 {
460   Property::Value value;
461   Toolkit::VideoView videoView = Toolkit::VideoView::DownCast( Dali::BaseHandle( object ) );
462
463   if( videoView )
464   {
465     VideoView& impl = GetImpl( videoView );
466
467     switch( propertyIndex )
468     {
469       case Toolkit::VideoView::Property::VIDEO:
470       {
471         if( !impl.mUrl.empty() )
472         {
473           value = impl.mUrl;
474         }
475         else if( !impl.mPropertyMap.Empty() )
476         {
477           value = impl.mPropertyMap;
478         }
479         break;
480       }
481       case Toolkit::VideoView::Property::LOOPING:
482       {
483         value = impl.IsLooping();
484         break;
485       }
486       case Toolkit::VideoView::Property::MUTED:
487       {
488         value = impl.IsMuted();
489         break;
490       }
491       case Toolkit::VideoView::Property::VOLUME:
492       {
493         Property::Map map;
494         float left, right;
495
496         impl.GetVolume( left, right );
497         map.Insert( VOLUME_LEFT, left );
498         map.Insert( VOLUME_RIGHT, right );
499         value = map;
500         break;
501       }
502       case Toolkit::VideoView::Property::UNDERLAY:
503       {
504         value = impl.IsUnderlay();
505         break;
506       }
507     }
508   }
509
510   return value;
511 }
512
513 void VideoView::SetDepthIndex( int depthIndex )
514 {
515   if( mVisual )
516   {
517     mVisual.SetDepthIndex( depthIndex );
518   }
519 }
520
521 void VideoView::OnStageConnection( int depth )
522 {
523   if( mVisual )
524   {
525     CustomActor self = Self();
526     Toolkit::GetImplementation(mVisual).SetOnStage( self );
527   }
528
529   Control::OnStageConnection( depth );
530 }
531
532 void VideoView::OnStageDisconnection()
533 {
534   if( mVisual )
535   {
536     CustomActor self = Self();
537     Toolkit::GetImplementation(mVisual).SetOffStage( self );
538   }
539
540   Control::OnStageDisconnection();
541 }
542
543 Vector3 VideoView::GetNaturalSize()
544 {
545   Vector3 size;
546   size.x = mVideoSize.GetWidth();
547   size.y = mVideoSize.GetHeight();
548
549   if( size.x > 0 && size.y > 0 )
550   {
551     size.z = std::min( size.x, size.y );
552     return size;
553   }
554   else
555   {
556     return Control::GetNaturalSize();
557   }
558 }
559
560 float VideoView::GetHeightForWidth( float width )
561 {
562   if( mVideoSize.GetWidth() > 0 && mVideoSize.GetHeight() > 0 )
563   {
564     return GetHeightForWidthBase( width );
565   }
566   else
567   {
568     return Control::GetHeightForWidthBase( width );
569   }
570 }
571
572 float VideoView::GetWidthForHeight( float height )
573 {
574   if( mVideoSize.GetWidth() > 0 && mVideoSize.GetHeight() > 0 )
575   {
576     return GetWidthForHeightBase( height );
577   }
578   else
579   {
580     return Control::GetWidthForHeightBase( height );
581   }
582 }
583
584 void VideoView::SetWindowSurfaceTarget()
585 {
586   Actor self = Self();
587   int curPos = mVideoPlayer.GetPlayPosition();
588
589   if( mVisual )
590   {
591     Toolkit::GetImplementation(mVisual).SetOffStage(self);
592     mVisual.Reset();
593   }
594
595   if( mIsPlay )
596   {
597     mVideoPlayer.Pause();
598   }
599
600   mPositionUpdateNotification = self.AddPropertyNotification( Actor::Property::WORLD_POSITION, StepCondition( 1.0f, 1.0f ) );
601   mSizeUpdateNotification = self.AddPropertyNotification( Actor::Property::SIZE, StepCondition( 1.0f, 1.0f ) );
602   mScaleUpdateNotification = self.AddPropertyNotification( Actor::Property::WORLD_SCALE, StepCondition( 0.1f, 1.0f ) );
603   mPositionUpdateNotification.NotifySignal().Connect( this, &VideoView::UpdateDisplayArea );
604   mSizeUpdateNotification.NotifySignal().Connect( this, &VideoView::UpdateDisplayArea );
605   mScaleUpdateNotification.NotifySignal().Connect( this, &VideoView::UpdateDisplayArea );
606
607   mVideoPlayer.SetRenderingTarget( Dali::Adaptor::Get().GetNativeWindowHandle() );
608   mVideoPlayer.SetUrl( mUrl );
609
610   if( !mRenderer )
611   {
612     // For underlay rendering mode, video display area have to be transparent.
613     Geometry geometry = VisualFactoryCache::CreateQuadGeometry();
614     Shader shader = Shader::New( VERTEX_SHADER, FRAGMENT_SHADER );
615     mRenderer = Renderer::New( geometry, shader );
616
617     mRenderer.SetProperty( Renderer::Property::BLEND_MODE, BlendMode::ON );
618     mRenderer.SetProperty( Renderer::Property::BLEND_FACTOR_SRC_RGB, BlendFactor::ONE );
619     mRenderer.SetProperty( Renderer::Property::BLEND_FACTOR_DEST_RGB, BlendFactor::ZERO );
620     mRenderer.SetProperty( Renderer::Property::BLEND_FACTOR_SRC_ALPHA, BlendFactor::ONE );
621     mRenderer.SetProperty( Renderer::Property::BLEND_FACTOR_DEST_ALPHA, BlendFactor::ZERO );
622   }
623
624   if( mIsPlay )
625   {
626     Play();
627   }
628
629   if( curPos > 0 )
630   {
631     mVideoPlayer.SetPlayPosition( curPos );
632   }
633 }
634
635 void VideoView::SetNativeImageTarget()
636 {
637   if( mVideoPlayer.IsVideoTextureSupported() == false )
638   {
639     DALI_LOG_ERROR( "Platform doesn't support decoded video frame images\n" );
640     mIsUnderlay = true;
641     return;
642   }
643
644   if( mIsPlay )
645   {
646     mVideoPlayer.Pause();
647   }
648
649   Actor self( Self() );
650   self.RemoveRenderer( mRenderer );
651   Dali::Stage::GetCurrent().KeepRendering( 0.0f );
652
653   self.RemovePropertyNotification( mPositionUpdateNotification );
654   self.RemovePropertyNotification( mSizeUpdateNotification );
655   self.RemovePropertyNotification( mScaleUpdateNotification );
656
657   int curPos = mVideoPlayer.GetPlayPosition();
658
659   Any source;
660   Dali::NativeImageSourcePtr nativeImageSourcePtr = Dali::NativeImageSource::New( source );
661   mNativeImage = Dali::NativeImage::New( *nativeImageSourcePtr );
662
663   mVideoPlayer.SetRenderingTarget( nativeImageSourcePtr );
664   mVideoPlayer.SetUrl( mUrl );
665
666   Internal::InitializeVisual( self, mVisual, mNativeImage );
667   Self().RemoveRenderer( mRenderer );
668
669   if( mIsPlay )
670   {
671     Play();
672   }
673
674   if( curPos > 0 )
675   {
676     mVideoPlayer.SetPlayPosition( curPos );
677   }
678 }
679
680 void VideoView::UpdateDisplayArea( Dali::PropertyNotification& source )
681 {
682   if( !mIsUnderlay )
683   {
684     return;
685   }
686
687   Actor self( Self() );
688
689   bool positionUsesAnchorPoint = self.GetProperty( DevelActor::Property::POSITION_USES_ANCHOR_POINT ).Get< bool >();
690   Vector3 actorSize = self.GetCurrentSize() * self.GetCurrentScale();
691   Vector3 anchorPointOffSet = actorSize * ( positionUsesAnchorPoint ? self.GetCurrentAnchorPoint() : AnchorPoint::TOP_LEFT );
692
693   Vector2 screenPosition = self.GetProperty( DevelActor::Property::SCREEN_POSITION ).Get< Vector2 >();
694
695   mDisplayArea.x = screenPosition.x - anchorPointOffSet.x;
696   mDisplayArea.y = screenPosition.y - anchorPointOffSet.y;
697   mDisplayArea.width = actorSize.x;
698   mDisplayArea.height = actorSize.y;
699
700   mVideoPlayer.SetDisplayArea( mDisplayArea );
701 }
702
703 void VideoView::SetUnderlay( bool set )
704 {
705   if( set != mIsUnderlay )
706   {
707     mIsUnderlay = set;
708
709     if( mIsUnderlay )
710     {
711       SetWindowSurfaceTarget();
712     }
713     else
714     {
715       SetNativeImageTarget();
716     }
717
718     RelayoutRequest();
719   }
720 }
721
722 bool VideoView::IsUnderlay()
723 {
724   return mIsUnderlay;
725 }
726
727 } // namespace Internal
728
729 } // namespace toolkit
730
731 } // namespace Dali