1 #include "mediacontrol-core.h"
10 #define HAS_SNAPSHOT 1
16 #include <stdlib.h> /* malloc(), free() */
19 #include <errno.h> /* ENOMEM */
27 #ifdef HAVE_SYS_TIME_H
28 # include <sys/time.h>
30 #include <sys/types.h>
32 #define RAISE( c, m ) exception->code = c; \
33 exception->message = strdup(m);
35 long long mediacontrol_unit_convert( input_thread_t *p_input,
36 mediacontrol_PositionKey from,
37 mediacontrol_PositionKey to,
43 /* For all conversions, we need data from p_input */
49 case mediacontrol_MediaTime:
50 if( to == mediacontrol_ByteCount )
54 /* return value * 50 * p_input->stream.i_mux_rate / 1000; */
57 if( to == mediacontrol_SampleCount )
61 if( demux2_Control( p_input->input.p_demux, DEMUX_GET_FPS, &f_fps ) || f_fps < 0.1 )
64 return( value * f_fps / 1000.0 );
67 /* See http://catb.org/~esr/jargon/html/entry/can't-happen.html */
70 case mediacontrol_SampleCount:
74 if( demux2_Control( p_input->input.p_demux, DEMUX_GET_FPS, &f_fps ) || f_fps < 0.1 )
77 if( to == mediacontrol_ByteCount )
81 /* return ( long long )( value * 50 * p_input->stream.i_mux_rate / f_fps ); */
85 if( to == mediacontrol_MediaTime )
86 return( long long )( value * 1000.0 / ( double )f_fps );
91 case mediacontrol_ByteCount:
96 // if( p_input->stream.i_mux_rate == 0 )
99 // /* Convert an offset into milliseconds. Taken from input_ext-intf.c.
100 // The 50 hardcoded constant comes from the definition of i_mux_rate :
101 // i_mux_rate : the rate we read the stream (in units of 50 bytes/s) ;
103 // if( to == mediacontrol_MediaTime )
104 // return ( long long )( 1000 * value / 50 / p_input->stream.i_mux_rate );
106 // if( to == mediacontrol_SampleCount )
109 // if( demux2_Control( p_input->input.p_demux, DEMUX_GET_FPS, &f_fps ) || f_fps < 0.1 )
112 // return ( long long )( value * f_fps / 50 / p_input->stream.i_mux_rate );
121 /* Converts a mediacontrol_Position into a time in microseconds in
124 mediacontrol_position2microsecond( input_thread_t* p_input, const mediacontrol_Position * pos )
126 switch( pos->origin )
128 case mediacontrol_AbsolutePosition:
129 return ( 1000 * mediacontrol_unit_convert( p_input,
131 mediacontrol_MediaTime, /* to */
134 case mediacontrol_RelativePosition:
142 var_Get( p_input, "time", &val );
145 l_pos = 1000 * mediacontrol_unit_convert( p_input,
147 mediacontrol_MediaTime,
149 return val.i_time + l_pos;
152 case mediacontrol_ModuloPosition:
160 var_Get( p_input, "length", &val );
165 l_pos = ( 1000 * mediacontrol_unit_convert( p_input,
167 mediacontrol_MediaTime,
173 return l_pos % val.i_time;
180 mediacontrol_RGBPicture*
181 mediacontrol_RGBPicture__alloc( int datasize )
183 mediacontrol_RGBPicture* pic;
185 pic = ( mediacontrol_RGBPicture * )malloc( sizeof( mediacontrol_RGBPicture ) );
189 pic->size = datasize;
190 pic->data = ( char* )malloc( datasize );
195 mediacontrol_RGBPicture__free( mediacontrol_RGBPicture* pic )
202 mediacontrol_PlaylistSeq*
203 mediacontrol_PlaylistSeq__alloc( int size )
205 mediacontrol_PlaylistSeq* ps;
207 ps =( mediacontrol_PlaylistSeq* )malloc( sizeof( mediacontrol_PlaylistSeq ) );
212 ps->data = ( char** )malloc( size * sizeof( char* ) );
217 mediacontrol_PlaylistSeq__free( mediacontrol_PlaylistSeq* ps )
222 for( i = 0 ; i < ps->size ; i++ )
229 mediacontrol_Exception*
230 mediacontrol_exception_init( mediacontrol_Exception *exception )
232 if( exception == NULL )
234 exception = ( mediacontrol_Exception* )malloc( sizeof( mediacontrol_Exception ) );
238 exception->message = NULL;
243 mediacontrol_exception_free( mediacontrol_Exception *exception )
248 free( exception->message );
252 mediacontrol_Instance* mediacontrol_new_from_object( vlc_object_t* p_object,
253 mediacontrol_Exception *exception )
255 mediacontrol_Instance* retval;
258 p_vlc = vlc_object_find( p_object, VLC_OBJECT_ROOT, FIND_PARENT );
261 RAISE( mediacontrol_InternalException, "Unable to initialize VLC" );
264 retval = ( mediacontrol_Instance* )malloc( sizeof( mediacontrol_Instance ) );
265 retval->p_vlc = p_vlc;
266 retval->vlc_object_id = p_vlc->i_object_id;
268 /* We can keep references on these, which should not change. Is it true ? */
269 retval->p_playlist = vlc_object_find( p_vlc, VLC_OBJECT_PLAYLIST, FIND_ANYWHERE );
270 retval->p_intf = vlc_object_find( p_vlc, VLC_OBJECT_INTF, FIND_ANYWHERE );
272 if( ! retval->p_playlist || ! retval->p_intf )
274 RAISE( mediacontrol_InternalException, "No available interface" );
280 /* Returns the current position in the stream. The returned value can
281 be relative or absolute( according to PositionOrigin ) and the unit
282 is set by PositionKey */
283 mediacontrol_Position*
284 mediacontrol_get_media_position( mediacontrol_Instance *self,
285 const mediacontrol_PositionOrigin an_origin,
286 const mediacontrol_PositionKey a_key,
287 mediacontrol_Exception *exception )
289 mediacontrol_Position* retval;
291 input_thread_t * p_input = self->p_playlist->p_input;
293 exception = mediacontrol_exception_init( exception );
295 retval = ( mediacontrol_Position* )malloc( sizeof( mediacontrol_Position ) );
296 retval->origin = an_origin;
302 RAISE( mediacontrol_InternalException, "No input thread." );
309 if( an_origin == mediacontrol_RelativePosition
310 || an_origin == mediacontrol_ModuloPosition )
312 /* Relative or ModuloPosition make no sense */
317 /* We are asked for an AbsolutePosition. */
319 var_Get( p_input, "time", &val );
320 /* FIXME: check val.i_time > 0 */
322 retval->value = mediacontrol_unit_convert( p_input,
323 mediacontrol_MediaTime,
329 /* Sets the media position */
331 mediacontrol_set_media_position( mediacontrol_Instance *self,
332 const mediacontrol_Position * a_position,
333 mediacontrol_Exception *exception )
336 input_thread_t * p_input = self->p_playlist->p_input;
338 exception=mediacontrol_exception_init( exception );
341 RAISE( mediacontrol_InternalException, "No input thread." );
345 if( !var_GetBool( p_input, "seekable" ) )
347 RAISE( mediacontrol_InvalidPosition, "Stream not seekable" );
351 val.i_time = mediacontrol_position2microsecond( p_input, a_position );
352 var_Set( p_input, "time", val );
356 /* Starts playing a stream */
358 mediacontrol_start( mediacontrol_Instance *self,
359 const mediacontrol_Position * a_position,
360 mediacontrol_Exception *exception )
362 playlist_t * p_playlist = self->p_playlist;
364 exception = mediacontrol_exception_init( exception );
367 RAISE( mediacontrol_PlaylistException, "No available playlist" );
371 vlc_mutex_lock( &p_playlist->object_lock );
372 if( p_playlist->i_size )
376 vlc_mutex_unlock( &p_playlist->object_lock );
379 val.i_int = mediacontrol_position2microsecond( p_playlist->p_input, a_position ) / 1000000;
380 var_Set( p_playlist, "start-time", val );
382 playlist_Play( p_playlist );
386 RAISE( mediacontrol_PlaylistException, "Empty playlist." );
387 vlc_mutex_unlock( &p_playlist->object_lock );
395 mediacontrol_pause( mediacontrol_Instance *self,
396 const mediacontrol_Position * a_position,
397 mediacontrol_Exception *exception )
399 input_thread_t *p_input = self->p_playlist->p_input;;
401 /* FIXME: use the a_position parameter */
402 exception=mediacontrol_exception_init( exception );
403 if( p_input != NULL )
405 var_SetInteger( p_input, "state", PAUSE_S );
409 RAISE( mediacontrol_InternalException, "No input" );
416 mediacontrol_resume( mediacontrol_Instance *self,
417 const mediacontrol_Position * a_position,
418 mediacontrol_Exception *exception )
420 input_thread_t *p_input = self->p_playlist->p_input;
422 /* FIXME: use the a_position parameter */
423 exception=mediacontrol_exception_init( exception );
424 if( p_input != NULL )
426 var_SetInteger( p_input, "state", PAUSE_S );
430 RAISE( mediacontrol_InternalException, "No input" );
435 mediacontrol_stop( mediacontrol_Instance *self,
436 const mediacontrol_Position * a_position,
437 mediacontrol_Exception *exception )
439 /* FIXME: use the a_position parameter */
440 exception=mediacontrol_exception_init( exception );
441 if( !self->p_playlist )
443 RAISE( mediacontrol_PlaylistException, "No playlist" );
447 playlist_Stop( self->p_playlist );
451 mediacontrol_playlist_add_item( mediacontrol_Instance *self,
452 const char * psz_file,
453 mediacontrol_Exception *exception )
455 exception=mediacontrol_exception_init( exception );
456 if( !self->p_playlist )
458 RAISE( mediacontrol_InternalException, "No playlist" );
462 playlist_Add( self->p_playlist, psz_file, psz_file , PLAYLIST_REPLACE, 0 );
466 mediacontrol_playlist_clear( mediacontrol_Instance *self,
467 mediacontrol_Exception *exception )
469 exception=mediacontrol_exception_init( exception );
470 if( !self->p_playlist )
472 RAISE( mediacontrol_PlaylistException, "No playlist" );
476 playlist_Clear( self->p_playlist );
481 mediacontrol_PlaylistSeq *
482 mediacontrol_playlist_get_list( mediacontrol_Instance *self,
483 mediacontrol_Exception *exception )
485 mediacontrol_PlaylistSeq *retval;
487 playlist_t * p_playlist = self->p_playlist;;
490 exception=mediacontrol_exception_init( exception );
493 RAISE( mediacontrol_PlaylistException, "No playlist" );
497 vlc_mutex_lock( &p_playlist->object_lock );
498 i_playlist_size = p_playlist->i_size;
500 retval = mediacontrol_PlaylistSeq__alloc( i_playlist_size );
502 for( i_index = 0 ; i_index < i_playlist_size ; i_index++ )
504 retval->data[i_index] = strdup( p_playlist->pp_items[i_index]->input.psz_uri );
506 vlc_mutex_unlock( &p_playlist->object_lock );
511 mediacontrol_RGBPicture*
512 _mediacontrol_createRGBPicture( int i_width, int i_height, long i_chroma, long long l_date,
513 char* p_data, int i_datasize )
515 mediacontrol_RGBPicture *retval;
517 retval = mediacontrol_RGBPicture__alloc( i_datasize );
520 retval->width = i_width;
521 retval->height = i_height;
522 retval->type = i_chroma;
523 retval->date = l_date;
524 retval->size = i_datasize;
525 memcpy( retval->data, p_data, i_datasize );
530 mediacontrol_RGBPicture *
531 mediacontrol_snapshot( mediacontrol_Instance *self,
532 const mediacontrol_Position * a_position,
533 mediacontrol_Exception *exception )
535 mediacontrol_RGBPicture *retval = NULL;
536 input_thread_t* p_input = self->p_playlist->p_input;
537 vout_thread_t *p_vout = NULL;
539 snapshot_t **pointer;
542 snapshot_t *p_best_snapshot;
548 exception=mediacontrol_exception_init( exception );
551 if( var_Get( self->p_vlc, "snapshot-id", &val ) == VLC_SUCCESS )
552 p_vout = vlc_object_get( self->p_vlc, val.i_int );
555 /* FIXME: if in p_libvlc, we cannot have multiple video outputs */
556 /* Once corrected, search for snapshot-id to modify all instances */
557 if( var_Get( p_input, "snapshot-id", &val ) != VLC_SUCCESS )
559 RAISE( mediacontrol_InternalException, "No snapshot-id in p_input" );
562 p_vout = vlc_object_get( self->p_vlc, val.i_int );
566 RAISE( mediacontrol_InternalException, "No snapshot module" );
571 /* We test if the vout is a snapshot module. We cannot test
572 pvout_psz_object_name( which is NULL ). But we can check if
573 there are snapshot-specific variables */
574 if( var_Get( p_vout, "snapshot-datasize", &val ) != VLC_SUCCESS )
576 RAISE( mediacontrol_InternalException, "No snapshot module" );
577 vlc_object_release( p_vout );
580 i_datasize = val.i_int;
582 /* Handle the a_position parameter */
583 if( ! ( a_position->origin == mediacontrol_RelativePosition
584 && a_position->value == 0 ) )
586 /* The position is not the current one. Go to it. */
587 mediacontrol_set_media_position( self,
588 ( mediacontrol_Position* ) a_position,
590 if( exception->code )
592 vlc_object_release( p_vout );
597 /* FIXME: We should not go further until we got past the position
598 ( which means that we had the possibility to capture the right
601 vlc_mutex_lock( &p_vout->picture_lock );
603 searched_date = mediacontrol_position2microsecond( p_input,
604 ( mediacontrol_Position * ) a_position );
606 var_Get( p_vout, "snapshot-cache-size", &val );
607 i_cachesize = val.i_int ;
609 var_Get( p_vout, "snapshot-list-pointer", &val );
610 pointer = ( snapshot_t ** )val.p_address;
614 RAISE( mediacontrol_InternalException, "No available snapshot" );
616 vlc_mutex_unlock( &p_vout->picture_lock );
617 vlc_object_release( p_vout );
621 /* Find the more appropriate picture, based on date */
622 p_best_snapshot = pointer[0];
624 for( i_index = 1 ; i_index < i_cachesize ; i_index++ )
626 long l_diff = pointer[i_index]->date - searched_date;
627 if( l_diff > 0 && l_diff < abs( p_best_snapshot->date - searched_date ))
629 /* This one is closer, and _after_ the requested position */
630 p_best_snapshot = pointer[i_index];
634 /* FIXME: add a test for the case that no picture matched the test
635 ( we have p_best_snapshot == pointer[0] */
636 retval = _mediacontrol_createRGBPicture( p_best_snapshot->i_width,
637 p_best_snapshot->i_height,
638 p_vout->output.i_chroma,
639 p_best_snapshot->date,
640 p_best_snapshot->p_data,
643 vlc_mutex_unlock( &p_vout->picture_lock );
644 vlc_object_release( p_vout );
651 mediacontrol_RGBPicture **
652 mediacontrol_all_snapshots( mediacontrol_Instance *self,
653 mediacontrol_Exception *exception )
655 mediacontrol_RGBPicture **retval = NULL;
656 vout_thread_t *p_vout = NULL;
662 snapshot_t **pointer;
665 exception=mediacontrol_exception_init( exception );
667 if( var_Get( self->p_playlist->p_input, "snapshot-id", &val ) == VLC_SUCCESS )
668 p_vout = vlc_object_get( self->p_vlc, val.i_int );
672 RAISE( mediacontrol_InternalException, "No snapshot module" );
676 /* We test if the vout is a snapshot module. We cannot test
677 pvout_psz_object_name( which is NULL ). But we can check if
678 there are snapshot-specific variables */
679 if( var_Get( p_vout, "snapshot-datasize", &val ) != VLC_SUCCESS )
681 RAISE( mediacontrol_InternalException, "No snapshot module" );
682 vlc_object_release( p_vout );
685 i_datasize = val.i_int;
687 vlc_mutex_lock( &p_vout->picture_lock );
689 var_Get( p_vout, "snapshot-cache-size", &val );
690 i_cachesize = val.i_int ;
692 var_Get( p_vout, "snapshot-list-pointer", &val );
693 pointer = ( snapshot_t ** )val.p_address;
697 RAISE( mediacontrol_InternalException, "No available picture" );
699 vlc_mutex_unlock( &p_vout->picture_lock );
700 vlc_object_release( p_vout );
704 retval = ( mediacontrol_RGBPicture** )malloc( (i_cachesize + 1 ) * sizeof( char* ));
706 for( i_index = 0 ; i_index < i_cachesize ; i_index++ )
708 snapshot_t *p_s = pointer[i_index];
709 mediacontrol_RGBPicture *p_rgb;
711 p_rgb = _mediacontrol_createRGBPicture( p_s->i_width,
713 p_vout->output.i_chroma,
718 retval[i_index] = p_rgb;
721 retval[i_cachesize] = NULL;
723 vlc_mutex_unlock( &p_vout->picture_lock );
724 vlc_object_release( p_vout );
732 mediacontrol_display_text( mediacontrol_Instance *self,
733 const char * message,
734 const mediacontrol_Position * begin,
735 const mediacontrol_Position * end,
736 mediacontrol_Exception *exception )
738 input_thread_t *p_input = NULL;
739 vout_thread_t *p_vout = NULL;
741 p_vout = vlc_object_find( self->p_playlist, VLC_OBJECT_VOUT, FIND_CHILD );
744 RAISE( mediacontrol_InternalException, "No video output" );
748 if( begin->origin == mediacontrol_RelativePosition &&
750 end->origin == mediacontrol_RelativePosition )
752 mtime_t i_duration = 0;
754 i_duration = 1000 * mediacontrol_unit_convert( self->p_playlist->p_input,
756 mediacontrol_MediaTime,
759 vout_ShowTextRelative( p_vout, DEFAULT_CHAN, ( char* ) message, NULL,
760 OSD_ALIGN_BOTTOM | OSD_ALIGN_LEFT, 20, 20,
765 mtime_t i_debut, i_fin, i_now;
767 p_input = self->p_playlist->p_input;
770 RAISE( mediacontrol_InternalException, "No input" );
771 vlc_object_release( p_vout );
776 /* i_now = input_ClockGetTS( p_input, NULL, 0 ); */
779 i_debut = mediacontrol_position2microsecond( p_input,
780 ( mediacontrol_Position* ) begin );
783 i_fin = mediacontrol_position2microsecond( p_input,
784 ( mediacontrol_Position * ) end );
787 vout_ShowTextAbsolute( p_vout, DEFAULT_CHAN, ( char* ) message, NULL,
788 OSD_ALIGN_BOTTOM | OSD_ALIGN_LEFT, 20, 20,
792 vlc_object_release( p_vout );
795 mediacontrol_StreamInformation *
796 mediacontrol_get_stream_information( mediacontrol_Instance *self,
797 mediacontrol_PositionKey a_key,
798 mediacontrol_Exception *exception )
800 mediacontrol_StreamInformation *retval;
801 input_thread_t *p_input = self->p_playlist->p_input;
804 retval = ( mediacontrol_StreamInformation* )malloc( sizeof( mediacontrol_StreamInformation ) );
807 RAISE( mediacontrol_InternalException, "Out of memory" );
813 /* No p_input defined */
814 retval->streamstatus = mediacontrol_UndefinedStatus;
815 retval->url = strdup( "None" );
816 retval->position = 0;
821 switch( var_GetInteger( p_input, "state" ) )
824 retval->streamstatus = mediacontrol_PlayingStatus;
827 retval->streamstatus = mediacontrol_PauseStatus;
830 retval->streamstatus = mediacontrol_InitStatus;
833 retval->streamstatus = mediacontrol_EndStatus;
836 retval->streamstatus = mediacontrol_UndefinedStatus;
840 retval->url = strdup( p_input->input.p_item->psz_uri );
842 /* TIME and LENGTH are in microseconds. We want them in ms */
843 var_Get( p_input, "time", &val);
844 retval->position = val.i_time / 1000;
846 var_Get( p_input, "length", &val);
847 retval->length = val.i_time / 1000;
849 retval->position = mediacontrol_unit_convert( p_input,
850 mediacontrol_MediaTime, a_key,
852 retval->length = mediacontrol_unit_convert( p_input,
853 mediacontrol_MediaTime, a_key,
860 mediacontrol_sound_get_volume( mediacontrol_Instance *self,
861 mediacontrol_Exception *exception )
864 audio_volume_t i_volume;
868 RAISE( mediacontrol_InternalException, "No interface module" );
871 aout_VolumeGet( self->p_intf, &i_volume );
877 mediacontrol_sound_set_volume( mediacontrol_Instance *self,
878 const unsigned short volume,
879 mediacontrol_Exception *exception )
883 RAISE( mediacontrol_InternalException, "No interface module" );
886 aout_VolumeSet( self->p_intf,( audio_volume_t )volume );