David pushed to branch 3.0.x at VideoLAN / VLC
Commits: c593d59c by Alexandre Janniaux at 2021-10-11T14:09:06+00:00 avcapture: setup framerate correctly Set the framerate to 1 / framerate_base, avoiding the following warning for a 30fps capture stream: rawvideo decoder warning: invalid frame rate 0/30000, using 25 fps instead Cherry-picked from commit 84a5179345c9ec0b2bb687db0d3ad7b0eb61664c. Signed-off-by: Alexandre Janniaux <aja...@videolabs.io> - - - - - 92188660 by Alexandre Janniaux at 2021-10-11T14:09:06+00:00 avcapture: fix typo Cherry-picked from commit 34111f324433e93d250c17129fc2d52bb6955b8e. Signed-off-by: Alexandre Janniaux <aja...@videolabs.io> - - - - - 8eee92c3 by Alexandre Janniaux at 2021-10-11T14:09:06+00:00 avcapture: remove trailing characters Cherry-picked from commit de1339120dbef5ff9eb1afd6df964424f4bbff43. Signed-off-by: Alexandre Janniaux <aja...@videolabs.io> - - - - - 8f60dd97 by Alexandre Janniaux at 2021-10-11T14:09:06+00:00 avcapture: improve the es_format_t exposed Cherry-picked from commit d105cfe42dda31d5403c01e71a44eae63b8b04a3. Signed-off-by: Alexandre Janniaux <aja...@videolabs.io> - - - - - 8f07b5cd by Alexandre Janniaux at 2021-10-11T14:09:06+00:00 avcapture: fix locking for pts Cherry-picked from commit 27489f99ccd8e56884d79359d30e7d499c415f03. Signed-off-by: Alexandre Janniaux <aja...@videolabs.io> - - - - - a71f62e7 by Alexandre Janniaux at 2021-10-11T14:09:06+00:00 avcapture: use video_format_Print ...instead of using custom debug strings Cherry-picked from commit 3e9507cd6b48f1d6a26e1710f56e6a6a856dbf80. Signed-off-by: Alexandre Janniaux <aja...@videolabs.io> - - - - - d9eb0075 by Alexandre Janniaux at 2021-10-11T14:09:06+00:00 avcapture: use a separate class for ARC ARC is not supported when storing NSObject in a structure. But it is well-supported when storing a NSObject as (__bridge_retained void*) into the p_sys until (__bridge_tranfer) releases it. It highly simplify the whole handling of ARC objects and avoid using CFRelease/CFRetain in the code, unifying with other Objective-C modules like VLCVideoUIView or VLCOpenGLES2VideoView. Cherry-picked from commit 3571e7ed3ad7df8aa62a3f360bed07bedb56bdda. Signed-off-by: Alexandre Janniaux <aja...@videolabs.io> - - - - - a4e07fb9 by Alexandre Janniaux at 2021-10-11T14:09:06+00:00 avcapture: signal missing protocol implemented AVCaptureVideoDataOutputSampleBufferDelegate is implemented by VLCAVDecompressedVideoOutput but wasn't signalled, leading to warnings when compiling. Cherry-picked from commit 2a3074cc36f708c0048e11347c9f337f017e1e47. Signed-off-by: Alexandre Janniaux <aja...@videolabs.io> - - - - - aa517507 by Alexandre Janniaux at 2021-10-11T14:09:06+00:00 avcapture: fix PTS conversion and usage CMTime is **not** equivalent to vlc_tick_t. In particular, the timescale doesn't match. So it should be converted before usage. In addition, GET_TIME is supposed to return where the access is currently reading, which should also match the timestamps of the output frames from the access. Fixes #26101 Cherry-picked from commit ca04b74fa6724c6d770cf277d97b1bfb4d18fe20. Signed-off-by: Alexandre Janniaux <aja...@videolabs.io> - - - - - 1 changed file: - modules/access/avcapture.m Changes: ===================================== modules/access/avcapture.m ===================================== @@ -72,11 +72,20 @@ vlc_module_begin () set_callbacks(Open, Close) vlc_module_end () +static mtime_t vlc_CMTime_to_mtime(CMTime timestamp) +{ + CMTime scaled = CMTimeConvertScale( + timestamp, CLOCK_FREQ, + kCMTimeRoundingMethod_Default); + + return 1 + scaled.value; +} /***************************************************************************** * AVFoundation Bridge *****************************************************************************/ -@interface VLCAVDecompressedVideoOutput : AVCaptureVideoDataOutput +@interface VLCAVDecompressedVideoOutput : + AVCaptureVideoDataOutput <AVCaptureVideoDataOutputSampleBufferDelegate> { demux_t *p_avcapture; @@ -157,7 +166,7 @@ vlc_module_end () self.videoDimensions = CMVideoFormatDescriptionGetDimensions(formatDescription); bytesPerRow = CVPixelBufferGetBytesPerRow(CMSampleBufferGetImageBuffer(sampleBuffer)); videoDimensionsReady = YES; - msg_Dbg(p_avcapture, "Dimensionns obtained height:%i width:%i bytesPerRow:%lu", [self height], [self width], bytesPerRow); + msg_Dbg(p_avcapture, "Dimensions obtained height:%i width:%i bytesPerRow:%lu", [self height], [self width], bytesPerRow); } } @@ -165,15 +174,14 @@ vlc_module_end () { mtime_t pts; - if ( !currentImageBuffer || currentPts == previousPts ) - return 0; - @synchronized (self) { + if ( !currentImageBuffer || currentPts == previousPts ) + return 0; pts = previousPts = currentPts; } - return currentPts; + return pts; } - (void)captureOutput:(AVCaptureOutput *)captureOutput @@ -187,13 +195,14 @@ didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer CVBufferRetain(videoFrame); [self getVideoDimensions:sampleBuffer]; + @synchronized (self) { imageBufferToRelease = currentImageBuffer; currentImageBuffer = videoFrame; - currentPts = (mtime_t)presentationtimestamp.value; + currentPts = vlc_CMTime_to_mtime(presentationtimestamp); timeScale = (long)presentationtimestamp.timescale; } - + CVBufferRelease(imageBufferToRelease); } } @@ -237,16 +246,21 @@ didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer * Struct *****************************************************************************/ -struct demux_sys_t -{ - CFTypeRef _Nullable session; // AVCaptureSession - CFTypeRef _Nullable device; // AVCaptureDevice - CFTypeRef _Nullable output; // VLCAVDecompressedVideoOutput - es_out_id_t *p_es_video; - es_format_t fmt; - int height, width; - BOOL b_es_setup; -}; +@interface VLCAVCaptureDemux : NSObject { + demux_t *_demux; + AVCaptureSession *_session; + AVCaptureDevice *_device; + VLCAVDecompressedVideoOutput *_output; + es_out_id_t *_es_video; + es_format_t _fmt; + int _height, _width; +} + +- (VLCAVCaptureDemux*)init:(demux_t *)demux; +- (int)demux; +- (mtime_t)pts; +- (void)dealloc; +@end /***************************************************************************** * Open: @@ -254,8 +268,89 @@ struct demux_sys_t static int Open(vlc_object_t *p_this) { demux_t *p_demux = (demux_t*)p_this; - demux_sys_t *p_sys = NULL; + /* Only when selected */ + if ( *p_demux->psz_access == '\0' ) + return VLC_EGENERIC; + + if (p_demux->out == NULL) + return VLC_EGENERIC; + + @autoreleasepool { + VLCAVCaptureDemux *demux = [[VLCAVCaptureDemux alloc] init:p_demux]; + if (demux == nil) + return VLC_EGENERIC; + p_demux->p_sys = (__bridge_retained void*)demux; + } + p_demux->pf_demux = Demux; + p_demux->pf_control = Control; + p_demux->info.i_update = 0; + p_demux->info.i_title = 0; + p_demux->info.i_seekpoint = 0; + + return VLC_SUCCESS; +} + +/***************************************************************************** +* Close: +*****************************************************************************/ +static void Close(vlc_object_t *p_this) +{ + demux_t *p_demux = (demux_t*)p_this; + VLCAVCaptureDemux *demux = + (__bridge_transfer VLCAVCaptureDemux*)p_demux->p_sys; + + /* Signal ARC we won't use those references anymore. */ + p_demux->p_sys = nil; + demux = nil; +} + +/***************************************************************************** +* Demux: +*****************************************************************************/ +static int Demux(demux_t *p_demux) +{ + VLCAVCaptureDemux *demux = (__bridge VLCAVCaptureDemux *)p_demux->p_sys; + return [demux demux]; +} + +/***************************************************************************** +* Control: +*****************************************************************************/ +static int Control(demux_t *p_demux, int i_query, va_list args) +{ + VLCAVCaptureDemux *demux = (__bridge VLCAVCaptureDemux *)p_demux->p_sys; + bool *pb; + + switch( i_query ) + { + /* Special for access_demux */ + case DEMUX_CAN_PAUSE: + case DEMUX_CAN_SEEK: + case DEMUX_SET_PAUSE_STATE: + case DEMUX_CAN_CONTROL_PACE: + pb = va_arg(args, bool *); + *pb = false; + return VLC_SUCCESS; + + case DEMUX_GET_PTS_DELAY: + *va_arg(args, int64_t *) = + INT64_C(1000) * var_InheritInteger(p_demux, "live-caching"); + return VLC_SUCCESS; + + case DEMUX_GET_TIME: + *va_arg(args, mtime_t *) = [demux pts]; + return VLC_SUCCESS; + + default: + return VLC_EGENERIC; + } + return VLC_EGENERIC; +} + +@implementation VLCAVCaptureDemux +- (VLCAVCaptureDemux *)init:(demux_t *)p_demux +{ NSString *avf_currdevice_uid; NSArray *myVideoDevices; NSError *o_returnedError; @@ -266,178 +361,135 @@ static int Open(vlc_object_t *p_this) char *psz_uid = NULL; - /* Only when selected */ - if ( *p_demux->psz_access == '\0' ) - return VLC_EGENERIC; + _demux = p_demux; - @autoreleasepool { - if (p_demux->psz_location && *p_demux->psz_location) - psz_uid = strdup(p_demux->psz_location); - - msg_Dbg(p_demux, "avcapture uid = %s", psz_uid); - avf_currdevice_uid = [[NSString alloc] initWithFormat:@"%s", psz_uid]; - - /* Set up p_demux */ - p_demux->pf_demux = Demux; - p_demux->pf_control = Control; - p_demux->info.i_update = 0; - p_demux->info.i_title = 0; - p_demux->info.i_seekpoint = 0; - - p_demux->p_sys = p_sys = calloc(1, sizeof(demux_sys_t)); - if ( !p_sys ) - return VLC_ENOMEM; - - myVideoDevices = [[AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo] - arrayByAddingObjectsFromArray:[AVCaptureDevice devicesWithMediaType:AVMediaTypeMuxed]]; - if ( [myVideoDevices count] == 0 ) - { - vlc_dialog_display_error(p_demux, _("No video devices found"), - _("Your Mac does not seem to be equipped with a suitable video input device. " - "Please check your connectors and drivers.")); - msg_Err(p_demux, "Can't find any suitable video device"); - goto error; - } + if (_demux->psz_location && *_demux->psz_location) + psz_uid = strdup(_demux->psz_location); - deviceCount = [myVideoDevices count]; - for ( ivideo = 0; ivideo < deviceCount; ivideo++ ) - { - AVCaptureDevice *avf_device; - avf_device = [myVideoDevices objectAtIndex:ivideo]; - msg_Dbg(p_demux, "avcapture %i/%i %s %s", ivideo, deviceCount, [[avf_device modelID] UTF8String], [[avf_device uniqueID] UTF8String]); - if ([[[avf_device uniqueID]stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]] isEqualToString:avf_currdevice_uid]) { - break; - } - } - - if ( ivideo < [myVideoDevices count] ) - { - p_sys->device = CFBridgingRetain([myVideoDevices objectAtIndex:ivideo]); - } - else - { - msg_Dbg(p_demux, "Cannot find designated device as %s, falling back to default.", [avf_currdevice_uid UTF8String]); - p_sys->device = CFBridgingRetain([AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo]); - } - if ( !p_sys->device ) - { - vlc_dialog_display_error(p_demux, _("No video devices found"), - _("Your Mac does not seem to be equipped with a suitable input device. " - "Please check your connectors and drivers.")); - msg_Err(p_demux, "Can't find any suitable video device"); - goto error; - } + msg_Dbg(_demux, "avcapture uid = %s", psz_uid); + avf_currdevice_uid = [[NSString alloc] initWithFormat:@"%s", psz_uid]; - if ( [(__bridge AVCaptureDevice *)p_sys->device isInUseByAnotherApplication] == YES ) - { - msg_Err(p_demux, "default capture device is exclusively in use by another application"); - goto error; - } + myVideoDevices = [[AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo] + arrayByAddingObjectsFromArray:[AVCaptureDevice devicesWithMediaType:AVMediaTypeMuxed]]; + if ( [myVideoDevices count] == 0 ) + { + vlc_dialog_display_error(_demux, _("No video devices found"), + _("Your Mac does not seem to be equipped with a suitable video input device. " + "Please check your connectors and drivers.")); + msg_Err(_demux, "Can't find any suitable video device"); + return nil; + } - if (@available(macOS 10.14, *)) { - msg_Dbg(p_demux, "Check user consent for access to the video device"); - - dispatch_semaphore_t sema = dispatch_semaphore_create(0); - __block bool accessGranted = NO; - [AVCaptureDevice requestAccessForMediaType: AVMediaTypeVideo completionHandler:^(BOOL granted) { - accessGranted = granted; - dispatch_semaphore_signal(sema); - } ]; - dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER); - dispatch_release(sema); - if (!accessGranted) { - msg_Err(p_demux, "Can't use the video device as access has not been granted by the user"); - vlc_dialog_display_error(p_demux, _("Problem accessing a system resource"), - _("Please open \"System Preferences\" -> \"Security & Privacy\" " - "and allow VLC to access your camera.")); - - goto error; - } + deviceCount = [myVideoDevices count]; + for ( ivideo = 0; ivideo < deviceCount; ivideo++ ) + { + AVCaptureDevice *avf_device; + avf_device = [myVideoDevices objectAtIndex:ivideo]; + msg_Dbg(_demux, "avcapture %i/%i %s %s", ivideo, deviceCount, [[avf_device modelID] UTF8String], [[avf_device uniqueID] UTF8String]); + if ([[[avf_device uniqueID]stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]] isEqualToString:avf_currdevice_uid]) { + break; } + } - input = [AVCaptureDeviceInput deviceInputWithDevice:(__bridge AVCaptureDevice *)p_sys->device error:&o_returnedError]; + if ( ivideo < [myVideoDevices count] ) + { + _device = [myVideoDevices objectAtIndex:ivideo]; + } + else + { + msg_Dbg(_demux, "Cannot find designated device as %s, falling back to default.", [avf_currdevice_uid UTF8String]); + _device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo]; + } - if ( !input ) - { - msg_Err(p_demux, "can't create a valid capture input facility: %s (%ld)",[[o_returnedError localizedDescription] UTF8String], [o_returnedError code]); - goto error; - } + if ( !_device ) + { + vlc_dialog_display_error(_demux, _("No video devices found"), + _("Your Mac does not seem to be equipped with a suitable input device. " + "Please check your connectors and drivers.")); + msg_Err(_demux, "Can't find any suitable video device"); + return nil; + } + AVCaptureDevice *device = _device; - int chroma = VLC_CODEC_RGB32; + if ( [device isInUseByAnotherApplication] == YES ) + { + msg_Err(_demux, "default capture device is exclusively in use by another application"); + return nil; + } - memset(&p_sys->fmt, 0, sizeof(es_format_t)); - es_format_Init(&p_sys->fmt, VIDEO_ES, chroma); + if (@available(macOS 10.14, *)) { + msg_Dbg(_demux, "Check user consent for access to the video device"); + + dispatch_semaphore_t sema = dispatch_semaphore_create(0); + __block bool accessGranted = NO; + [AVCaptureDevice requestAccessForMediaType: AVMediaTypeVideo completionHandler:^(BOOL granted) { + accessGranted = granted; + dispatch_semaphore_signal(sema); + } ]; + dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER); + dispatch_release(sema); + if (!accessGranted) { + msg_Err(_demux, "Can't use the video device as access has not been granted by the user"); + vlc_dialog_display_error(_demux, _("Problem accessing a system resource"), + _("Please open \"System Preferences\" -> \"Security & Privacy\" " + "and allow VLC to access your camera.")); + + return nil; + } + } - p_sys->session = CFBridgingRetain([[AVCaptureSession alloc] init]); - [(__bridge AVCaptureSession *)p_sys->session addInput:input]; + input = [AVCaptureDeviceInput deviceInputWithDevice:device error:&o_returnedError]; - p_sys->output = CFBridgingRetain([[VLCAVDecompressedVideoOutput alloc] initWithDemux:p_demux]); - [(__bridge AVCaptureSession *)p_sys->session addOutput:(__bridge VLCAVDecompressedVideoOutput *)p_sys->output]; + if ( !input ) + { + msg_Err(_demux, "can't create a valid capture input facility: %s (%ld)",[[o_returnedError localizedDescription] UTF8String], [o_returnedError code]); + return nil; + } - dispatch_queue_t queue = dispatch_queue_create("avCaptureQueue", NULL); - [(__bridge VLCAVDecompressedVideoOutput *)p_sys->output setSampleBufferDelegate:(__bridge id)p_sys->output queue:queue]; - dispatch_release(queue); + int chroma = VLC_CODEC_RGB32; - [(__bridge VLCAVDecompressedVideoOutput *)p_sys->output setVideoSettings:[NSDictionary dictionaryWithObject:[NSNumber numberWithInt:kCVPixelFormatType_32BGRA] forKey:(id)kCVPixelBufferPixelFormatTypeKey]]; - [(__bridge AVCaptureSession *)p_sys->session startRunning]; + memset(&_fmt, 0, sizeof(es_format_t)); + es_format_Init(&_fmt, VIDEO_ES, chroma); - input = nil; + _session = [[AVCaptureSession alloc] init]; - msg_Dbg(p_demux, "AVCapture: Video device ready!"); + [_session addInput:input]; - return VLC_SUCCESS; - error: - msg_Err(p_demux, "Error"); - input = nil; + _output = [[VLCAVDecompressedVideoOutput alloc] initWithDemux:_demux]; - free(p_sys); + [_session addOutput:_output]; - return VLC_EGENERIC; - } -} + dispatch_queue_t queue = dispatch_queue_create("avCaptureQueue", NULL); + [_output setSampleBufferDelegate:_output queue:queue]; + dispatch_release(queue); -/***************************************************************************** -* Close: -*****************************************************************************/ -static void Close(vlc_object_t *p_this) -{ - demux_t *p_demux = (demux_t*)p_this; - demux_sys_t *p_sys = p_demux->p_sys; + [_output setVideoSettings:[NSDictionary dictionaryWithObject:[NSNumber numberWithInt:kCVPixelFormatType_32BGRA] forKey:(id)kCVPixelBufferPixelFormatTypeKey]]; + [_session startRunning]; - @autoreleasepool { - msg_Dbg(p_demux,"Close AVCapture"); + input = nil; - // Perform this on main thread, as the framework itself will sometimes try to synchronously - // work on main thread. And this will create a dead lock. - [(__bridge AVCaptureSession *)p_sys->session performSelectorOnMainThread:@selector(stopRunning) withObject:nil waitUntilDone:NO]; - CFBridgingRelease(p_sys->output); - CFBridgingRelease(p_sys->session); + msg_Dbg(_demux, "AVCapture: Video device ready!"); - free(p_sys); - } + return self; } -/***************************************************************************** -* Demux: -*****************************************************************************/ -static int Demux(demux_t *p_demux) +- (int)demux { - demux_sys_t *p_sys = p_demux->p_sys; block_t *p_block; @autoreleasepool { - @synchronized ( p_sys->output ) + @synchronized(_output) { - p_block = block_Alloc([(__bridge VLCAVDecompressedVideoOutput *)p_sys->output width] * [(__bridge VLCAVDecompressedVideoOutput *)p_sys->output bytesPerRow]); + p_block = block_Alloc([_output width] * [_output bytesPerRow]); if ( !p_block ) { - msg_Err(p_demux, "cannot get block"); + msg_Err(_demux, "cannot get block"); return 0; } - p_block->i_pts = [(__bridge VLCAVDecompressedVideoOutput *)p_sys->output copyCurrentFrameToBuffer: p_block->p_buffer]; + p_block->i_pts = [_output copyCurrentFrameToBuffer: p_block->p_buffer]; if ( !p_block->i_pts ) { @@ -446,56 +498,41 @@ static int Demux(demux_t *p_demux) msleep(10000); return 1; } - else if ( !p_sys->b_es_setup ) + else if ( !_es_video ) { - p_sys->fmt.video.i_frame_rate_base = [(__bridge VLCAVDecompressedVideoOutput *)p_sys->output timeScale]; - msg_Dbg(p_demux, "using frame rate base: %i", p_sys->fmt.video.i_frame_rate_base); - p_sys->width = p_sys->fmt.video.i_width = [(__bridge VLCAVDecompressedVideoOutput *)p_sys->output width]; - p_sys->height = p_sys->fmt.video.i_height = [(__bridge VLCAVDecompressedVideoOutput *)p_sys->output height]; - p_sys->p_es_video = es_out_Add(p_demux->out, &p_sys->fmt); - msg_Dbg(p_demux, "added new video es %4.4s %dx%d", (char*)&p_sys->fmt.i_codec, p_sys->width, p_sys->height); - p_sys->b_es_setup = YES; + _fmt.video.i_frame_rate = 1; + _fmt.video.i_frame_rate_base = [_output timeScale]; + msg_Dbg(_demux, "using frame rate base: %i", _fmt.video.i_frame_rate_base); + _width + = _fmt.video.i_width + = _fmt.video.i_visible_width + = [_output width]; + _height + = _fmt.video.i_height + = _fmt.video.i_visible_height + = [_output height]; + _fmt.video.i_chroma = _fmt.i_codec; + + _es_video = es_out_Add(_demux->out, &_fmt); + video_format_Print(&_demux->obj, "added new video es", &_fmt.video); } } - - es_out_SetPCR(p_demux->out, p_block->i_pts); - es_out_Send(p_demux->out, p_sys->p_es_video, p_block); - + + es_out_SetPCR(_demux->out, p_block->i_pts); + es_out_Send(_demux->out, _es_video, p_block); } return 1; } -/***************************************************************************** -* Control: -*****************************************************************************/ -static int Control(demux_t *p_demux, int i_query, va_list args) +- (mtime_t)pts { - bool *pb; - int64_t *pi64; - - switch( i_query ) - { - /* Special for access_demux */ - case DEMUX_CAN_PAUSE: - case DEMUX_CAN_SEEK: - case DEMUX_SET_PAUSE_STATE: - case DEMUX_CAN_CONTROL_PACE: - pb = va_arg(args, bool *); - *pb = false; - return VLC_SUCCESS; - - case DEMUX_GET_PTS_DELAY: - pi64 = va_arg(args, int64_t *); - *pi64 = INT64_C(1000) * var_InheritInteger(p_demux, "live-caching"); - return VLC_SUCCESS; - - case DEMUX_GET_TIME: - pi64 = va_arg(args, int64_t *); - *pi64 = mdate(); - return VLC_SUCCESS; + return [_output currentPts]; +} - default: - return VLC_EGENERIC; - } - return VLC_EGENERIC; +- (void)dealloc +{ + // Perform this on main thread, as the framework itself will sometimes try to synchronously + // work on main thread. And this will create a dead lock. + [_session performSelectorOnMainThread:@selector(stopRunning) withObject:nil waitUntilDone:NO]; } +@end View it on GitLab: https://code.videolan.org/videolan/vlc/-/compare/2c077f260eaa8d17370d35ad47b8ba10bf987bae...aa51750767f722debbfbe9bda4cb59aef8901cd8 -- View it on GitLab: https://code.videolan.org/videolan/vlc/-/compare/2c077f260eaa8d17370d35ad47b8ba10bf987bae...aa51750767f722debbfbe9bda4cb59aef8901cd8 You're receiving this email because of your account on code.videolan.org.
_______________________________________________ vlc-commits mailing list vlc-commits@videolan.org https://mailman.videolan.org/listinfo/vlc-commits