1 /*****************************************************************************
2  * video.c: libvlc new API video functions
3  *****************************************************************************
4  * Copyright (C) 2005-2010 VLC authors and VideoLAN
5  *
6  * $Id: acbba3a30fe84e2847cfd8cd63d595e6a54acac3 $
7  *
8  * Authors: Clément Stenac <zorglub@videolan.org>
9  *          Filippo Carone <littlejohn@videolan.org>
10  *          Jean-Paul Saman <jpsaman _at_ m2x _dot_ nl>
11  *          Damien Fouilleul <damienf a_t videolan dot org>
12  *
13  * This program is free software; you can redistribute it and/or modify it
14  * under the terms of the GNU Lesser General Public License as published by
15  * the Free Software Foundation; either version 2.1 of the License, or
16  * (at your option) any later version.
17  *
18  * This program is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21  * GNU Lesser General Public License for more details.
22  *
23  * You should have received a copy of the GNU Lesser General Public License
24  * along with this program; if not, write to the Free Software Foundation,
25  * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
26  *****************************************************************************/
27 
28 #ifdef HAVE_CONFIG_H
29 # include "config.h"
30 #endif
31 
32 #include <vlc/libvlc.h>
33 #include <vlc/libvlc_renderer_discoverer.h>
34 #include <vlc/libvlc_media.h>
35 #include <vlc/libvlc_media_player.h>
36 
37 #include <vlc_common.h>
38 #include <vlc_modules.h>
39 #include <vlc_input.h>
40 #include <vlc_vout.h>
41 #include <vlc_url.h>
42 
43 #include "libvlc_internal.h"
44 #include "media_player_internal.h"
45 #include <math.h>
46 #include <assert.h>
47 
48 /*
49  * Remember to release the returned vout_thread_t.
50  */
GetVouts(libvlc_media_player_t * p_mi,size_t * n)51 static vout_thread_t **GetVouts( libvlc_media_player_t *p_mi, size_t *n )
52 {
53     input_thread_t *p_input = libvlc_get_input_thread( p_mi );
54     if( !p_input )
55     {
56         *n = 0;
57         return NULL;
58     }
59 
60     vout_thread_t **pp_vouts;
61     if (input_Control( p_input, INPUT_GET_VOUTS, &pp_vouts, n))
62     {
63         *n = 0;
64         pp_vouts = NULL;
65     }
66     vlc_object_release (p_input);
67     return pp_vouts;
68 }
69 
GetVout(libvlc_media_player_t * mp,size_t num)70 static vout_thread_t *GetVout (libvlc_media_player_t *mp, size_t num)
71 {
72     vout_thread_t *p_vout = NULL;
73     size_t n;
74     vout_thread_t **pp_vouts = GetVouts (mp, &n);
75     if (pp_vouts == NULL)
76         goto err;
77 
78     if (num < n)
79         p_vout = pp_vouts[num];
80 
81     for (size_t i = 0; i < n; i++)
82         if (i != num)
83             vlc_object_release (pp_vouts[i]);
84     free (pp_vouts);
85 
86     if (p_vout == NULL)
87 err:
88         libvlc_printerr ("Video output not active");
89     return p_vout;
90 }
91 
92 /**********************************************************************
93  * Exported functions
94  **********************************************************************/
95 
libvlc_set_fullscreen(libvlc_media_player_t * p_mi,int b_fullscreen)96 void libvlc_set_fullscreen( libvlc_media_player_t *p_mi, int b_fullscreen )
97 {
98     /* This will work even if the video is not currently active */
99     var_SetBool (p_mi, "fullscreen", !!b_fullscreen);
100 
101     /* Apply to current video outputs (if any) */
102     size_t n;
103     vout_thread_t **pp_vouts = GetVouts (p_mi, &n);
104     for (size_t i = 0; i < n; i++)
105     {
106         var_SetBool (pp_vouts[i], "fullscreen", b_fullscreen);
107         vlc_object_release (pp_vouts[i]);
108     }
109     free (pp_vouts);
110 }
111 
libvlc_get_fullscreen(libvlc_media_player_t * p_mi)112 int libvlc_get_fullscreen( libvlc_media_player_t *p_mi )
113 {
114     return var_GetBool (p_mi, "fullscreen");
115 }
116 
libvlc_toggle_fullscreen(libvlc_media_player_t * p_mi)117 void libvlc_toggle_fullscreen( libvlc_media_player_t *p_mi )
118 {
119     bool b_fullscreen = var_ToggleBool (p_mi, "fullscreen");
120 
121     /* Apply to current video outputs (if any) */
122     size_t n;
123     vout_thread_t **pp_vouts = GetVouts (p_mi, &n);
124     for (size_t i = 0; i < n; i++)
125     {
126         vout_thread_t *p_vout = pp_vouts[i];
127 
128         var_SetBool (p_vout, "fullscreen", b_fullscreen);
129         vlc_object_release (p_vout);
130     }
131     free (pp_vouts);
132 }
133 
libvlc_video_set_key_input(libvlc_media_player_t * p_mi,unsigned on)134 void libvlc_video_set_key_input( libvlc_media_player_t *p_mi, unsigned on )
135 {
136     var_SetBool (p_mi, "keyboard-events", !!on);
137 }
138 
libvlc_video_set_mouse_input(libvlc_media_player_t * p_mi,unsigned on)139 void libvlc_video_set_mouse_input( libvlc_media_player_t *p_mi, unsigned on )
140 {
141     var_SetBool (p_mi, "mouse-events", !!on);
142 }
143 
144 int
libvlc_video_take_snapshot(libvlc_media_player_t * p_mi,unsigned num,const char * psz_filepath,unsigned int i_width,unsigned int i_height)145 libvlc_video_take_snapshot( libvlc_media_player_t *p_mi, unsigned num,
146                             const char *psz_filepath,
147                             unsigned int i_width, unsigned int i_height )
148 {
149     assert( psz_filepath );
150 
151     vout_thread_t *p_vout = GetVout (p_mi, num);
152     if (p_vout == NULL)
153         return -1;
154 
155     /* FIXME: This is not atomic. All parameters should be passed at once
156      * (obviously _not_ with var_*()). Also, the libvlc object should not be
157      * used for the callbacks: that breaks badly if there are concurrent
158      * media players in the instance. */
159     var_Create( p_vout, "snapshot-width", VLC_VAR_INTEGER );
160     var_SetInteger( p_vout, "snapshot-width", i_width);
161     var_Create( p_vout, "snapshot-height", VLC_VAR_INTEGER );
162     var_SetInteger( p_vout, "snapshot-height", i_height );
163     var_Create( p_vout, "snapshot-path", VLC_VAR_STRING );
164     var_SetString( p_vout, "snapshot-path", psz_filepath );
165     var_Create( p_vout, "snapshot-format", VLC_VAR_STRING );
166     var_SetString( p_vout, "snapshot-format", "png" );
167     var_TriggerCallback( p_vout, "video-snapshot" );
168     vlc_object_release( p_vout );
169     return 0;
170 }
171 
libvlc_video_get_size(libvlc_media_player_t * p_mi,unsigned num,unsigned * restrict px,unsigned * restrict py)172 int libvlc_video_get_size( libvlc_media_player_t *p_mi, unsigned num,
173                            unsigned *restrict px, unsigned *restrict py )
174 {
175     libvlc_media_track_info_t *info;
176     int ret = -1;
177     if (!p_mi->p_md)
178         return ret;
179     int infos = libvlc_media_get_tracks_info(p_mi->p_md, &info);
180     if (infos <= 0)
181         return ret;
182 
183     for (int i = 0; i < infos; i++)
184         if (info[i].i_type == libvlc_track_video && num-- == 0) {
185             *px = info[i].u.video.i_width;
186             *py = info[i].u.video.i_height;
187             ret = 0;
188             break;
189         }
190 
191     free(info);
192     return ret;
193 }
194 
libvlc_video_get_height(libvlc_media_player_t * p_mi)195 int libvlc_video_get_height( libvlc_media_player_t *p_mi )
196 {
197     unsigned width, height;
198 
199     if (libvlc_video_get_size (p_mi, 0, &width, &height))
200         return 0;
201     return height;
202 }
203 
libvlc_video_get_width(libvlc_media_player_t * p_mi)204 int libvlc_video_get_width( libvlc_media_player_t *p_mi )
205 {
206     unsigned width, height;
207 
208     if (libvlc_video_get_size (p_mi, 0, &width, &height))
209         return 0;
210     return width;
211 }
212 
libvlc_video_get_cursor(libvlc_media_player_t * mp,unsigned num,int * restrict px,int * restrict py)213 int libvlc_video_get_cursor( libvlc_media_player_t *mp, unsigned num,
214                              int *restrict px, int *restrict py )
215 {
216     vout_thread_t *p_vout = GetVout (mp, num);
217     if (p_vout == NULL)
218         return -1;
219 
220     var_GetCoords (p_vout, "mouse-moved", px, py);
221     vlc_object_release (p_vout);
222     return 0;
223 }
224 
libvlc_media_player_has_vout(libvlc_media_player_t * p_mi)225 unsigned libvlc_media_player_has_vout( libvlc_media_player_t *p_mi )
226 {
227     size_t n;
228     vout_thread_t **pp_vouts = GetVouts (p_mi, &n);
229     for (size_t i = 0; i < n; i++)
230         vlc_object_release (pp_vouts[i]);
231     free (pp_vouts);
232     return n;
233 }
234 
libvlc_video_get_scale(libvlc_media_player_t * mp)235 float libvlc_video_get_scale( libvlc_media_player_t *mp )
236 {
237     float f_scale = var_GetFloat (mp, "zoom");
238     if (var_GetBool (mp, "autoscale"))
239         f_scale = 0.f;
240     return f_scale;
241 }
242 
libvlc_video_set_scale(libvlc_media_player_t * p_mp,float f_scale)243 void libvlc_video_set_scale( libvlc_media_player_t *p_mp, float f_scale )
244 {
245     if (isfinite(f_scale) && f_scale != 0.f)
246         var_SetFloat (p_mp, "zoom", f_scale);
247     var_SetBool (p_mp, "autoscale", f_scale == 0.f);
248 
249     /* Apply to current video outputs (if any) */
250     size_t n;
251     vout_thread_t **pp_vouts = GetVouts (p_mp, &n);
252     for (size_t i = 0; i < n; i++)
253     {
254         vout_thread_t *p_vout = pp_vouts[i];
255 
256         if (isfinite(f_scale) && f_scale != 0.f)
257             var_SetFloat (p_vout, "zoom", f_scale);
258         var_SetBool (p_vout, "autoscale", f_scale == 0.f);
259         vlc_object_release (p_vout);
260     }
261     free (pp_vouts);
262 }
263 
libvlc_video_get_aspect_ratio(libvlc_media_player_t * p_mi)264 char *libvlc_video_get_aspect_ratio( libvlc_media_player_t *p_mi )
265 {
266     return var_GetNonEmptyString (p_mi, "aspect-ratio");
267 }
268 
libvlc_video_set_aspect_ratio(libvlc_media_player_t * p_mi,const char * psz_aspect)269 void libvlc_video_set_aspect_ratio( libvlc_media_player_t *p_mi,
270                                     const char *psz_aspect )
271 {
272     if (psz_aspect == NULL)
273         psz_aspect = "";
274     var_SetString (p_mi, "aspect-ratio", psz_aspect);
275 
276     size_t n;
277     vout_thread_t **pp_vouts = GetVouts (p_mi, &n);
278     for (size_t i = 0; i < n; i++)
279     {
280         vout_thread_t *p_vout = pp_vouts[i];
281 
282         var_SetString (p_vout, "aspect-ratio", psz_aspect);
283         vlc_object_release (p_vout);
284     }
285     free (pp_vouts);
286 }
287 
libvlc_video_new_viewpoint(void)288 libvlc_video_viewpoint_t *libvlc_video_new_viewpoint(void)
289 {
290     libvlc_video_viewpoint_t *p_vp = malloc(sizeof *p_vp);
291     if (unlikely(p_vp == NULL))
292         return NULL;
293     p_vp->f_yaw = p_vp->f_pitch = p_vp->f_roll = p_vp->f_field_of_view = 0.0f;
294     return p_vp;
295 }
296 
libvlc_video_update_viewpoint(libvlc_media_player_t * p_mi,const libvlc_video_viewpoint_t * p_viewpoint,bool b_absolute)297 int libvlc_video_update_viewpoint( libvlc_media_player_t *p_mi,
298                                    const libvlc_video_viewpoint_t *p_viewpoint,
299                                    bool b_absolute )
300 {
301     vlc_viewpoint_t update = {
302         .yaw   = p_viewpoint->f_yaw,
303         .pitch = p_viewpoint->f_pitch,
304         .roll  = p_viewpoint->f_roll,
305         .fov   = p_viewpoint->f_field_of_view,
306     };
307 
308     input_thread_t *p_input_thread = libvlc_get_input_thread( p_mi );
309     if( p_input_thread != NULL )
310     {
311         if( input_UpdateViewpoint( p_input_thread, &update,
312                                    b_absolute ) != VLC_SUCCESS )
313         {
314             vlc_object_release( p_input_thread );
315             return -1;
316         }
317         vlc_object_release( p_input_thread );
318         return 0;
319     }
320 
321     /* Save the viewpoint in case the input is not created yet */
322     if( !b_absolute )
323     {
324         p_mi->viewpoint.yaw += update.yaw;
325         p_mi->viewpoint.pitch += update.pitch;
326         p_mi->viewpoint.roll += update.roll;
327         p_mi->viewpoint.fov += update.fov;
328     }
329     else
330         p_mi->viewpoint = update;
331 
332     vlc_viewpoint_clip( &p_mi->viewpoint );
333 
334     return 0;
335 }
336 
libvlc_video_get_spu(libvlc_media_player_t * p_mi)337 int libvlc_video_get_spu( libvlc_media_player_t *p_mi )
338 {
339     input_thread_t *p_input_thread = libvlc_get_input_thread( p_mi );
340 
341     if( !p_input_thread )
342     {
343         libvlc_printerr( "No active input" );
344         return -1;
345     }
346 
347     int i_spu = var_GetInteger( p_input_thread, "spu-es" );
348     vlc_object_release( p_input_thread );
349     return i_spu;
350 }
351 
libvlc_video_get_spu_count(libvlc_media_player_t * p_mi)352 int libvlc_video_get_spu_count( libvlc_media_player_t *p_mi )
353 {
354     input_thread_t *p_input_thread = libvlc_get_input_thread( p_mi );
355     int i_spu_count;
356 
357     if( !p_input_thread )
358         return 0;
359 
360     i_spu_count = var_CountChoices( p_input_thread, "spu-es" );
361     vlc_object_release( p_input_thread );
362     return i_spu_count;
363 }
364 
365 libvlc_track_description_t *
libvlc_video_get_spu_description(libvlc_media_player_t * p_mi)366         libvlc_video_get_spu_description( libvlc_media_player_t *p_mi )
367 {
368     return libvlc_get_track_description( p_mi, "spu-es" );
369 }
370 
libvlc_video_set_spu(libvlc_media_player_t * p_mi,int i_spu)371 int libvlc_video_set_spu( libvlc_media_player_t *p_mi, int i_spu )
372 {
373     input_thread_t *p_input_thread = libvlc_get_input_thread( p_mi );
374     vlc_value_t list;
375     int i_ret = -1;
376 
377     if( !p_input_thread )
378         return -1;
379 
380     var_Change (p_input_thread, "spu-es", VLC_VAR_GETCHOICES, &list, NULL);
381     for (int i = 0; i < list.p_list->i_count; i++)
382     {
383         if( i_spu == list.p_list->p_values[i].i_int )
384         {
385             if( var_SetInteger( p_input_thread, "spu-es", i_spu ) < 0 )
386                 break;
387             i_ret = 0;
388             goto end;
389         }
390     }
391     libvlc_printerr( "Track identifier not found" );
392 end:
393     vlc_object_release (p_input_thread);
394     var_FreeList (&list, NULL);
395     return i_ret;
396 }
397 
libvlc_video_set_subtitle_file(libvlc_media_player_t * p_mi,const char * psz_subtitle)398 int libvlc_video_set_subtitle_file( libvlc_media_player_t *p_mi,
399                                     const char *psz_subtitle )
400 {
401     input_thread_t *p_input_thread = libvlc_get_input_thread ( p_mi );
402     bool b_ret = false;
403 
404     if( p_input_thread )
405     {
406         char* psz_mrl = vlc_path2uri( psz_subtitle, NULL );
407         if( psz_mrl )
408         {
409             if( !input_AddSlave( p_input_thread, SLAVE_TYPE_SPU, psz_mrl,
410                                  true, false, false ) )
411                 b_ret = true;
412             free( psz_mrl );
413         }
414         vlc_object_release( p_input_thread );
415     }
416     return b_ret;
417 }
418 
libvlc_video_get_spu_delay(libvlc_media_player_t * p_mi)419 int64_t libvlc_video_get_spu_delay( libvlc_media_player_t *p_mi )
420 {
421     input_thread_t *p_input_thread = libvlc_get_input_thread( p_mi );
422     int64_t val = 0;
423 
424     if( p_input_thread )
425     {
426         val = var_GetInteger( p_input_thread, "spu-delay" );
427         vlc_object_release( p_input_thread );
428     }
429     else
430     {
431         libvlc_printerr( "No active input" );
432     }
433 
434     return val;
435 }
436 
libvlc_video_set_spu_delay(libvlc_media_player_t * p_mi,int64_t i_delay)437 int libvlc_video_set_spu_delay( libvlc_media_player_t *p_mi,
438                                 int64_t i_delay )
439 {
440     input_thread_t *p_input_thread = libvlc_get_input_thread( p_mi );
441     int ret = -1;
442 
443     if( p_input_thread )
444     {
445         var_SetInteger( p_input_thread, "spu-delay", i_delay );
446         vlc_object_release( p_input_thread );
447         ret = 0;
448     }
449     else
450     {
451         libvlc_printerr( "No active input" );
452     }
453 
454     return ret;
455 }
456 
457 libvlc_track_description_t *
libvlc_video_get_title_description(libvlc_media_player_t * p_mi)458         libvlc_video_get_title_description( libvlc_media_player_t *p_mi )
459 {
460     return libvlc_get_track_description( p_mi, "title" );
461 }
462 
463 libvlc_track_description_t *
libvlc_video_get_chapter_description(libvlc_media_player_t * p_mi,int i_title)464         libvlc_video_get_chapter_description( libvlc_media_player_t *p_mi,
465                                               int i_title )
466 {
467     char psz_title[sizeof ("title ") + 3 * sizeof (int)];
468     sprintf( psz_title,  "title %2u", i_title );
469     return libvlc_get_track_description( p_mi, psz_title );
470 }
471 
libvlc_video_get_crop_geometry(libvlc_media_player_t * p_mi)472 char *libvlc_video_get_crop_geometry (libvlc_media_player_t *p_mi)
473 {
474     return var_GetNonEmptyString (p_mi, "crop");
475 }
476 
libvlc_video_set_crop_geometry(libvlc_media_player_t * p_mi,const char * psz_geometry)477 void libvlc_video_set_crop_geometry( libvlc_media_player_t *p_mi,
478                                      const char *psz_geometry )
479 {
480     if (psz_geometry == NULL)
481         psz_geometry = "";
482 
483     var_SetString (p_mi, "crop", psz_geometry);
484 
485     size_t n;
486     vout_thread_t **pp_vouts = GetVouts (p_mi, &n);
487 
488     for (size_t i = 0; i < n; i++)
489     {
490         vout_thread_t *p_vout = pp_vouts[i];
491 
492         var_SetString (p_vout, "crop", psz_geometry);
493         vlc_object_release (p_vout);
494     }
495     free (pp_vouts);
496 }
497 
libvlc_video_get_teletext(libvlc_media_player_t * p_mi)498 int libvlc_video_get_teletext( libvlc_media_player_t *p_mi )
499 {
500     return var_GetInteger (p_mi, "vbi-page");
501 }
502 
teletext_enable(input_thread_t * p_input_thread,bool b_enable)503 static void teletext_enable( input_thread_t *p_input_thread, bool b_enable )
504 {
505     if( b_enable )
506     {
507         vlc_value_t list;
508         if( !var_Change( p_input_thread, "teletext-es", VLC_VAR_GETCHOICES,
509                          &list, NULL ) )
510         {
511             if( list.p_list->i_count > 0 )
512                 var_SetInteger( p_input_thread, "spu-es",
513                                 list.p_list->p_values[0].i_int );
514 
515             var_FreeList( &list, NULL );
516         }
517     }
518     else
519         var_SetInteger( p_input_thread, "spu-es", -1 );
520 }
521 
libvlc_video_set_teletext(libvlc_media_player_t * p_mi,int i_page)522 void libvlc_video_set_teletext( libvlc_media_player_t *p_mi, int i_page )
523 {
524     input_thread_t *p_input_thread;
525     vlc_object_t *p_zvbi = NULL;
526     int telx;
527     bool b_key = false;
528 
529     if( i_page >= 0 && i_page < 1000 )
530         var_SetInteger( p_mi, "vbi-page", i_page );
531     else if( i_page >= 1000 )
532     {
533         switch (i_page)
534         {
535             case libvlc_teletext_key_red:
536             case libvlc_teletext_key_green:
537             case libvlc_teletext_key_yellow:
538             case libvlc_teletext_key_blue:
539             case libvlc_teletext_key_index:
540                 b_key = true;
541                 break;
542             default:
543                 libvlc_printerr("Invalid key action");
544                 return;
545         }
546     }
547     else
548     {
549         libvlc_printerr("Invalid page number");
550         return;
551     }
552 
553     p_input_thread = libvlc_get_input_thread( p_mi );
554     if( !p_input_thread ) return;
555 
556     if( var_CountChoices( p_input_thread, "teletext-es" ) <= 0 )
557     {
558         vlc_object_release( p_input_thread );
559         return;
560     }
561 
562     if( i_page == 0 )
563     {
564         teletext_enable( p_input_thread, false );
565     }
566     else
567     {
568         telx = var_GetInteger( p_input_thread, "teletext-es" );
569         if( telx >= 0 )
570         {
571             if( input_GetEsObjects( p_input_thread, telx, &p_zvbi, NULL, NULL )
572                 == VLC_SUCCESS )
573             {
574                 var_SetInteger( p_zvbi, "vbi-page", i_page );
575                 vlc_object_release( p_zvbi );
576             }
577         }
578         else if (!b_key)
579         {
580             /* the "vbi-page" will be selected on es creation */
581             teletext_enable( p_input_thread, true );
582         }
583         else
584             libvlc_printerr("Key action sent while the teletext is disabled");
585     }
586     vlc_object_release( p_input_thread );
587 }
588 
libvlc_toggle_teletext(libvlc_media_player_t * p_mi)589 void libvlc_toggle_teletext( libvlc_media_player_t *p_mi )
590 {
591     input_thread_t *p_input_thread;
592 
593     p_input_thread = libvlc_get_input_thread(p_mi);
594     if( !p_input_thread ) return;
595 
596     if( var_CountChoices( p_input_thread, "teletext-es" ) <= 0 )
597     {
598         vlc_object_release( p_input_thread );
599         return;
600     }
601     const bool b_selected = var_GetInteger( p_input_thread, "teletext-es" ) >= 0;
602     teletext_enable( p_input_thread, !b_selected );
603     vlc_object_release( p_input_thread );
604 }
605 
libvlc_video_get_track_count(libvlc_media_player_t * p_mi)606 int libvlc_video_get_track_count( libvlc_media_player_t *p_mi )
607 {
608     input_thread_t *p_input_thread = libvlc_get_input_thread( p_mi );
609     int i_track_count;
610 
611     if( !p_input_thread )
612         return -1;
613 
614     i_track_count = var_CountChoices( p_input_thread, "video-es" );
615 
616     vlc_object_release( p_input_thread );
617     return i_track_count;
618 }
619 
620 libvlc_track_description_t *
libvlc_video_get_track_description(libvlc_media_player_t * p_mi)621         libvlc_video_get_track_description( libvlc_media_player_t *p_mi )
622 {
623     return libvlc_get_track_description( p_mi, "video-es" );
624 }
625 
libvlc_video_get_track(libvlc_media_player_t * p_mi)626 int libvlc_video_get_track( libvlc_media_player_t *p_mi )
627 {
628     input_thread_t *p_input_thread = libvlc_get_input_thread( p_mi );
629 
630     if( !p_input_thread )
631         return -1;
632 
633     int id = var_GetInteger( p_input_thread, "video-es" );
634     vlc_object_release( p_input_thread );
635     return id;
636 }
637 
libvlc_video_set_track(libvlc_media_player_t * p_mi,int i_track)638 int libvlc_video_set_track( libvlc_media_player_t *p_mi, int i_track )
639 {
640     input_thread_t *p_input_thread = libvlc_get_input_thread( p_mi );
641     vlc_value_t val_list;
642     int i_ret = -1;
643 
644     if( !p_input_thread )
645         return -1;
646 
647     var_Change( p_input_thread, "video-es", VLC_VAR_GETCHOICES, &val_list, NULL );
648     for( int i = 0; i < val_list.p_list->i_count; i++ )
649     {
650         if( i_track == val_list.p_list->p_values[i].i_int )
651         {
652             if( var_SetInteger( p_input_thread, "video-es", i_track ) < 0 )
653                 break;
654             i_ret = 0;
655             goto end;
656         }
657     }
658     libvlc_printerr( "Track identifier not found" );
659 end:
660     var_FreeList( &val_list, NULL );
661     vlc_object_release( p_input_thread );
662     return i_ret;
663 }
664 
665 /******************************************************************************
666  * libvlc_video_set_deinterlace : enable deinterlace
667  *****************************************************************************/
libvlc_video_set_deinterlace(libvlc_media_player_t * p_mi,const char * psz_mode)668 void libvlc_video_set_deinterlace( libvlc_media_player_t *p_mi,
669                                    const char *psz_mode )
670 {
671     if (psz_mode == NULL)
672         psz_mode = "";
673     if (*psz_mode
674      && strcmp (psz_mode, "blend")    && strcmp (psz_mode, "bob")
675      && strcmp (psz_mode, "discard")  && strcmp (psz_mode, "linear")
676      && strcmp (psz_mode, "mean")     && strcmp (psz_mode, "x")
677      && strcmp (psz_mode, "yadif")    && strcmp (psz_mode, "yadif2x")
678      && strcmp (psz_mode, "phosphor") && strcmp (psz_mode, "ivtc"))
679         return;
680 
681     if (*psz_mode)
682     {
683         var_SetString (p_mi, "deinterlace-mode", psz_mode);
684         var_SetInteger (p_mi, "deinterlace", 1);
685     }
686     else
687         var_SetInteger (p_mi, "deinterlace", 0);
688 
689     size_t n;
690     vout_thread_t **pp_vouts = GetVouts (p_mi, &n);
691     for (size_t i = 0; i < n; i++)
692     {
693         vout_thread_t *p_vout = pp_vouts[i];
694 
695         if (*psz_mode)
696         {
697             var_SetString (p_vout, "deinterlace-mode", psz_mode);
698             var_SetInteger (p_vout, "deinterlace", 1);
699         }
700         else
701             var_SetInteger (p_vout, "deinterlace", 0);
702         vlc_object_release (p_vout);
703     }
704     free (pp_vouts);
705 }
706 
707 /* ************** */
708 /* module helpers */
709 /* ************** */
710 
get_filter_str(vlc_object_t * p_parent,const char * psz_name,bool b_add,const char ** ppsz_filter_type,char ** ppsz_filter_value)711 static int get_filter_str( vlc_object_t *p_parent, const char *psz_name,
712                            bool b_add, const char **ppsz_filter_type,
713                            char **ppsz_filter_value)
714 {
715     char *psz_parser;
716     char *psz_string;
717     const char *psz_filter_type;
718 
719     module_t *p_obj = module_find( psz_name );
720     if( !p_obj )
721     {
722         msg_Err( p_parent, "Unable to find filter module \"%s\".", psz_name );
723         return VLC_EGENERIC;
724     }
725 
726     if( module_provides( p_obj, "video filter" ) )
727     {
728         psz_filter_type = "video-filter";
729     }
730     else if( module_provides( p_obj, "sub source" ) )
731     {
732         psz_filter_type = "sub-source";
733     }
734     else if( module_provides( p_obj, "sub filter" ) )
735     {
736         psz_filter_type = "sub-filter";
737     }
738     else
739     {
740         msg_Err( p_parent, "Unknown video filter type." );
741         return VLC_EGENERIC;
742     }
743 
744     psz_string = var_GetString( p_parent, psz_filter_type );
745 
746     /* Todo : Use some generic chain manipulation functions */
747     if( !psz_string ) psz_string = strdup("");
748 
749     psz_parser = strstr( psz_string, psz_name );
750     if( b_add )
751     {
752         if( !psz_parser )
753         {
754             psz_parser = psz_string;
755             if( asprintf( &psz_string, (*psz_string) ? "%s:%s" : "%s%s",
756                           psz_string, psz_name ) == -1 )
757             {
758                 free( psz_parser );
759                 return VLC_EGENERIC;
760             }
761             free( psz_parser );
762         }
763         else
764         {
765             free( psz_string );
766             return VLC_EGENERIC;
767         }
768     }
769     else
770     {
771         if( psz_parser )
772         {
773             memmove( psz_parser, psz_parser + strlen(psz_name) +
774                             (*(psz_parser + strlen(psz_name)) == ':' ? 1 : 0 ),
775                             strlen(psz_parser + strlen(psz_name)) + 1 );
776 
777             /* Remove trailing : : */
778             if( *(psz_string+strlen(psz_string ) -1 ) == ':' )
779                 *(psz_string+strlen(psz_string ) -1 ) = '\0';
780         }
781         else
782         {
783             free( psz_string );
784             return VLC_EGENERIC;
785         }
786     }
787 
788     *ppsz_filter_type = psz_filter_type;
789     *ppsz_filter_value = psz_string;
790     return VLC_SUCCESS;
791 }
792 
find_sub_source_by_name(libvlc_media_player_t * p_mi,const char * restrict name)793 static bool find_sub_source_by_name( libvlc_media_player_t *p_mi, const char *restrict name )
794 {
795     vout_thread_t *vout = GetVout( p_mi, 0 );
796     if (!vout)
797         return false;
798 
799     char *psz_sources = var_GetString( vout, "sub-source" );
800     if( !psz_sources )
801     {
802         libvlc_printerr( "%s not enabled", name );
803         vlc_object_release( vout );
804         return false;
805     }
806 
807     /* Find 'name'  */
808     char *p = strstr( psz_sources, name );
809     free( psz_sources );
810     vlc_object_release( vout );
811     return (p != NULL);
812 }
813 
814 typedef const struct {
815     const char name[20];
816     unsigned type;
817 } opt_t;
818 
819 static void
set_value(libvlc_media_player_t * p_mi,const char * restrict name,const opt_t * restrict opt,unsigned i_expected_type,const vlc_value_t * val,bool b_sub_source)820 set_value( libvlc_media_player_t *p_mi, const char *restrict name,
821            const opt_t *restrict opt, unsigned i_expected_type,
822            const vlc_value_t *val, bool b_sub_source )
823 {
824     if( !opt ) return;
825 
826     int i_type = opt->type;
827     vlc_value_t new_val = *val;
828     const char *psz_opt_name = opt->name;
829     switch( i_type )
830     {
831         case 0: /* the enabler */
832         {
833             int i_ret = get_filter_str( VLC_OBJECT( p_mi ), opt->name, val->i_int,
834                                         &psz_opt_name, &new_val.psz_string );
835             if( i_ret != VLC_SUCCESS )
836                 return;
837             i_type = VLC_VAR_STRING;
838             break;
839         }
840         case VLC_VAR_INTEGER:
841         case VLC_VAR_FLOAT:
842         case VLC_VAR_STRING:
843             if( i_expected_type != opt->type )
844             {
845                 libvlc_printerr( "Invalid argument to %s", name );
846                 return;
847             }
848             break;
849         default:
850             libvlc_printerr( "Invalid argument to %s", name );
851             return;
852     }
853 
854     /* Set the new value to the media player. Next vouts created from this
855      * media player will inherit this new value */
856     var_SetChecked( p_mi, psz_opt_name, i_type, new_val );
857 
858     /* Set the new value to every loaded vouts */
859     size_t i_vout_count;
860     vout_thread_t **pp_vouts = GetVouts( p_mi, &i_vout_count );
861     for( size_t i = 0; i < i_vout_count; ++i )
862     {
863         var_SetChecked( pp_vouts[i], psz_opt_name, i_type, new_val );
864         if( b_sub_source )
865             var_TriggerCallback( pp_vouts[i], "sub-source" );
866         vlc_object_release( pp_vouts[i] );
867     }
868 
869     if( opt->type == 0 )
870         free( new_val.psz_string );
871 }
872 
873 static int
get_int(libvlc_media_player_t * p_mi,const char * restrict name,const opt_t * restrict opt)874 get_int( libvlc_media_player_t *p_mi, const char *restrict name,
875          const opt_t *restrict opt )
876 {
877     if( !opt ) return 0;
878 
879     switch( opt->type )
880     {
881         case 0: /* the enabler */
882         {
883             bool b_enabled = find_sub_source_by_name( p_mi, name );
884             return b_enabled ? 1 : 0;
885         }
886     case VLC_VAR_INTEGER:
887         return var_GetInteger(p_mi, opt->name);
888     case VLC_VAR_FLOAT:
889         return lroundf(var_GetFloat(p_mi, opt->name));
890     default:
891         libvlc_printerr( "Invalid argument to %s in %s", name, "get int" );
892         return 0;
893     }
894 }
895 
896 static float
get_float(libvlc_media_player_t * p_mi,const char * restrict name,const opt_t * restrict opt)897 get_float( libvlc_media_player_t *p_mi, const char *restrict name,
898             const opt_t *restrict opt )
899 {
900     if( !opt ) return 0.0;
901 
902     if( opt->type != VLC_VAR_FLOAT )
903     {
904         libvlc_printerr( "Invalid argument to %s in %s", name, "get float" );
905         return 0.0;
906     }
907 
908     return var_GetFloat( p_mi, opt->name );
909 }
910 
911 static char *
get_string(libvlc_media_player_t * p_mi,const char * restrict name,const opt_t * restrict opt)912 get_string( libvlc_media_player_t *p_mi, const char *restrict name,
913             const opt_t *restrict opt )
914 {
915     if( !opt ) return NULL;
916 
917     if( opt->type != VLC_VAR_STRING )
918     {
919         libvlc_printerr( "Invalid argument to %s in %s", name, "get string" );
920         return NULL;
921     }
922 
923     return var_GetString( p_mi, opt->name );
924 }
925 
926 static const opt_t *
marq_option_bynumber(unsigned option)927 marq_option_bynumber(unsigned option)
928 {
929     static const opt_t optlist[] =
930     {
931         { "marq",          0 },
932         { "marq-marquee",  VLC_VAR_STRING },
933         { "marq-color",    VLC_VAR_INTEGER },
934         { "marq-opacity",  VLC_VAR_INTEGER },
935         { "marq-position", VLC_VAR_INTEGER },
936         { "marq-refresh",  VLC_VAR_INTEGER },
937         { "marq-size",     VLC_VAR_INTEGER },
938         { "marq-timeout",  VLC_VAR_INTEGER },
939         { "marq-x",        VLC_VAR_INTEGER },
940         { "marq-y",        VLC_VAR_INTEGER },
941     };
942     enum { num_opts = sizeof(optlist) / sizeof(*optlist) };
943 
944     const opt_t *r = option < num_opts ? optlist+option : NULL;
945     if( !r )
946         libvlc_printerr( "Unknown marquee option" );
947     return r;
948 }
949 
950 /*****************************************************************************
951  * libvlc_video_get_marquee_int : get a marq option value
952  *****************************************************************************/
libvlc_video_get_marquee_int(libvlc_media_player_t * p_mi,unsigned option)953 int libvlc_video_get_marquee_int( libvlc_media_player_t *p_mi,
954                                   unsigned option )
955 {
956     return get_int( p_mi, "marq", marq_option_bynumber(option) );
957 }
958 
959 /*****************************************************************************
960  * libvlc_video_get_marquee_string : get a marq option value
961  *****************************************************************************/
libvlc_video_get_marquee_string(libvlc_media_player_t * p_mi,unsigned option)962 char * libvlc_video_get_marquee_string( libvlc_media_player_t *p_mi,
963                                         unsigned option )
964 {
965     return get_string( p_mi, "marq", marq_option_bynumber(option) );
966 }
967 
968 /*****************************************************************************
969  * libvlc_video_set_marquee_int: enable, disable or set an int option
970  *****************************************************************************/
libvlc_video_set_marquee_int(libvlc_media_player_t * p_mi,unsigned option,int value)971 void libvlc_video_set_marquee_int( libvlc_media_player_t *p_mi,
972                          unsigned option, int value )
973 {
974     set_value( p_mi, "marq", marq_option_bynumber(option), VLC_VAR_INTEGER,
975                &(vlc_value_t) { .i_int = value }, true );
976 }
977 
978 /*****************************************************************************
979  * libvlc_video_set_marquee_string: set a string option
980  *****************************************************************************/
libvlc_video_set_marquee_string(libvlc_media_player_t * p_mi,unsigned option,const char * value)981 void libvlc_video_set_marquee_string( libvlc_media_player_t *p_mi,
982                 unsigned option, const char * value )
983 {
984     set_value( p_mi, "marq", marq_option_bynumber(option), VLC_VAR_STRING,
985                &(vlc_value_t){ .psz_string = (char *)value }, true );
986 }
987 
988 
989 /* logo module support */
990 
991 static const opt_t *
logo_option_bynumber(unsigned option)992 logo_option_bynumber( unsigned option )
993 {
994     static const opt_t vlogo_optlist[] =
995     /* depends on libvlc_video_logo_option_t */
996     {
997         { "logo",          0 },
998         { "logo-file",     VLC_VAR_STRING },
999         { "logo-x",        VLC_VAR_INTEGER },
1000         { "logo-y",        VLC_VAR_INTEGER },
1001         { "logo-delay",    VLC_VAR_INTEGER },
1002         { "logo-repeat",   VLC_VAR_INTEGER },
1003         { "logo-opacity",  VLC_VAR_INTEGER },
1004         { "logo-position", VLC_VAR_INTEGER },
1005     };
1006     enum { num_vlogo_opts = sizeof(vlogo_optlist) / sizeof(*vlogo_optlist) };
1007 
1008     const opt_t *r = option < num_vlogo_opts ? vlogo_optlist+option : NULL;
1009     if( !r )
1010         libvlc_printerr( "Unknown logo option" );
1011     return r;
1012 }
1013 
libvlc_video_set_logo_string(libvlc_media_player_t * p_mi,unsigned option,const char * psz_value)1014 void libvlc_video_set_logo_string( libvlc_media_player_t *p_mi,
1015                                    unsigned option, const char *psz_value )
1016 {
1017     set_value( p_mi,"logo",logo_option_bynumber(option), VLC_VAR_STRING,
1018                &(vlc_value_t){ .psz_string = (char *)psz_value }, true );
1019 }
1020 
1021 
libvlc_video_set_logo_int(libvlc_media_player_t * p_mi,unsigned option,int value)1022 void libvlc_video_set_logo_int( libvlc_media_player_t *p_mi,
1023                                 unsigned option, int value )
1024 {
1025     set_value( p_mi, "logo", logo_option_bynumber(option), VLC_VAR_INTEGER,
1026                &(vlc_value_t) { .i_int = value }, true );
1027 }
1028 
1029 
libvlc_video_get_logo_int(libvlc_media_player_t * p_mi,unsigned option)1030 int libvlc_video_get_logo_int( libvlc_media_player_t *p_mi,
1031                                unsigned option )
1032 {
1033     return get_int( p_mi, "logo", logo_option_bynumber(option) );
1034 }
1035 
1036 
1037 /* adjust module support */
1038 
1039 
1040 static const opt_t *
adjust_option_bynumber(unsigned option)1041 adjust_option_bynumber( unsigned option )
1042 {
1043     static const opt_t optlist[] =
1044     {
1045         { "adjust",     0 },
1046         { "contrast",   VLC_VAR_FLOAT },
1047         { "brightness", VLC_VAR_FLOAT },
1048         { "hue",        VLC_VAR_FLOAT },
1049         { "saturation", VLC_VAR_FLOAT },
1050         { "gamma",      VLC_VAR_FLOAT },
1051     };
1052     enum { num_opts = sizeof(optlist) / sizeof(*optlist) };
1053 
1054     const opt_t *r = option < num_opts ? optlist+option : NULL;
1055     if( !r )
1056         libvlc_printerr( "Unknown adjust option" );
1057     return r;
1058 }
1059 
1060 
libvlc_video_set_adjust_int(libvlc_media_player_t * p_mi,unsigned option,int value)1061 void libvlc_video_set_adjust_int( libvlc_media_player_t *p_mi,
1062                                   unsigned option, int value )
1063 {
1064     set_value( p_mi, "adjust", adjust_option_bynumber(option), VLC_VAR_INTEGER,
1065                &(vlc_value_t) { .i_int = value }, false );
1066 }
1067 
1068 
libvlc_video_get_adjust_int(libvlc_media_player_t * p_mi,unsigned option)1069 int libvlc_video_get_adjust_int( libvlc_media_player_t *p_mi,
1070                                  unsigned option )
1071 {
1072     return get_int( p_mi, "adjust", adjust_option_bynumber(option) );
1073 }
1074 
1075 
libvlc_video_set_adjust_float(libvlc_media_player_t * p_mi,unsigned option,float value)1076 void libvlc_video_set_adjust_float( libvlc_media_player_t *p_mi,
1077                                     unsigned option, float value )
1078 {
1079     set_value( p_mi, "adjust", adjust_option_bynumber(option), VLC_VAR_FLOAT,
1080                &(vlc_value_t) { .f_float = value }, false );
1081 }
1082 
1083 
libvlc_video_get_adjust_float(libvlc_media_player_t * p_mi,unsigned option)1084 float libvlc_video_get_adjust_float( libvlc_media_player_t *p_mi,
1085                                      unsigned option )
1086 {
1087     return get_float( p_mi, "adjust", adjust_option_bynumber(option) );
1088 }
1089