2fdb70b206d2d2a93c9662de1d106a1fb737f8ef
[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
66 DALI_SIGNAL_REGISTRATION( Toolkit, VideoView, "finished", FINISHED_SIGNAL )
67
68 DALI_ACTION_REGISTRATION( Toolkit, VideoView, "play", ACTION_VIDEOVIEW_PLAY )
69 DALI_ACTION_REGISTRATION( Toolkit, VideoView, "pause", ACTION_VIDEOVIEW_PAUSE )
70 DALI_ACTION_REGISTRATION( Toolkit, VideoView, "stop", ACTION_VIDEOVIEW_STOP )
71 DALI_ACTION_REGISTRATION( Toolkit, VideoView, "forward", ACTION_VIDEOVIEW_FORWARD )
72 DALI_ACTION_REGISTRATION( Toolkit, VideoView, "backward", ACTION_VIDEOVIEW_BACKWARD )
73
74 DALI_TYPE_REGISTRATION_END()
75
76 const char* const VOLUME_LEFT( "volumeLeft" );
77 const char* const VOLUME_RIGHT( "volumeRight" );
78 const char* const RENDERING_TARGET( "renderingTarget" );
79 const char* const WINDOW_SURFACE_TARGET( "windowSurfaceTarget" );
80 const char* const NATIVE_IMAGE_TARGET( "nativeImageTarget" );
81
82 const char* VERTEX_SHADER = DALI_COMPOSE_SHADER(
83   attribute mediump vec2 aPosition;\n
84   uniform mediump mat4 uMvpMatrix;\n
85   uniform mediump vec3 uSize;\n
86   \n
87   void main()\n
88   {\n
89     mediump vec4 vertexPosition = vec4(aPosition, 0.0, 1.0);\n
90     vertexPosition.xyz *= uSize;\n
91     gl_Position = uMvpMatrix * vertexPosition;\n
92   }\n
93 );
94
95 const char* FRAGMENT_SHADER = DALI_COMPOSE_SHADER(
96   uniform lowp vec4 uColor;\n
97   uniform lowp vec3 mixColor;\n
98   uniform lowp float opacity;\n
99   \n
100   void main()\n
101   {\n
102     gl_FragColor = vec4(mixColor, opacity)*uColor;\n
103   }\n
104 );
105
106 struct TriggerFunctor
107 {
108   TriggerFunctor( TriggerEventInterface* notification )
109   : mNotification( notification )
110   {
111   }
112
113   void operator()( bool& current, const PropertyInputContainer& inputs )
114   {
115     if( mNotification != NULL )
116     {
117       mNotification->Trigger();
118     }
119   }
120
121   TriggerEventInterface* mNotification;
122 };
123
124 } // anonymous namepsace
125
126 VideoView::VideoView()
127 : Control( ControlBehaviour( ACTOR_BEHAVIOUR_DEFAULT | DISABLE_STYLE_CHANGE_SIGNALS ) ),
128   mUpdateTriggerPropertyIndex( Property::INVALID_INDEX),
129   mNotification( NULL ),
130   mCurrentVideoPlayPosition( 0 ),
131   mIsNativeImageTarget( true ),
132   mIsPlay( false ),
133   mIsPause( false )
134 {
135   mVideoPlayer = Dali::VideoPlayer::New();
136
137   TriggerEventFactory triggerEventFactory;
138   mNotification = triggerEventFactory.CreateTriggerEvent( MakeCallback(this, &VideoView::UpdateDisplayArea ),
139                                                                TriggerEventInterface::KEEP_ALIVE_AFTER_TRIGGER);
140 }
141
142 VideoView::~VideoView()
143 {
144   if( mNotification != NULL )
145   {
146     delete mNotification;
147   }
148 }
149
150 Toolkit::VideoView VideoView::New()
151 {
152   VideoView* impl = new VideoView();
153   Toolkit::VideoView handle = Toolkit::VideoView( *impl );
154
155   impl->Initialize();
156
157   return handle;
158 }
159
160 void VideoView::OnInitialize()
161 {
162   Any source;
163   Dali::NativeImageSourcePtr nativeImageSourcePtr = Dali::NativeImageSource::New( source );
164   mNativeImage = Dali::NativeImage::New( *nativeImageSourcePtr );
165
166   mVideoPlayer.SetRenderingTarget( nativeImageSourcePtr );
167   mVideoPlayer.FinishedSignal().Connect( this, &VideoView::EmitSignalFinish );
168
169   mUpdateTriggerPropertyIndex = Self().RegisterProperty( "updateTrigger", true );
170 }
171
172 void VideoView::SetUrl( const std::string& url )
173 {
174   if( mUrl != url || !mPropertyMap.Empty() )
175   {
176     mUrl = url;
177     mPropertyMap.Clear();
178   }
179
180   if( mIsNativeImageTarget )
181   {
182     Actor self( Self() );
183     Internal::InitializeVisual( self, mVisual, mNativeImage );
184   }
185
186   mVideoPlayer.SetUrl( mUrl );
187 }
188
189 void VideoView::SetPropertyMap( Property::Map map )
190 {
191   mPropertyMap = map;
192
193   Actor self( Self() );
194   Internal::InitializeVisual( self, mVisual, mPropertyMap );
195
196   Property::Value* widthValue = mPropertyMap.Find( "width" );
197   if( widthValue )
198   {
199     int width;
200     if( widthValue->Get( width ) )
201     {
202       mVideoSize = ImageDimensions( width, mVideoSize.GetHeight() );
203     }
204   }
205
206   Property::Value* heightValue = mPropertyMap.Find( "height" );
207   if( heightValue )
208   {
209     int height;
210     if( heightValue->Get( height ) )
211     {
212       mVideoSize = ImageDimensions( mVideoSize.GetWidth(), height );
213     }
214   }
215
216   Property::Value* target = map.Find( RENDERING_TARGET );
217   std::string targetType;
218
219   if( target && target->Get( targetType ) && targetType == WINDOW_SURFACE_TARGET )
220   {
221     this->SetWindowSurfaceTarget();
222   }
223   else if( target && target->Get( targetType ) && targetType == NATIVE_IMAGE_TARGET )
224   {
225     this->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     }
469   }
470 }
471
472 Property::Value VideoView::GetProperty( BaseObject* object, Property::Index propertyIndex )
473 {
474   Property::Value value;
475   Toolkit::VideoView videoView = Toolkit::VideoView::DownCast( Dali::BaseHandle( object ) );
476
477   if( videoView )
478   {
479     VideoView& impl = GetImpl( videoView );
480
481     switch( propertyIndex )
482     {
483       case Toolkit::VideoView::Property::VIDEO:
484       {
485         if( !impl.mUrl.empty() )
486         {
487           value = impl.mUrl;
488         }
489         else if( !impl.mPropertyMap.Empty() )
490         {
491           value = impl.mPropertyMap;
492         }
493         break;
494       }
495       case Toolkit::VideoView::Property::LOOPING:
496       {
497         value = impl.IsLooping();
498         break;
499       }
500       case Toolkit::VideoView::Property::MUTED:
501       {
502         value = impl.IsMuted();
503         break;
504       }
505       case Toolkit::VideoView::Property::VOLUME:
506       {
507         Property::Map map;
508         float left, right;
509
510         impl.GetVolume( left, right );
511         map.Insert( VOLUME_LEFT, left );
512         map.Insert( VOLUME_RIGHT, right );
513         value = map;
514         break;
515       }
516     }
517   }
518
519   return value;
520 }
521
522 void VideoView::SetDepthIndex( int depthIndex )
523 {
524   if( mVisual )
525   {
526     mVisual.SetDepthIndex( depthIndex );
527   }
528 }
529
530 void VideoView::OnStageConnection( int depth )
531 {
532   if( mVisual )
533   {
534     CustomActor self = Self();
535     Toolkit::GetImplementation(mVisual).SetOnStage( self );
536   }
537
538   Control::OnStageConnection( depth );
539 }
540
541 void VideoView::OnStageDisconnection()
542 {
543   if( mVisual )
544   {
545     CustomActor self = Self();
546     Toolkit::GetImplementation(mVisual).SetOffStage( self );
547   }
548
549   Control::OnStageDisconnection();
550 }
551
552 Vector3 VideoView::GetNaturalSize()
553 {
554   Vector3 size;
555   size.x = mVideoSize.GetWidth();
556   size.y = mVideoSize.GetHeight();
557
558   if( size.x > 0 && size.y > 0 )
559   {
560     size.z = std::min( size.x, size.y );
561     return size;
562   }
563   else
564   {
565     return Control::GetNaturalSize();
566   }
567 }
568
569 float VideoView::GetHeightForWidth( float width )
570 {
571   if( mVideoSize.GetWidth() > 0 && mVideoSize.GetHeight() > 0 )
572   {
573     return GetHeightForWidthBase( width );
574   }
575   else
576   {
577     return Control::GetHeightForWidthBase( width );
578   }
579 }
580
581 float VideoView::GetWidthForHeight( float height )
582 {
583   if( mVideoSize.GetWidth() > 0 && mVideoSize.GetHeight() > 0 )
584   {
585     return GetWidthForHeightBase( height );
586   }
587   else
588   {
589     return Control::GetWidthForHeightBase( height );
590   }
591 }
592
593 void VideoView::SetWindowSurfaceTarget()
594 {
595   Actor self = Self();
596   int curPos = mVideoPlayer.GetPlayPosition();
597
598   if( mVisual )
599   {
600     Toolkit::GetImplementation(mVisual).SetOffStage(self);
601     mVisual.Reset();
602   }
603
604   Constraint constraint = Constraint::New<bool>( self, mUpdateTriggerPropertyIndex, TriggerFunctor( mNotification ) );
605   constraint.AddSource( Source( self, Actor::Property::POSITION ) );
606   constraint.AddSource( Source( self, Actor::Property::SIZE ) );
607   constraint.Apply();
608
609   mVideoPlayer.SetRenderingTarget( Dali::Adaptor::Get().GetNativeWindowHandle() );
610   mVideoPlayer.SetUrl( mUrl );
611
612   mIsNativeImageTarget = false;
613
614   if( mIsPlay )
615   {
616     mVideoPlayer.Play();
617   }
618   if( mIsPause )
619   {
620     mVideoPlayer.Play();
621     mVideoPlayer.Pause();
622   }
623
624   if( curPos > 0 )
625   {
626     mVideoPlayer.SetPlayPosition( curPos );
627   }
628
629   // For underlay rendering mode, video display area have to be transparent.
630   Geometry geometry = VisualFactoryCache::CreateQuadGeometry();
631   Shader shader = Shader::New( VERTEX_SHADER, FRAGMENT_SHADER );
632   Renderer renderer = Renderer::New( geometry, shader );
633
634   renderer.SetProperty( Renderer::Property::BLEND_MODE, BlendMode::ON );
635   renderer.SetProperty( Renderer::Property::BLEND_FACTOR_SRC_RGB, BlendFactor::ONE );
636   renderer.SetProperty( Renderer::Property::BLEND_FACTOR_DEST_RGB, BlendFactor::ZERO );
637   renderer.SetProperty( Renderer::Property::BLEND_FACTOR_SRC_ALPHA, BlendFactor::ONE );
638   renderer.SetProperty( Renderer::Property::BLEND_FACTOR_DEST_ALPHA, BlendFactor::ZERO );
639   self.AddRenderer( renderer );
640 }
641
642 void VideoView::SetNativeImageTarget()
643 {
644   Actor self( Self() );
645   int curPos = mVideoPlayer.GetPlayPosition();
646
647   Any source;
648   Dali::NativeImageSourcePtr nativeImageSourcePtr = Dali::NativeImageSource::New( source );
649   mNativeImage = Dali::NativeImage::New( *nativeImageSourcePtr );
650
651   mVideoPlayer.SetRenderingTarget( nativeImageSourcePtr );
652   mVideoPlayer.SetUrl( mUrl );
653
654   Internal::InitializeVisual( self, mVisual, mNativeImage );
655   mIsNativeImageTarget = true;
656
657   if( mIsPlay )
658   {
659     mVideoPlayer.Play();
660   }
661
662   if( mIsPause )
663   {
664     mVideoPlayer.Play();
665     mVideoPlayer.Pause();
666   }
667   if( curPos > 0 )
668   {
669     mVideoPlayer.SetPlayPosition( curPos );
670   }
671 }
672
673 void VideoView::UpdateDisplayArea()
674 {
675   Actor self( Self() );
676
677   bool positionUsesAnchorPoint = self.GetProperty( DevelActor::Property::POSITION_USES_ANCHOR_POINT ).Get< bool >();
678   Vector3 actorSize = self.GetCurrentSize() * self.GetCurrentScale();
679   Vector3 anchorPointOffSet = actorSize * ( positionUsesAnchorPoint ? self.GetCurrentAnchorPoint() : AnchorPoint::TOP_LEFT );
680
681   Vector2 screenPosition = self.GetProperty( DevelActor::Property::SCREEN_POSITION ).Get< Vector2 >();
682
683   mDisplayArea.x = screenPosition.x - anchorPointOffSet.x;
684   mDisplayArea.y = screenPosition.y - anchorPointOffSet.y;
685   mDisplayArea.width = actorSize.x;
686   mDisplayArea.height = actorSize.y;
687
688   mVideoPlayer.SetDisplayArea( mDisplayArea );
689 }
690
691 } // namespace Internal
692
693 } // namespace toolkit
694
695 } // namespace Dali