1 #include "mediacontrol-core.h"
15 #include <stdlib.h> /* malloc(), free() */
18 #include <errno.h> /* ENOMEM */
26 #ifdef HAVE_SYS_TIME_H
27 # include <sys/time.h>
29 #include <sys/types.h>
31 #define RAISE( c, m ) exception->code = c; \
32 exception->message = strdup(m);
34 long long mediacontrol_unit_convert( input_thread_t *p_input,
35 mediacontrol_PositionKey from,
36 mediacontrol_PositionKey to,
42 /* For all conversions, we need data from p_input */
48 case mediacontrol_MediaTime:
49 if( to == mediacontrol_ByteCount )
50 return value * 50 * p_input->stream.i_mux_rate / 1000;
52 if( to == mediacontrol_SampleCount )
56 if( demux_Control( p_input, DEMUX_GET_FPS, &f_fps ) || f_fps < 0.1 )
59 return( value * f_fps / 1000.0 );
62 /* See http://catb.org/~esr/jargon/html/entry/can't-happen.html */
65 case mediacontrol_SampleCount:
69 if( demux_Control( p_input, DEMUX_GET_FPS, &f_fps ) || f_fps < 0.1 )
72 if( to == mediacontrol_ByteCount )
73 return ( long long )( value * 50 * p_input->stream.i_mux_rate / f_fps );
75 if( to == mediacontrol_MediaTime )
76 return( long long )( value * 1000.0 / ( double )f_fps );
81 case mediacontrol_ByteCount:
82 if( p_input->stream.i_mux_rate == 0 )
85 /* Convert an offset into milliseconds. Taken from input_ext-intf.c.
86 The 50 hardcoded constant comes from the definition of i_mux_rate :
87 i_mux_rate : the rate we read the stream (in units of 50 bytes/s) ;
89 if( to == mediacontrol_MediaTime )
90 return ( long long )( 1000 * value / 50 / p_input->stream.i_mux_rate );
92 if( to == mediacontrol_SampleCount )
95 if( demux_Control( p_input, DEMUX_GET_FPS, &f_fps ) || f_fps < 0.1 )
98 return ( long long )( value * f_fps / 50 / p_input->stream.i_mux_rate );
107 /* Converts a mediacontrol_Position into a time in microseconds in
110 mediacontrol_position2microsecond( input_thread_t* p_input, const mediacontrol_Position * pos )
112 switch( pos->origin )
114 case mediacontrol_AbsolutePosition:
115 return ( 1000 * mediacontrol_unit_convert( p_input,
117 mediacontrol_MediaTime, /* to */
120 case mediacontrol_RelativePosition:
128 var_Get( p_input, "time", &val );
131 l_pos = 1000 * mediacontrol_unit_convert( p_input,
133 mediacontrol_MediaTime,
135 return val.i_time + l_pos;
138 case mediacontrol_ModuloPosition:
146 var_Get( p_input, "length", &val );
151 l_pos = ( 1000 * mediacontrol_unit_convert( p_input,
153 mediacontrol_MediaTime,
159 return l_pos % val.i_time;
166 mediacontrol_RGBPicture*
167 mediacontrol_RGBPicture__alloc( int datasize )
169 mediacontrol_RGBPicture* pic;
171 pic = ( mediacontrol_RGBPicture * )malloc( sizeof( mediacontrol_RGBPicture ) );
175 pic->size = datasize;
176 pic->data = ( char* )malloc( datasize );
181 mediacontrol_RGBPicture__free( mediacontrol_RGBPicture* pic )
188 mediacontrol_PlaylistSeq*
189 mediacontrol_PlaylistSeq__alloc( int size )
191 mediacontrol_PlaylistSeq* ps;
193 ps =( mediacontrol_PlaylistSeq* )malloc( sizeof( mediacontrol_PlaylistSeq ) );
198 ps->data = ( char** )malloc( size * sizeof( char* ) );
203 mediacontrol_PlaylistSeq__free( mediacontrol_PlaylistSeq* ps )
208 for( i = 0 ; i < ps->size ; i++ )
215 mediacontrol_Exception*
216 mediacontrol_exception_init( mediacontrol_Exception *exception )
218 if( exception == NULL )
220 exception = ( mediacontrol_Exception* )malloc( sizeof( mediacontrol_Exception ) );
224 exception->message = NULL;
229 mediacontrol_exception_free( mediacontrol_Exception *exception )
234 free( exception->message );
238 mediacontrol_Instance* mediacontrol_new_from_object( vlc_object_t* p_object,
239 mediacontrol_Exception *exception )
241 mediacontrol_Instance* retval;
244 p_vlc = vlc_object_find( p_object, VLC_OBJECT_ROOT, FIND_PARENT );
247 RAISE( mediacontrol_InternalException, "Unable to initialize VLC" );
250 retval = ( mediacontrol_Instance* )malloc( sizeof( mediacontrol_Instance ) );
251 retval->p_vlc = p_vlc;
252 retval->vlc_object_id = p_vlc->i_object_id;
254 /* We can keep references on these, which should not change. Is it true ? */
255 retval->p_playlist = vlc_object_find( p_vlc, VLC_OBJECT_PLAYLIST, FIND_ANYWHERE );
256 retval->p_intf = vlc_object_find( p_vlc, VLC_OBJECT_INTF, FIND_ANYWHERE );
258 if( ! retval->p_playlist || ! retval->p_intf )
260 RAISE( mediacontrol_InternalException, "No available interface" );
266 /* Returns the current position in the stream. The returned value can
267 be relative or absolute( according to PositionOrigin ) and the unit
268 is set by PositionKey */
269 mediacontrol_Position*
270 mediacontrol_get_media_position( mediacontrol_Instance *self,
271 const mediacontrol_PositionOrigin an_origin,
272 const mediacontrol_PositionKey a_key,
273 mediacontrol_Exception *exception )
275 mediacontrol_Position* retval;
277 input_thread_t * p_input = self->p_playlist->p_input;
279 exception = mediacontrol_exception_init( exception );
281 retval = ( mediacontrol_Position* )malloc( sizeof( mediacontrol_Position ) );
282 retval->origin = an_origin;
288 RAISE( mediacontrol_InternalException, "No input thread." );
295 if( an_origin == mediacontrol_RelativePosition
296 || an_origin == mediacontrol_ModuloPosition )
298 /* Relative or ModuloPosition make no sense */
303 /* We are asked for an AbsolutePosition. */
305 var_Get( p_input, "time", &val );
306 /* FIXME: check val.i_time > 0 */
308 retval->value = mediacontrol_unit_convert( p_input,
309 mediacontrol_MediaTime,
315 /* Sets the media position */
317 mediacontrol_set_media_position( mediacontrol_Instance *self,
318 const mediacontrol_Position * a_position,
319 mediacontrol_Exception *exception )
322 input_thread_t * p_input = self->p_playlist->p_input;
324 exception=mediacontrol_exception_init( exception );
327 RAISE( mediacontrol_InternalException, "No input thread." );
331 if( !p_input->stream.b_seekable )
333 RAISE( mediacontrol_InvalidPosition, "Stream not seekable" );
337 val.i_time = mediacontrol_position2microsecond( p_input, a_position );
338 var_Set( p_input, "time", val );
342 /* Starts playing a stream */
344 mediacontrol_start( mediacontrol_Instance *self,
345 const mediacontrol_Position * a_position,
346 mediacontrol_Exception *exception )
348 playlist_t * p_playlist = self->p_playlist;
350 exception = mediacontrol_exception_init( exception );
353 RAISE( mediacontrol_PlaylistException, "No available playlist" );
357 vlc_mutex_lock( &p_playlist->object_lock );
358 if( p_playlist->i_size )
362 vlc_mutex_unlock( &p_playlist->object_lock );
365 val.i_int = mediacontrol_position2microsecond( p_playlist->p_input, a_position ) / 1000000;
366 var_Set( p_playlist, "start-time", val );
368 playlist_Play( p_playlist );
372 RAISE( mediacontrol_PlaylistException, "Empty playlist." );
373 vlc_mutex_unlock( &p_playlist->object_lock );
381 mediacontrol_pause( mediacontrol_Instance *self,
382 const mediacontrol_Position * a_position,
383 mediacontrol_Exception *exception )
385 input_thread_t *p_input = self->p_playlist->p_input;;
387 /* FIXME: use the a_position parameter */
388 exception=mediacontrol_exception_init( exception );
389 if( p_input != NULL )
391 var_SetInteger( p_input, "state", PAUSE_S );
395 RAISE( mediacontrol_InternalException, "No input" );
402 mediacontrol_resume( mediacontrol_Instance *self,
403 const mediacontrol_Position * a_position,
404 mediacontrol_Exception *exception )
406 input_thread_t *p_input = self->p_playlist->p_input;
408 /* FIXME: use the a_position parameter */
409 exception=mediacontrol_exception_init( exception );
410 if( p_input != NULL )
412 var_SetInteger( p_input, "state", PAUSE_S );
416 RAISE( mediacontrol_InternalException, "No input" );
421 mediacontrol_stop( mediacontrol_Instance *self,
422 const mediacontrol_Position * a_position,
423 mediacontrol_Exception *exception )
425 /* FIXME: use the a_position parameter */
426 exception=mediacontrol_exception_init( exception );
427 if( !self->p_playlist )
429 RAISE( mediacontrol_PlaylistException, "No playlist" );
433 playlist_Stop( self->p_playlist );
437 mediacontrol_playlist_add_item( mediacontrol_Instance *self,
438 const char * psz_file,
439 mediacontrol_Exception *exception )
441 exception=mediacontrol_exception_init( exception );
442 if( !self->p_playlist )
444 RAISE( mediacontrol_InternalException, "No playlist" );
448 playlist_Add( self->p_playlist, psz_file, psz_file , PLAYLIST_REPLACE, 0 );
452 mediacontrol_playlist_clear( mediacontrol_Instance *self,
453 mediacontrol_Exception *exception )
455 exception=mediacontrol_exception_init( exception );
456 if( !self->p_playlist )
458 RAISE( mediacontrol_PlaylistException, "No playlist" );
462 playlist_Clear( self->p_playlist );
467 mediacontrol_PlaylistSeq *
468 mediacontrol_playlist_get_list( mediacontrol_Instance *self,
469 mediacontrol_Exception *exception )
471 mediacontrol_PlaylistSeq *retval;
473 playlist_t * p_playlist = self->p_playlist;;
476 exception=mediacontrol_exception_init( exception );
479 RAISE( mediacontrol_PlaylistException, "No playlist" );
483 vlc_mutex_lock( &p_playlist->object_lock );
484 i_playlist_size = p_playlist->i_size;
486 retval = mediacontrol_PlaylistSeq__alloc( i_playlist_size );
488 for( i_index = 0 ; i_index < i_playlist_size ; i_index++ )
490 retval->data[i_index] = strdup( p_playlist->pp_items[i_index]->input.psz_uri );
492 vlc_mutex_unlock( &p_playlist->object_lock );
497 mediacontrol_RGBPicture*
498 _mediacontrol_createRGBPicture( int i_width, int i_height, long i_chroma, long long l_date,
499 char* p_data, int i_datasize )
501 mediacontrol_RGBPicture *retval;
503 retval = mediacontrol_RGBPicture__alloc( i_datasize );
506 retval->width = i_width;
507 retval->height = i_height;
508 retval->type = i_chroma;
509 retval->date = l_date;
510 retval->size = i_datasize;
511 memcpy( retval->data, p_data, i_datasize );
516 mediacontrol_RGBPicture *
517 mediacontrol_snapshot( mediacontrol_Instance *self,
518 const mediacontrol_Position * a_position,
519 mediacontrol_Exception *exception )
521 mediacontrol_RGBPicture *retval = NULL;
522 input_thread_t* p_input = self->p_playlist->p_input;
523 vout_thread_t *p_vout = NULL;
525 snapshot_t **pointer;
528 snapshot_t *p_best_snapshot;
534 exception=mediacontrol_exception_init( exception );
537 if( var_Get( self->p_vlc, "snapshot-id", &val ) == VLC_SUCCESS )
538 p_vout = vlc_object_get( self->p_vlc, val.i_int );
541 /* FIXME: if in p_libvlc, we cannot have multiple video outputs */
542 /* Once corrected, search for snapshot-id to modify all instances */
543 if( var_Get( p_input, "snapshot-id", &val ) != VLC_SUCCESS )
545 RAISE( mediacontrol_InternalException, "No snapshot-id in p_input" );
548 p_vout = vlc_object_get( self->p_vlc, val.i_int );
552 RAISE( mediacontrol_InternalException, "No snapshot module" );
557 /* We test if the vout is a snapshot module. We cannot test
558 pvout_psz_object_name( which is NULL ). But we can check if
559 there are snapshot-specific variables */
560 if( var_Get( p_vout, "snapshot-datasize", &val ) != VLC_SUCCESS )
562 RAISE( mediacontrol_InternalException, "No snapshot module" );
563 vlc_object_release( p_vout );
566 i_datasize = val.i_int;
568 /* Handle the a_position parameter */
569 if( ! ( a_position->origin == mediacontrol_RelativePosition
570 && a_position->value == 0 ) )
572 /* The position is not the current one. Go to it. */
573 mediacontrol_set_media_position( self,
574 ( mediacontrol_Position* ) a_position,
576 if( exception->code )
578 vlc_object_release( p_vout );
583 /* FIXME: We should not go further until we got past the position
584 ( which means that we had the possibility to capture the right
587 vlc_mutex_lock( &p_vout->picture_lock );
589 searched_date = mediacontrol_position2microsecond( p_input,
590 ( mediacontrol_Position * ) a_position );
592 var_Get( p_vout, "snapshot-cache-size", &val );
593 i_cachesize = val.i_int ;
595 var_Get( p_vout, "snapshot-list-pointer", &val );
596 pointer = ( snapshot_t ** )val.p_address;
600 RAISE( mediacontrol_InternalException, "No available snapshot" );
602 vlc_mutex_unlock( &p_vout->picture_lock );
603 vlc_object_release( p_vout );
607 /* Find the more appropriate picture, based on date */
608 p_best_snapshot = pointer[0];
610 for( i_index = 1 ; i_index < i_cachesize ; i_index++ )
612 long l_diff = pointer[i_index]->date - searched_date;
613 if( l_diff > 0 && l_diff < abs( p_best_snapshot->date - searched_date ))
615 /* This one is closer, and _after_ the requested position */
616 p_best_snapshot = pointer[i_index];
620 /* FIXME: add a test for the case that no picture matched the test
621 ( we have p_best_snapshot == pointer[0] */
622 retval = _mediacontrol_createRGBPicture( p_best_snapshot->i_width,
623 p_best_snapshot->i_height,
624 p_vout->output.i_chroma,
625 p_best_snapshot->date,
626 p_best_snapshot->p_data,
629 vlc_mutex_unlock( &p_vout->picture_lock );
630 vlc_object_release( p_vout );
637 mediacontrol_RGBPicture **
638 mediacontrol_all_snapshots( mediacontrol_Instance *self,
639 mediacontrol_Exception *exception )
641 mediacontrol_RGBPicture **retval = NULL;
642 vout_thread_t *p_vout = NULL;
648 snapshot_t **pointer;
651 exception=mediacontrol_exception_init( exception );
653 if( var_Get( self->p_playlist->p_input, "snapshot-id", &val ) == VLC_SUCCESS )
654 p_vout = vlc_object_get( self->p_vlc, val.i_int );
658 RAISE( mediacontrol_InternalException, "No snapshot module" );
662 /* We test if the vout is a snapshot module. We cannot test
663 pvout_psz_object_name( which is NULL ). But we can check if
664 there are snapshot-specific variables */
665 if( var_Get( p_vout, "snapshot-datasize", &val ) != VLC_SUCCESS )
667 RAISE( mediacontrol_InternalException, "No snapshot module" );
668 vlc_object_release( p_vout );
671 i_datasize = val.i_int;
673 vlc_mutex_lock( &p_vout->picture_lock );
675 var_Get( p_vout, "snapshot-cache-size", &val );
676 i_cachesize = val.i_int ;
678 var_Get( p_vout, "snapshot-list-pointer", &val );
679 pointer = ( snapshot_t ** )val.p_address;
683 RAISE( mediacontrol_InternalException, "No available picture" );
685 vlc_mutex_unlock( &p_vout->picture_lock );
686 vlc_object_release( p_vout );
690 retval = ( mediacontrol_RGBPicture** )malloc( (i_cachesize + 1 ) * sizeof( char* ));
692 for( i_index = 0 ; i_index < i_cachesize ; i_index++ )
694 snapshot_t *p_s = pointer[i_index];
695 mediacontrol_RGBPicture *p_rgb;
697 p_rgb = _mediacontrol_createRGBPicture( p_s->i_width,
699 p_vout->output.i_chroma,
704 retval[i_index] = p_rgb;
707 retval[i_cachesize] = NULL;
709 vlc_mutex_unlock( &p_vout->picture_lock );
710 vlc_object_release( p_vout );
718 mediacontrol_display_text( mediacontrol_Instance *self,
719 const char * message,
720 const mediacontrol_Position * begin,
721 const mediacontrol_Position * end,
722 mediacontrol_Exception *exception )
724 input_thread_t *p_input = NULL;
725 vout_thread_t *p_vout = NULL;
727 p_vout = vlc_object_find( self->p_playlist, VLC_OBJECT_VOUT, FIND_CHILD );
730 RAISE( mediacontrol_InternalException, "No video output" );
734 if( begin->origin == mediacontrol_RelativePosition &&
736 end->origin == mediacontrol_RelativePosition )
738 mtime_t i_duration = 0;
740 i_duration = 1000 * mediacontrol_unit_convert( self->p_playlist->p_input,
742 mediacontrol_MediaTime,
745 vout_ShowTextRelative( p_vout, SUBT1_CHAN, ( char* ) message, NULL,
746 OSD_ALIGN_BOTTOM | OSD_ALIGN_LEFT, 20, 20,
751 mtime_t i_debut, i_fin, i_now;
753 p_input = self->p_playlist->p_input;
756 RAISE( mediacontrol_InternalException, "No input" );
757 vlc_object_release( p_vout );
761 i_now = input_ClockGetTS( p_input, NULL, 0 );
763 i_debut = mediacontrol_position2microsecond( p_input,
764 ( mediacontrol_Position* ) begin );
767 i_fin = mediacontrol_position2microsecond( p_input,
768 ( mediacontrol_Position * ) end );
771 vout_ShowTextAbsolute( p_vout, SUBT1_CHAN, ( char* )message, NULL,
772 OSD_ALIGN_BOTTOM | OSD_ALIGN_LEFT, 20, 20,
776 vlc_object_release( p_vout );
779 mediacontrol_StreamInformation *
780 mediacontrol_get_stream_information( mediacontrol_Instance *self,
781 mediacontrol_PositionKey a_key,
782 mediacontrol_Exception *exception )
784 mediacontrol_StreamInformation *retval;
785 input_thread_t *p_input = self->p_playlist->p_input;
788 retval = ( mediacontrol_StreamInformation* )malloc( sizeof( mediacontrol_StreamInformation ) );
791 RAISE( mediacontrol_InternalException, "Out of memory" );
797 /* No p_input defined */
798 retval->streamstatus = mediacontrol_UndefinedStatus;
799 retval->url = strdup( "None" );
800 retval->position = 0;
805 switch( p_input->stream.control.i_status )
808 retval->streamstatus = mediacontrol_UndefinedStatus;
811 retval->streamstatus = mediacontrol_PlayingStatus;
814 retval->streamstatus = mediacontrol_PauseStatus;
817 retval->streamstatus = mediacontrol_ForwardStatus;
820 retval->streamstatus = mediacontrol_BackwardStatus;
823 retval->streamstatus = mediacontrol_InitStatus;
826 retval->streamstatus = mediacontrol_EndStatus;
829 retval->streamstatus = mediacontrol_UndefinedStatus;
833 retval->url = strdup( p_input->psz_source );
835 /* TIME and LENGTH are in microseconds. We want them in ms */
836 var_Get( p_input, "time", &val);
837 retval->position = val.i_time / 1000;
839 var_Get( p_input, "length", &val);
840 retval->length = val.i_time / 1000;
842 retval->position = mediacontrol_unit_convert( p_input,
843 mediacontrol_MediaTime, a_key,
845 retval->length = mediacontrol_unit_convert( p_input,
846 mediacontrol_MediaTime, a_key,
853 mediacontrol_sound_get_volume( mediacontrol_Instance *self,
854 mediacontrol_Exception *exception )
857 audio_volume_t i_volume;
861 RAISE( mediacontrol_InternalException, "No interface module" );
864 aout_VolumeGet( self->p_intf, &i_volume );
870 mediacontrol_sound_set_volume( mediacontrol_Instance *self,
871 const unsigned short volume,
872 mediacontrol_Exception *exception )
876 RAISE( mediacontrol_InternalException, "No interface module" );
879 aout_VolumeSet( self->p_intf,( audio_volume_t )volume );