[vlc-devel] [PATCH 03/15] core: add viewpoint to change the yaw/pitch/roll view during playback

Steve Lhomme robux4 at gmail.com
Tue Nov 8 11:22:49 CET 2016


On Tue, Nov 8, 2016 at 10:32 AM, Steve Lhomme <robux4 at gmail.com> wrote:
> On Mon, Nov 7, 2016 at 6:30 PM, Rémi Denis-Courmont <remi at remlab.net> wrote:
>> Le maanantaina 7. marraskuuta 2016, 10.51.52 EET Steve Lhomme a écrit :
>>> --
>>> * move the playlist viewpoint in playlist_private_t
>>> * less vlc_vout.h includes
>>> * removed text on the "viewpoint" variable
>>>
>>> replaces https://patches.videolan.org/patch/14923/
>>> * forgot to remove playlist entries from libvlccore.sym
>>>
>>> replaces https://patches.videolan.org/patch/14923/
>>> * use a different CLI parameter for each viewpoint parameter
>>> * drop the f_ prefix on vout float variables
>>> ---
>>>  include/vlc_common.h             |  1 +
>>>  include/vlc_vout.h               |  9 ++++++++
>>>  include/vlc_vout_display.h       |  7 ++++++
>>>  include/vlc_vout_wrapper.h       |  2 ++
>>>  src/libvlc-module.c              | 25 ++++++++++++++++++++
>>>  src/libvlccore.sym               |  2 ++
>>>  src/playlist/engine.c            |  8 +++++++
>>>  src/playlist/playlist_internal.h |  3 +++
>>>  src/video_output/control.h       |  2 ++
>>>  src/video_output/display.c       | 49
>>> +++++++++++++++++++++++++++++++++++++++- src/video_output/video_output.c  |
>>> 25 ++++++++++++++++++++
>>>  src/video_output/vout_intf.c     | 16 +++++++++++++
>>>  12 files changed, 148 insertions(+), 1 deletion(-)
>>>
>>> diff --git a/include/vlc_common.h b/include/vlc_common.h
>>> index a9683d6..0b1db73 100644
>>> --- a/include/vlc_common.h
>>> +++ b/include/vlc_common.h
>>> @@ -249,6 +249,7 @@ typedef audio_format_t audio_sample_format_t;
>>>
>>>  /* Video */
>>>  typedef struct vout_thread_t vout_thread_t;
>>> +typedef struct vlc_viewpoint_t vlc_viewpoint_t;
>>>
>>>  typedef video_format_t video_frame_format_t;
>>>  typedef struct picture_t picture_t;
>>> diff --git a/include/vlc_vout.h b/include/vlc_vout.h
>>> index 600cb56..57afacc 100644
>>> --- a/include/vlc_vout.h
>>> +++ b/include/vlc_vout.h
>>> @@ -82,6 +82,12 @@ struct vout_thread_t {
>>>  #define VOUT_ALIGN_BOTTOM       0x0008
>>>  #define VOUT_ALIGN_VMASK        0x000C
>>>
>>> +struct vlc_viewpoint_t {
>>> +    float yaw;   /* yaw in radians */
>>> +    float pitch; /* pitch in radians */
>>> +    float roll;  /* roll in radians */
>>> +};
>>> +
>>>  /**************************************************************************
>>> *** * Prototypes
>>>
>>> ***************************************************************************
>>> **/ @@ -156,6 +162,9 @@ VLC_API void vout_FlushSubpictureChannel(
>>> vout_thread_t *, int );
>>>
>>>  VLC_API void vout_EnableFilter( vout_thread_t *, const char *,bool , bool
>>> );
>>>
>>> +VLC_API void vout_GetViewpoint( vout_thread_t *, vlc_viewpoint_t
>>> *p_viewpoint ); +VLC_API void vout_SetViewpoint( vout_thread_t *, const
>>> vlc_viewpoint_t *p_viewpoint ); +
>>>  /**@}*/
>>>
>>>  #endif /* _VLC_VIDEO_H */
>>> diff --git a/include/vlc_vout_display.h b/include/vlc_vout_display.h
>>> index 817f770..e1efe00 100644
>>> --- a/include/vlc_vout_display.h
>>> +++ b/include/vlc_vout_display.h
>>> @@ -30,6 +30,7 @@
>>>  #include <vlc_subpicture.h>
>>>  #include <vlc_keys.h>
>>>  #include <vlc_mouse.h>
>>> +#include <vlc_vout.h>
>>>  #include <vlc_vout_window.h>
>>>
>>>  /**
>>> @@ -115,6 +116,8 @@ typedef struct {
>>>          int den;
>>>      } zoom;
>>>
>>> +    vlc_viewpoint_t viewpoint;
>>> +
>>>  } vout_display_cfg_t;
>>>
>>>  /**
>>> @@ -175,6 +178,10 @@ enum {
>>>       * The cropping requested is stored by video_format_t::i_x/y_offset and
>>> * video_format_t::i_visible_width/height */
>>>      VOUT_DISPLAY_CHANGE_SOURCE_CROP,   /* const video_format_t *p_source */
>>> +
>>> +    /* Ask the module to acknowledge/refuse VR/360° viewing direction after
>>> +     * being requested externally */
>>> +    VOUT_DISPLAY_CHANGE_VIEWPOINT,   /* const vout_display_cfg_t *p_cfg */
>>>  };
>>>
>>>  /**
>>> diff --git a/include/vlc_vout_wrapper.h b/include/vlc_vout_wrapper.h
>>> index 419bfc8..2ba4fc6 100644
>>> --- a/include/vlc_vout_wrapper.h
>>> +++ b/include/vlc_vout_wrapper.h
>>> @@ -93,6 +93,8 @@ void vout_SetDisplayZoom(vout_display_t *, unsigned num,
>>> unsigned den); void vout_SetDisplayAspect(vout_display_t *, unsigned num,
>>> unsigned den); void vout_SetDisplayCrop(vout_display_t *, unsigned num,
>>> unsigned den, unsigned left, unsigned top, int right, int bottom); +void
>>> vout_GetDisplayViewpoint(vout_display_t *, vlc_viewpoint_t *p_viewpoint);
>>> +void vout_SetDisplayViewpoint(vout_display_t *, const vlc_viewpoint_t
>>> *p_viewpoint);
>>>
>>>  #endif /* VLC_VOUT_WRAPPER_H */
>>>
>>> diff --git a/src/libvlc-module.c b/src/libvlc-module.c
>>> index 035bcd2..05b171d 100644
>>> --- a/src/libvlc-module.c
>>> +++ b/src/libvlc-module.c
>>> @@ -433,6 +433,22 @@ static const char *const ppsz_pos_descriptions[] =
>>>      "aspect, or a float value (1.25, 1.3333, etc.) expressing pixel " \
>>>      "squareness.")
>>>
>>> +#define VIEWPOINT_YAW_TEXT N_("Viewpoint Yaw")
>>> +#define VIEWPOINT_YAW_LONGTEXT N_( \
>>> +    "This forces the user viewpoint yaw (horizontal shift) for the
>>> displayed " \ +    "video, in radians." )
>>> +
>>> +#define VIEWPOINT_PITCH_TEXT N_("Viewpoint Pitch")
>>> +#define VIEWPOINT_PITCH_LONGTEXT N_( \
>>> +    "This forces the user viewpoint pitch (vertical shift) for the
>>> displayed " \ +    "video, in radians." )
>>> +
>>> +#define VIEWPOINT_ROLL_TEXT N_("Viewpoint Roll")
>>> +#define VIEWPOINT_ROLL_LONGTEXT N_( \
>>> +    "This forces the user viewpoint roll (head tilting) for the displayed "
>>> \ +    "video, in radians." )
>>> +
>>> +
>>>  #define AUTOSCALE_TEXT N_("Video Auto Scaling")
>>>  #define AUTOSCALE_LONGTEXT N_( \
>>>      "Let the video scale to fit a given window or fullscreen.")
>>> @@ -1589,6 +1605,15 @@ vlc_module_begin ()
>>>      add_string( "aspect-ratio", NULL,
>>>                  ASPECT_RATIO_TEXT, ASPECT_RATIO_LONGTEXT, false )
>>>          change_safe ()
>>> +    add_float( "viewpoint-yaw", 0.,
>>> +                VIEWPOINT_YAW_TEXT, VIEWPOINT_YAW_LONGTEXT, false )
>>> +        change_safe ()
>>> +    add_float( "viewpoint-pitch", 0.,
>>> +                VIEWPOINT_PITCH_TEXT, VIEWPOINT_PITCH_LONGTEXT, false )
>>> +        change_safe ()
>>> +    add_float( "viewpoint-roll", 0.,
>>> +                VIEWPOINT_ROLL_TEXT, VIEWPOINT_ROLL_LONGTEXT, false )
>>> +        change_safe ()
>>>      add_bool( "autoscale", true, AUTOSCALE_TEXT, AUTOSCALE_LONGTEXT, false
>>> ) change_safe ()
>>>      add_obsolete_float( "scale" ) /* since 3.0.0 */
>>> diff --git a/src/libvlccore.sym b/src/libvlccore.sym
>>> index 8bb2dac..59a6e56 100644
>>> --- a/src/libvlccore.sym
>>> +++ b/src/libvlccore.sym
>>> @@ -734,6 +734,8 @@ vout_window_Delete
>>>  vout_display_GetDefaultDisplaySize
>>>  vout_display_PlacePicture
>>>  vout_display_SendMouseMovedDisplayCoordinates
>>> +vout_GetViewpoint
>>> +vout_SetViewpoint
>>>  xml_Create
>>>  text_style_Copy
>>>  text_style_Create
>>> diff --git a/src/playlist/engine.c b/src/playlist/engine.c
>>> index 7c00a48..e840e9c 100644
>>> --- a/src/playlist/engine.c
>>> +++ b/src/playlist/engine.c
>>> @@ -479,6 +479,14 @@ static void VariablesInit( playlist_t *p_playlist )
>>>      var_Create( p_playlist, "mute", VLC_VAR_BOOL );
>>>      var_Create( p_playlist, "volume", VLC_VAR_FLOAT );
>>>      var_SetFloat( p_playlist, "volume", -1.f );
>>> +
>>> +    vlc_viewpoint_t *p_vp = &pl_priv(p_playlist)->viewpoint;
>>> +    p_vp->yaw   = var_InheritFloat( p_playlist, "viewpoint-yaw" );
>>> +    p_vp->pitch = var_InheritFloat( p_playlist, "viewpoint-pitch" );
>>> +    p_vp->roll  = var_InheritFloat( p_playlist, "viewpoint-roll" );
>>> +
>>> +    var_Create( p_playlist, "viewpoint", VLC_VAR_ADDRESS );
>>> +    var_SetAddress( p_playlist, "viewpoint", p_vp );
>>>  }
>>>
>>>  playlist_item_t * playlist_CurrentPlayingItem( playlist_t * p_playlist )
>>> diff --git a/src/playlist/playlist_internal.h
>>> b/src/playlist/playlist_internal.h index d655dc3..faa7a14 100644
>>> --- a/src/playlist/playlist_internal.h
>>> +++ b/src/playlist/playlist_internal.h
>>> @@ -35,6 +35,7 @@
>>>   */
>>>
>>>  #include "input/input_interface.h"
>>> +#include "vlc_vout.h"
>>>  #include <assert.h>
>>>
>>>  #include "art.h"
>>> @@ -87,6 +88,8 @@ typedef struct playlist_private_t
>>>
>>>      bool     b_tree; /**< Display as a tree */
>>>      bool     b_preparse; /**< Preparse items */
>>> +
>>> +    vlc_viewpoint_t  viewpoint; /**< playback viewpoint */
>>>  } playlist_private_t;
>>>
>>>  #define pl_priv( pl ) ((playlist_private_t *)(pl))
>>> diff --git a/src/video_output/control.h b/src/video_output/control.h
>>> index eff9aa1..add2246 100644
>>> --- a/src/video_output/control.h
>>> +++ b/src/video_output/control.h
>>> @@ -58,6 +58,7 @@ enum {
>>>      VOUT_CONTROL_CROP_BORDER,           /* border */
>>>      VOUT_CONTROL_CROP_RATIO,            /* pair */
>>>      VOUT_CONTROL_CROP_WINDOW,           /* window */
>>> +    VOUT_CONTROL_VIEWPOINT,             /* viewpoint */
>>>  };
>>>
>>>  typedef struct {
>>> @@ -93,6 +94,7 @@ typedef struct {
>>>              unsigned width;
>>>              unsigned height;
>>>          } window;
>>> +        vlc_viewpoint_t viewpoint;
>>>          const vout_configuration_t *cfg;
>>>          subpicture_t *subpicture;
>>>      } u;
>>> diff --git a/src/video_output/display.c b/src/video_output/display.c
>>> index ecf661a..4716755 100644
>>> --- a/src/video_output/display.c
>>> +++ b/src/video_output/display.c
>>> @@ -373,6 +373,10 @@ struct vout_display_owner_sys_t {
>>>          unsigned den;
>>>      } crop;
>>>
>>> +    bool ch_viewpoint;
>>> +    vlc_viewpoint_t viewpoint;
>>> +    vlc_mutex_t viewpoint_lock;
>>> +
>>>      /* */
>>>      video_format_t source;
>>>      filter_chain_t *filters;
>>> @@ -850,7 +854,8 @@ bool vout_ManageDisplay(vout_display_t *vd, bool
>>> allow_reset_pictures) !ch_wm_state &&
>>>  #endif
>>>              !osys->ch_sar &&
>>> -            !osys->ch_crop) {
>>> +            !osys->ch_crop &&
>>> +            !osys->ch_viewpoint) {
>>>
>>>              if (!osys->cfg.is_fullscreen && osys->fit_window != 0) {
>>>                  VoutDisplayFitWindow(vd, osys->fit_window == -1);
>>> @@ -1034,6 +1039,20 @@ bool vout_ManageDisplay(vout_display_t *vd, bool
>>> allow_reset_pictures) osys->crop.den    = crop_den;
>>>              osys->ch_crop = false;
>>>          }
>>> +        if (osys->ch_viewpoint) {
>>> +            vout_display_cfg_t cfg = osys->cfg;
>>> +
>>> +            vlc_mutex_lock(&osys->viewpoint_lock);
>>> +            cfg.viewpoint = osys->viewpoint;
>>> +
>>> +            if (vout_display_Control(vd, VOUT_DISPLAY_CHANGE_VIEWPOINT,
>>> &cfg)) { +                msg_Err(vd, "Failed to change Viewpoint");
>>> +                osys->viewpoint = cfg.viewpoint;
>>> +            }
>>> +            osys->cfg.viewpoint = osys->viewpoint;
>>> +            osys->ch_viewpoint  = false;
>>> +            vlc_mutex_unlock(&osys->viewpoint_lock);
>>> +        }
>>>
>>>          /* */
>>>          if (reset_pictures) {
>>> @@ -1193,6 +1212,30 @@ void vout_SetDisplayCrop(vout_display_t *vd,
>>>      }
>>>  }
>>>
>>> +void vout_GetDisplayViewpoint(vout_display_t *vd, vlc_viewpoint_t
>>> *p_viewpoint) +{
>>> +    vout_display_owner_sys_t *osys = vd->owner.sys;
>>> +
>>> +    vlc_mutex_lock(&osys->viewpoint_lock);
>>> +    *p_viewpoint = osys->viewpoint;
>>> +    vlc_mutex_unlock(&osys->viewpoint_lock);
>>> +}
>>
>> I believe that there is a reason why there are no getters in this file: they
>> simply make no sense. By the time the function returns, the mutex is already
>> unlocked, and the obtained value is already meaningless.
>
> We need a way to get the current viewpoint to modify it later. Another
> option would be to have no getter/setter and just the increments we
> want compared to the previous call.

One big drawback of that approach is that the "controller" would need
to send the changes since its last increment, not based on the
original reference point. And incrementing/decrementing floating point
values many times will lead to rounding issues. When you come back at
your original physical point you may not point exactly in the same
place. And it will get worse the longer you use this system.

With getter/setter you always use the same reference so there's no
major rounding issues.

We may switch to integers to store the viewpoint values, at least in
libvlc to avoid this issue.

> The viewpoint controllers would probably work by using a reference
> (current mouse position for instance) and send differences when it
> changes to affect the viewpoint. It becomes tricky when different
> controllers work on the same viewpoint. For example you can have the
> gyroscope handling the position in space and the mouse/keyboard handle
> the zoom/field of view. It would still work if they all modify
> different fields. If they modify the same field it's a mess. But then
> moving the device and the mouse and except it to both affect the
> position would be a bad user experience anyway.
>
> What we want to avoid is to have the (current) 3 fields modified
> separately, that would result in weird "scale" effect during movement.
>
> Opinions ?
>
>> Please, no.
>>
>>> +
>>> +void vout_SetDisplayViewpoint(vout_display_t *vd, const vlc_viewpoint_t
>>> *p_viewpoint) +{
>>> +    vout_display_owner_sys_t *osys = vd->owner.sys;
>>> +
>>> +    vlc_mutex_lock(&osys->viewpoint_lock);
>>> +    if (osys->viewpoint.yaw   != p_viewpoint->yaw ||
>>> +        osys->viewpoint.pitch != p_viewpoint->pitch ||
>>> +        osys->viewpoint.roll  != p_viewpoint->roll) {
>>> +        osys->viewpoint = *p_viewpoint;
>>> +
>>> +        osys->ch_viewpoint = true;
>>> +    }
>>> +    vlc_mutex_unlock(&osys->viewpoint_lock);
>>> +}
>>> +
>>>  static vout_display_t *DisplayNew(vout_thread_t *vout,
>>>                                    const video_format_t *source,
>>>                                    const vout_display_state_t *state,
>>> @@ -1218,6 +1261,8 @@ static vout_display_t *DisplayNew(vout_thread_t *vout,
>>>
>>>      vlc_mutex_init(&osys->lock);
>>>
>>> +    vlc_mutex_init(&osys->viewpoint_lock);
>>> +
>>>      vlc_mouse_Init(&osys->mouse.state);
>>>      osys->mouse.last_moved = mdate();
>>>      osys->mouse.double_click_timeout = double_click_timeout;
>>> @@ -1289,6 +1334,7 @@ static vout_display_t *DisplayNew(vout_thread_t *vout,
>>>
>>>      return p_display;
>>>  error:
>>> +    vlc_mutex_destroy(&osys->viewpoint_lock);
>>>      vlc_mutex_destroy(&osys->lock);
>>>      free(osys);
>>>      return NULL;
>>> @@ -1317,6 +1363,7 @@ void vout_DeleteDisplay(vout_display_t *vd,
>>> vout_display_state_t *state) vlc_join(osys->event.thread, NULL);
>>>          block_FifoRelease(osys->event.fifo);
>>>      }
>>> +    vlc_mutex_destroy(&osys->viewpoint_lock);
>>>      vlc_mutex_destroy(&osys->lock);
>>>      free(osys);
>>>  }
>>> diff --git a/src/video_output/video_output.c
>>> b/src/video_output/video_output.c index 7994bec..9a97767 100644
>>> --- a/src/video_output/video_output.c
>>> +++ b/src/video_output/video_output.c
>>> @@ -540,12 +540,29 @@ void vout_ControlChangeSubMargin(vout_thread_t *vout,
>>> int margin) margin);
>>>  }
>>>
>>> +void vout_GetViewpoint( vout_thread_t *vout, vlc_viewpoint_t *p_viewpoint )
>>> +{
>>> +    vout_GetDisplayViewpoint(vout->p->display.vd, p_viewpoint);
>>> +}
>>> +
>>> +void vout_SetViewpoint(vout_thread_t *vout, const vlc_viewpoint_t
>>> *p_viewpoint) +{
>>> +    vout_control_cmd_t cmd;
>>> +    vout_control_cmd_Init(&cmd, VOUT_CONTROL_VIEWPOINT);
>>> +    cmd.u.viewpoint = *p_viewpoint;
>>> +
>>> +    vout_control_Push(&vout->p->control, &cmd);
>>> +}
>>> +
>>>  /* */
>>>  static void VoutGetDisplayCfg(vout_thread_t *vout, vout_display_cfg_t *cfg,
>>> const char *title) {
>>>      /* Load configuration */
>>>      cfg->is_fullscreen = var_CreateGetBool(vout, "fullscreen")
>>>
>>>                           || var_InheritBool(vout, "video-wallpaper");
>>>
>>> +    cfg->viewpoint.yaw   = 0.0f;
>>> +    cfg->viewpoint.pitch = 0.0f;
>>> +    cfg->viewpoint.roll  = 0.0f;
>>>      cfg->display.title = title;
>>>      const int display_width = var_CreateGetInteger(vout, "width");
>>>      const int display_height = var_CreateGetInteger(vout, "height");
>>> @@ -1289,6 +1306,11 @@ static void ThreadExecuteCropRatio(vout_thread_t
>>> *vout, 0, 0, 0, 0);
>>>  }
>>>
>>> +static void ThreadExecuteViewpoint(vout_thread_t *vout, const
>>> vlc_viewpoint_t *p_viewpoint) +{
>>> +    vout_SetDisplayViewpoint(vout->p->display.vd, p_viewpoint);
>>> +}
>>> +
>>>  static int ThreadStart(vout_thread_t *vout, vout_display_state_t *state)
>>>  {
>>>      vlc_mouse_Init(&vout->p->mouse);
>>> @@ -1548,6 +1570,9 @@ static int ThreadControl(vout_thread_t *vout,
>>> vout_control_cmd_t cmd) cmd.u.border.left,  cmd.u.border.top,
>>>                  cmd.u.border.right, cmd.u.border.bottom);
>>>          break;
>>> +    case VOUT_CONTROL_VIEWPOINT:
>>> +        ThreadExecuteViewpoint(vout, &cmd.u.viewpoint);
>>> +        break;
>>>      default:
>>>          break;
>>>      }
>>> diff --git a/src/video_output/vout_intf.c b/src/video_output/vout_intf.c
>>> index 11240c8..fe6c36b 100644
>>> --- a/src/video_output/vout_intf.c
>>> +++ b/src/video_output/vout_intf.c
>>> @@ -57,6 +57,8 @@ static int AutoScaleCallback( vlc_object_t *, char const
>>> *, vlc_value_t, vlc_value_t, void * ); static int ZoomCallback(
>>> vlc_object_t *, char const *,
>>>                           vlc_value_t, vlc_value_t, void * );
>>> +static int ViewpointCallback( vlc_object_t *, char const *,
>>> +                              vlc_value_t, vlc_value_t, void * );
>>>  static int AboveCallback( vlc_object_t *, char const *,
>>>                            vlc_value_t, vlc_value_t, void * );
>>>  static int WallPaperCallback( vlc_object_t *, char const *,
>>> @@ -252,6 +254,10 @@ void vout_IntfInit( vout_thread_t *p_vout )
>>>      var_Change( p_vout, "video-on-top", VLC_VAR_SETTEXT, &text, NULL );
>>>      var_AddCallback( p_vout, "video-on-top", AboveCallback, NULL );
>>>
>>> +    /* Add a variable to indicate if the viewpoint to use to display the
>>> video */ +    var_Create( p_vout, "viewpoint", VLC_VAR_ADDRESS |
>>> VLC_VAR_DOINHERIT ); +    var_AddCallback( p_vout, "viewpoint",
>>> ViewpointCallback, NULL ); +
>>>      /* Add a variable to indicate if the window should be below all others
>>> */ var_Create( p_vout, "video-wallpaper", VLC_VAR_BOOL | VLC_VAR_DOINHERIT
>>> ); var_AddCallback( p_vout, "video-wallpaper", WallPaperCallback, @@ -309,6
>>> +315,7 @@ void vout_IntfReinit( vout_thread_t *p_vout )
>>>      var_TriggerCallback( p_vout, "zoom" );
>>>      var_TriggerCallback( p_vout, "crop" );
>>>      var_TriggerCallback( p_vout, "aspect-ratio" );
>>> +    var_TriggerCallback( p_vout, "viewpoint" );
>>>
>>>      var_TriggerCallback( p_vout, "video-on-top" );
>>>      var_TriggerCallback( p_vout, "video-wallpaper" );
>>> @@ -602,6 +609,15 @@ static int ZoomCallback( vlc_object_t *obj, char const
>>> *name, return VLC_SUCCESS;
>>>  }
>>>
>>> +static int ViewpointCallback(vlc_object_t *obj, char const *cmd,
>>> +                             vlc_value_t oldval, vlc_value_t newval, void
>>> *data) +{
>>> +    vout_SetViewpoint((vout_thread_t *)obj, newval.p_address);
>>> +
>>> +    VLC_UNUSED(cmd); VLC_UNUSED(oldval); VLC_UNUSED(data);
>>> +    return VLC_SUCCESS;
>>> +}
>>> +
>>>  static int AboveCallback( vlc_object_t *obj, char const *name,
>>>                            vlc_value_t prev, vlc_value_t cur, void *data )
>>>  {
>>
>>
>> --
>> Rémi Denis-Courmont
>> http://www.remlab.net/
>>
>> _______________________________________________
>> vlc-devel mailing list
>> To unsubscribe or modify your subscription options:
>> https://mailman.videolan.org/listinfo/vlc-devel


More information about the vlc-devel mailing list