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)
114 case mediacontrol_AbsolutePosition:
115 return (1000 * mediacontrol_unit_convert(p_input,
117 mediacontrol_MediaTime, /* to */
120 case mediacontrol_RelativePosition:
126 vlc_mutex_lock( &p_input->stream.stream_lock );
127 l_offset = p_input->stream.p_selected_area->i_tell;
128 vlc_mutex_unlock( &p_input->stream.stream_lock );
130 l_current = 1000 * mediacontrol_unit_convert(p_input,
131 mediacontrol_ByteCount,
132 mediacontrol_MediaTime,
134 l_pos = 1000 * mediacontrol_unit_convert(p_input,
136 mediacontrol_MediaTime,
138 return l_current + l_pos;
141 case mediacontrol_ModuloPosition:
143 long long l_duration;
146 l_duration = 1000 * mediacontrol_unit_convert(p_input,
147 mediacontrol_ByteCount,
148 mediacontrol_MediaTime,
149 p_input->stream.p_selected_area->i_size);
150 l_pos = (1000 * mediacontrol_unit_convert(p_input,
152 mediacontrol_MediaTime, /* to */
154 return l_pos % l_duration;
161 mediacontrol_RGBPicture*
162 mediacontrol_RGBPicture__alloc (int datasize)
164 mediacontrol_RGBPicture* pic;
166 pic = (mediacontrol_RGBPicture*)malloc(sizeof(mediacontrol_RGBPicture));
170 pic->size = datasize;
171 pic->data = (char*)malloc(datasize);
176 mediacontrol_RGBPicture__free (mediacontrol_RGBPicture* pic)
183 mediacontrol_PlaylistSeq*
184 mediacontrol_PlaylistSeq__alloc (int size)
186 mediacontrol_PlaylistSeq* ps;
188 ps = (mediacontrol_PlaylistSeq*)malloc(sizeof(mediacontrol_PlaylistSeq));
193 ps->data = (char**)malloc(size * sizeof(char*));
198 mediacontrol_PlaylistSeq__free (mediacontrol_PlaylistSeq* ps)
203 for (i = 0; i < ps->size; i++)
210 mediacontrol_Exception*
211 mediacontrol_exception_init(mediacontrol_Exception *exception)
213 if (exception == NULL)
215 exception = (mediacontrol_Exception*)malloc(sizeof(mediacontrol_Exception));
219 exception->message = NULL;
224 mediacontrol_exception_free(mediacontrol_Exception *exception)
229 free(exception->message);
233 mediacontrol_Instance* mediacontrol_new_from_object(vlc_object_t* p_object,
234 mediacontrol_Exception *exception)
236 mediacontrol_Instance* retval;
239 p_vlc = vlc_object_find(p_object, VLC_OBJECT_ROOT, FIND_PARENT);
242 RAISE(mediacontrol_InternalException, "Unable to initialize VLC");
245 retval = (mediacontrol_Instance*)malloc(sizeof(mediacontrol_Instance));
246 retval->p_vlc = p_vlc;
247 retval->vlc_object_id = p_vlc->i_object_id;
249 /* We can keep references on these, which should not change. Is it true ? */
250 retval->p_playlist = vlc_object_find(p_vlc, VLC_OBJECT_PLAYLIST, FIND_ANYWHERE);
251 retval->p_intf = vlc_object_find(p_vlc, VLC_OBJECT_INTF, FIND_ANYWHERE);
253 if (! retval->p_playlist || ! retval->p_intf)
255 RAISE(mediacontrol_InternalException, "No available interface");
261 /* Returns the current position in the stream. The returned value can
262 be relative or absolute (according to PositionOrigin) and the unit
263 is set by PositionKey */
264 mediacontrol_Position*
265 mediacontrol_get_media_position(mediacontrol_Instance *self,
266 const mediacontrol_PositionOrigin an_origin,
267 const mediacontrol_PositionKey a_key,
268 mediacontrol_Exception *exception)
270 mediacontrol_Position* retval;
272 input_thread_t * p_input = self->p_playlist->p_input;;
274 exception=mediacontrol_exception_init(exception);
276 retval = (mediacontrol_Position*)malloc(sizeof(mediacontrol_Position));
277 retval->origin = an_origin;
283 RAISE(mediacontrol_InternalException, "No input thread.");
290 if ( an_origin == mediacontrol_RelativePosition
291 || an_origin == mediacontrol_ModuloPosition )
293 /* Relative or ModuloPosition make no sense */
298 /* We are asked for an AbsolutePosition. */
299 vlc_mutex_lock( &p_input->stream.stream_lock );
300 l_offset = p_input->stream.p_selected_area->i_tell;
301 vlc_mutex_unlock( &p_input->stream.stream_lock );
303 retval->value = mediacontrol_unit_convert(p_input,
304 mediacontrol_ByteCount,
310 /* Sets the media position */
312 mediacontrol_set_media_position(mediacontrol_Instance *self,
313 const mediacontrol_Position * a_position,
314 mediacontrol_Exception *exception)
316 off_t l_offset_destination = 0;
318 input_thread_t * p_input = self->p_playlist->p_input;
320 exception=mediacontrol_exception_init(exception);
323 RAISE(mediacontrol_InternalException, "No input thread.");
327 if ( !p_input->stream.b_seekable )
329 RAISE(mediacontrol_InvalidPosition, "Stream not seekable");
333 /* FIXME FIXME FIXME input_Seek is deprecated, and I don't know how to fix that --fenrir */
334 /* You need to do a var_SetFloat( p_input, pos ); where pos is a float between 0.0 and 1.0 */
335 RAISE(mediacontrol_InvalidPosition, "mediacontrol_set_media_position is not usable for now");
338 l_offset_destination = a_position->value;
340 i_whence |= INPUT_SEEK_BYTES;
341 l_offset_destination = mediacontrol_unit_convert(p_input,
343 mediacontrol_ByteCount,
344 l_offset_destination);
346 switch ( a_position->origin)
348 case mediacontrol_RelativePosition:
349 i_whence |= INPUT_SEEK_CUR;
351 case mediacontrol_ModuloPosition:
352 i_whence |= INPUT_SEEK_END;
354 case mediacontrol_AbsolutePosition:
355 i_whence |= INPUT_SEEK_SET;
358 i_whence |= INPUT_SEEK_SET;
362 /* Now we can set the position. The lock is taken in the input_Seek
363 function (cf input_ext-intf.c) */
364 input_Seek (p_input, l_offset_destination, i_whence);
369 /* Starts playing a stream */
371 mediacontrol_start(mediacontrol_Instance *self,
372 const mediacontrol_Position * a_position,
373 mediacontrol_Exception *exception)
375 playlist_t * p_playlist = self->p_playlist;
377 exception=mediacontrol_exception_init(exception);
380 RAISE(mediacontrol_PlaylistException, "No available playlist");
384 vlc_mutex_lock( &p_playlist->object_lock );
385 if (p_playlist->i_size)
389 vlc_mutex_unlock( &p_playlist->object_lock );
392 val.i_int = mediacontrol_position2microsecond(p_playlist->p_input, a_position) / 1000000;
393 var_Set (p_playlist, "start-time", val);
395 playlist_Play( p_playlist );
399 RAISE(mediacontrol_PlaylistException, "Empty playlist.");
400 vlc_mutex_unlock( &p_playlist->object_lock );
408 mediacontrol_pause(mediacontrol_Instance *self,
409 const mediacontrol_Position * a_position,
410 mediacontrol_Exception *exception)
412 input_thread_t *p_input = self->p_playlist->p_input;;
414 /* FIXME: use the a_position parameter */
415 exception=mediacontrol_exception_init(exception);
418 var_SetInteger( p_input, "state", PAUSE_S );
422 RAISE(mediacontrol_InternalException, "No input");
427 mediacontrol_resume(mediacontrol_Instance *self,
428 const mediacontrol_Position * a_position,
429 mediacontrol_Exception *exception)
431 input_thread_t *p_input = self->p_playlist->p_input;
433 /* FIXME: use the a_position parameter */
434 exception=mediacontrol_exception_init(exception);
437 var_SetInteger( p_input, "state", PAUSE_S );
441 RAISE(mediacontrol_InternalException, "No input");
446 mediacontrol_stop(mediacontrol_Instance *self,
447 const mediacontrol_Position * a_position,
448 mediacontrol_Exception *exception)
450 /* FIXME: use the a_position parameter */
451 exception=mediacontrol_exception_init(exception);
452 if (!self->p_playlist)
454 RAISE(mediacontrol_PlaylistException, "No playlist");
458 playlist_Stop( self->p_playlist );
464 mediacontrol_playlist_add_item(mediacontrol_Instance *self,
465 const char * psz_file,
466 mediacontrol_Exception *exception)
468 exception=mediacontrol_exception_init(exception);
469 if (!self->p_playlist)
471 RAISE(mediacontrol_InternalException, "No playlist");
475 playlist_Add (self->p_playlist, psz_file, psz_file , PLAYLIST_REPLACE, 0);
481 mediacontrol_playlist_clear(mediacontrol_Instance *self,
482 mediacontrol_Exception *exception)
486 exception=mediacontrol_exception_init(exception);
487 if (!self->p_playlist)
489 RAISE(mediacontrol_PlaylistException, "No playlist");
493 for (i_index = 0 ; i_index < self->p_playlist->i_size ; i_index++)
495 playlist_Delete (self->p_playlist, i_index);
501 mediacontrol_PlaylistSeq *
502 mediacontrol_playlist_get_list(mediacontrol_Instance *self,
503 mediacontrol_Exception *exception)
505 mediacontrol_PlaylistSeq *retval;
507 playlist_t * p_playlist = self->p_playlist;;
510 exception=mediacontrol_exception_init(exception);
513 RAISE(mediacontrol_PlaylistException, "No playlist");
517 vlc_mutex_lock( &p_playlist->object_lock );
518 i_playlist_size = p_playlist->i_size;
520 retval = mediacontrol_PlaylistSeq__alloc(i_playlist_size);
522 for (i_index = 0 ; i_index < i_playlist_size ; i_index++)
524 retval->data[i_index] = strdup (p_playlist->pp_items[i_index]->input.psz_uri);
526 vlc_mutex_unlock( &p_playlist->object_lock );
531 mediacontrol_RGBPicture*
532 _mediacontrol_createRGBPicture (int i_width, int i_height, long i_chroma, long long l_date,
533 char* p_data, int i_datasize)
535 mediacontrol_RGBPicture *retval;
537 retval = mediacontrol_RGBPicture__alloc (i_datasize);
540 retval->width = i_width;
541 retval->height = i_height;
542 retval->type = i_chroma;
543 retval->date = l_date;
544 retval->size = i_datasize;
545 memcpy (retval->data, p_data, i_datasize);
550 mediacontrol_RGBPicture *
551 mediacontrol_snapshot(mediacontrol_Instance *self,
552 const mediacontrol_Position * a_position,
553 mediacontrol_Exception *exception)
555 mediacontrol_RGBPicture *retval = NULL;
556 input_thread_t* p_input = self->p_playlist->p_input;
557 vout_thread_t *p_vout = NULL;
559 snapshot_t **pointer;
562 snapshot_t *p_best_snapshot;
568 exception=mediacontrol_exception_init(exception);
571 if (var_Get (self->p_vlc, "snapshot-id", &val) == VLC_SUCCESS)
572 p_vout = vlc_object_get (self->p_vlc, val.i_int);
575 /* FIXME: if in p_libvlc, we cannot have multiple video outputs */
576 /* Once corrected, search for snapshot-id to modify all instances */
577 if (var_Get (p_input, "snapshot-id", &val) != VLC_SUCCESS)
579 RAISE(mediacontrol_InternalException, "No snapshot-id in p_input");
582 p_vout = vlc_object_get (self->p_vlc, val.i_int);
586 RAISE(mediacontrol_InternalException, "No snapshot module");
591 /* We test if the vout is a snapshot module. We cannot test
592 pvout_psz_object_name (which is NULL). But we can check if
593 there are snapshot-specific variables */
594 if (var_Get( p_vout, "snapshot-datasize", &val ) != VLC_SUCCESS)
596 RAISE(mediacontrol_InternalException, "No snapshot module");
597 vlc_object_release(p_vout);
600 i_datasize = val.i_int;
602 /* Handle the a_position parameter */
603 if (! (a_position->origin == mediacontrol_RelativePosition
604 && a_position->value == 0))
606 /* The position is not the current one. Go to it. */
607 mediacontrol_set_media_position (self, (mediacontrol_Position*)a_position, exception);
610 vlc_object_release(p_vout);
615 /* FIXME: We should not go further until we got past the position
616 (which means that we had the possibility to capture the right
619 vlc_mutex_lock( &p_vout->picture_lock );
621 searched_date = mediacontrol_position2microsecond (p_input,
622 (mediacontrol_Position *)a_position);
624 var_Get( p_vout, "snapshot-cache-size", &val );
625 i_cachesize = val.i_int ;
627 var_Get( p_vout, "snapshot-list-pointer", &val );
628 pointer = (snapshot_t **)val.p_address;
632 RAISE(mediacontrol_InternalException, "No available snapshot");
634 vlc_mutex_unlock( &p_vout->picture_lock );
635 vlc_object_release( p_vout );
639 /* Find the more appropriate picture, based on date */
640 p_best_snapshot = pointer[0];
642 for (i_index = 1 ; i_index < i_cachesize ; i_index++)
644 long l_diff = pointer[i_index]->date - searched_date;
645 if (l_diff > 0 && l_diff < abs(p_best_snapshot->date - searched_date))
647 /* This one is closer, and _after_ the requested position */
648 p_best_snapshot = pointer[i_index];
652 /* FIXME: add a test for the case that no picture matched the test
653 (we have p_best_snapshot == pointer[0] */
654 retval = _mediacontrol_createRGBPicture (p_best_snapshot->i_width,
655 p_best_snapshot->i_height,
656 p_vout->output.i_chroma,
657 p_best_snapshot->date,
658 p_best_snapshot->p_data,
661 vlc_mutex_unlock( &p_vout->picture_lock );
662 vlc_object_release( p_vout );
669 mediacontrol_RGBPicture **
670 mediacontrol_all_snapshots(mediacontrol_Instance *self,
671 mediacontrol_Exception *exception)
673 mediacontrol_RGBPicture **retval = NULL;
674 vout_thread_t *p_vout = NULL;
680 snapshot_t **pointer;
683 exception=mediacontrol_exception_init(exception);
685 if (var_Get (self->p_playlist->p_input, "snapshot-id", &val) == VLC_SUCCESS)
686 p_vout = vlc_object_get (self->p_vlc, val.i_int);
690 RAISE(mediacontrol_InternalException, "No snapshot module");
694 /* We test if the vout is a snapshot module. We cannot test
695 pvout_psz_object_name (which is NULL). But we can check if
696 there are snapshot-specific variables */
697 if (var_Get( p_vout, "snapshot-datasize", &val ) != VLC_SUCCESS)
699 RAISE(mediacontrol_InternalException, "No snapshot module");
700 vlc_object_release(p_vout);
703 i_datasize = val.i_int;
705 vlc_mutex_lock( &p_vout->picture_lock );
707 var_Get( p_vout, "snapshot-cache-size", &val );
708 i_cachesize = val.i_int ;
710 var_Get( p_vout, "snapshot-list-pointer", &val );
711 pointer = (snapshot_t **)val.p_address;
715 RAISE(mediacontrol_InternalException, "No available picture");
717 vlc_mutex_unlock( &p_vout->picture_lock );
718 vlc_object_release( p_vout );
722 retval = (mediacontrol_RGBPicture**)malloc((i_cachesize + 1) * sizeof(char*));
724 for (i_index = 0 ; i_index < i_cachesize ; i_index++)
726 snapshot_t *p_s = pointer[i_index];
727 mediacontrol_RGBPicture *p_rgb;
729 p_rgb = _mediacontrol_createRGBPicture (p_s->i_width,
731 p_vout->output.i_chroma,
736 retval[i_index] = p_rgb;
739 retval[i_cachesize] = NULL;
741 vlc_mutex_unlock( &p_vout->picture_lock );
742 vlc_object_release( p_vout );
750 mediacontrol_display_text(mediacontrol_Instance *self,
751 const char * message,
752 const mediacontrol_Position * begin,
753 const mediacontrol_Position * end,
754 mediacontrol_Exception *exception)
756 input_thread_t *p_input = NULL;
757 vout_thread_t *p_vout = NULL;
759 p_vout = vlc_object_find( self->p_vlc, VLC_OBJECT_VOUT, FIND_ANYWHERE );
762 RAISE(mediacontrol_InternalException, "No video output");
766 if (begin->origin == mediacontrol_RelativePosition &&
768 end->origin == mediacontrol_RelativePosition)
770 mtime_t i_duration = 0;
772 i_duration = 1000 * mediacontrol_unit_convert(self->p_playlist->p_input,
774 mediacontrol_MediaTime,
777 vout_ShowTextRelative( p_vout, (char*)message, NULL,
778 OSD_ALIGN_BOTTOM|OSD_ALIGN_LEFT, 20, 20,
783 mtime_t i_debut, i_fin, i_now;
785 p_input = self->p_playlist->p_input;
788 RAISE(mediacontrol_InternalException, "No input");
789 vlc_object_release(p_vout);
793 i_now = input_ClockGetTS (p_input, NULL, 0);
795 i_debut = mediacontrol_position2microsecond (p_input, (mediacontrol_Position *) begin);
798 i_fin = mediacontrol_position2microsecond (p_input, (mediacontrol_Position *) end);
801 vout_ShowTextAbsolute( p_vout, (char*)message, NULL,
802 OSD_ALIGN_BOTTOM|OSD_ALIGN_LEFT, 20, 20,
806 vlc_object_release( p_vout );
809 mediacontrol_StreamInformation *
810 mediacontrol_get_stream_information(mediacontrol_Instance *self,
811 mediacontrol_PositionKey a_key,
812 mediacontrol_Exception *exception)
814 mediacontrol_StreamInformation *retval;
815 input_thread_t *p_input = self->p_playlist->p_input;
817 retval = (mediacontrol_StreamInformation*)malloc(sizeof(mediacontrol_StreamInformation));
820 RAISE(mediacontrol_InternalException, "Out of memory");
826 /* No p_input defined */
827 retval->streamstatus = mediacontrol_UndefinedStatus;
828 retval->url = strdup ("None");
829 retval->position = 0;
834 switch (p_input->stream.control.i_status)
837 retval->streamstatus = mediacontrol_UndefinedStatus;
840 retval->streamstatus = mediacontrol_PlayingStatus;
843 retval->streamstatus = mediacontrol_PauseStatus;
846 retval->streamstatus = mediacontrol_ForwardStatus;
849 retval->streamstatus = mediacontrol_BackwardStatus;
852 retval->streamstatus = mediacontrol_InitStatus;
855 retval->streamstatus = mediacontrol_EndStatus;
858 retval->streamstatus = mediacontrol_UndefinedStatus;
862 retval->url = strdup (p_input->psz_source);
864 demux_Control(p_input, DEMUX_GET_TIME, &(retval->position));
865 demux_Control(p_input, DEMUX_GET_LENGTH, &(retval->length));
866 /* TIME and LENGTH are in microseconds. We want them in ms */
867 retval->position /= 1000;
868 retval->length /= 1000;
870 retval->position = mediacontrol_unit_convert(p_input,
871 mediacontrol_MediaTime, a_key,
873 retval->length = mediacontrol_unit_convert(p_input,
874 mediacontrol_MediaTime, a_key,
881 mediacontrol_sound_get_volume(mediacontrol_Instance *self,
882 mediacontrol_Exception *exception)
885 audio_volume_t i_volume;
889 RAISE(mediacontrol_InternalException, "No interface module");
892 aout_VolumeGet( self->p_intf, &i_volume );
898 mediacontrol_sound_set_volume(mediacontrol_Instance *self,
899 const unsigned short volume,
900 mediacontrol_Exception *exception)
904 RAISE(mediacontrol_InternalException, "No interface module");
907 aout_VolumeSet( self->p_intf, (audio_volume_t)volume );