]> git.sesse.net Git - vlc/blob - modules/access/avcapture.m
vpx: fix leak
[vlc] / modules / access / avcapture.m
1 /*****************************************************************************
2  * avcapture.m: AVFoundation (Mac OS X) based video capture module
3  *****************************************************************************
4  * Copyright © 2008-2013 VLC authors and VideoLAN
5  *
6  * Authors: Michael Feurstein <michael.feurstein@gmail.com>
7  *
8  ****************************************************************************
9  * This program is free software; you can redistribute it and/or modify it
10  * under the terms of the GNU Lesser General Public License as published by
11  * the Free Software Foundation; either version 2.1 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17  * GNU Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public License
20  * along with this program; if not, write to the Free Software Foundation,
21  * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22  *****************************************************************************/
23
24 /*****************************************************************************
25  * Preamble
26  *****************************************************************************/
27
28 #ifdef HAVE_CONFIG_H
29 # include "config.h"
30 #endif
31
32 #include <vlc_common.h>
33 #include <vlc_plugin.h>
34 #include <vlc_input.h>
35 #include <vlc_demux.h>
36 #include <vlc_interface.h>
37 #include <vlc_dialog.h>
38 #include <vlc_access.h>
39
40 #import <AVFoundation/AVFoundation.h>
41 #import <CoreMedia/CoreMedia.h>
42
43 /*****************************************************************************
44 * Local prototypes
45 *****************************************************************************/
46 static int Open(vlc_object_t *p_this);
47 static void Close(vlc_object_t *p_this);
48 static int Demux(demux_t *p_demux);
49 static int Control(demux_t *, int, va_list);
50
51 /*****************************************************************************
52 * Module descriptor
53 *****************************************************************************/
54 vlc_module_begin ()
55    set_shortname(N_("AVFoundation Video Capture"))
56    set_description(N_("AVFoundation video capture module."))
57    set_category(CAT_INPUT)
58    set_subcategory(SUBCAT_INPUT_ACCESS)
59    add_shortcut("avcapture")
60    set_capability("access_demux", 10)
61    set_callbacks(Open, Close)
62 vlc_module_end ()
63
64
65 /*****************************************************************************
66 * AVFoundation Bridge
67 *****************************************************************************/
68 @interface VLCAVDecompressedVideoOutput : AVCaptureVideoDataOutput
69 {
70     demux_t             *p_avcapture;
71
72     CVImageBufferRef    currentImageBuffer;
73     CMVideoDimensions   videoDimensions;
74
75     mtime_t             currentPts;
76     mtime_t             previousPts;
77     size_t              bytesPerRow;
78
79     long                timeScale;
80     BOOL                videoDimensionsReady;
81 }
82
83 @property (readwrite) CMVideoDimensions videoDimensions;
84
85 - (id)initWithDemux:(demux_t *)p_demux;
86 - (int)width;
87 - (int)height;
88 - (void)getVideoDimensions:(CMSampleBufferRef)sampleBuffer;
89 - (mtime_t)currentPts;
90 - (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection *)connection;
91 - (mtime_t)copyCurrentFrameToBuffer:(void *)buffer;
92 @end
93
94 @implementation VLCAVDecompressedVideoOutput : AVCaptureVideoDataOutput
95
96 @synthesize videoDimensions;
97
98 - (id)initWithDemux:(demux_t *)p_demux
99 {
100     if (self = [super init])
101     {
102         p_avcapture = p_demux;
103         currentImageBuffer = nil;
104         currentPts = 0;
105         previousPts = 0;
106         bytesPerRow = 0;
107         timeScale = 0;
108         videoDimensionsReady = NO;
109     }
110     return self;
111 }
112
113 - (void)dealloc
114 {
115     @synchronized (self)
116     {
117         CVBufferRelease(currentImageBuffer);
118         currentImageBuffer = nil;
119         bytesPerRow = 0;
120         videoDimensionsReady = NO;
121     }
122     [super dealloc];
123 }
124
125 - (long)timeScale
126 {
127     return timeScale;
128 }
129
130 - (int)width
131 {
132     return self.videoDimensions.width;
133 }
134
135 - (int)height
136 {
137     return self.videoDimensions.height;
138 }
139
140 - (size_t)bytesPerRow
141 {
142     return bytesPerRow;
143 }
144
145 - (void)getVideoDimensions:(CMSampleBufferRef)sampleBuffer
146 {
147     if (!videoDimensionsReady)
148     {
149         CMFormatDescriptionRef formatDescription = CMSampleBufferGetFormatDescription(sampleBuffer);
150         self.videoDimensions = CMVideoFormatDescriptionGetDimensions(formatDescription);
151         bytesPerRow = CVPixelBufferGetBytesPerRow(CMSampleBufferGetImageBuffer(sampleBuffer));
152         videoDimensionsReady = YES;
153         msg_Dbg(p_avcapture, "Dimensionns obtained height:%i width:%i bytesPerRow:%lu", [self height], [self width], bytesPerRow);
154     }
155 }
156
157 -(mtime_t)currentPts
158 {
159     mtime_t pts;
160
161     if ( !currentImageBuffer || currentPts == previousPts )
162         return 0;
163
164     @synchronized (self)
165     {
166         pts = previousPts = currentPts;
167     }
168
169     return currentPts;
170 }
171
172 - (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection *)connection
173 {
174     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
175
176     CVImageBufferRef imageBufferToRelease;
177     CMTime presentationtimestamp = CMSampleBufferGetPresentationTimeStamp(sampleBuffer);
178     CVImageBufferRef videoFrame = CMSampleBufferGetImageBuffer(sampleBuffer);
179     CVBufferRetain(videoFrame);
180     [self getVideoDimensions:sampleBuffer];
181
182     @synchronized (self)
183     {
184         imageBufferToRelease = currentImageBuffer;
185         currentImageBuffer = videoFrame;
186         currentPts = (mtime_t)presentationtimestamp.value;
187         timeScale = (long)presentationtimestamp.timescale;
188     }
189
190     CVBufferRelease(imageBufferToRelease);
191     [pool release];
192 }
193
194 - (mtime_t)copyCurrentFrameToBuffer:(void *)buffer
195 {
196     CVImageBufferRef imageBuffer;
197     mtime_t pts;
198
199     void *pixels;
200
201     if ( !currentImageBuffer || currentPts == previousPts )
202         return 0;
203
204     @synchronized (self)
205     {
206         imageBuffer = CVBufferRetain(currentImageBuffer);
207         if (imageBuffer)
208         {
209             pts = previousPts = currentPts;
210             CVPixelBufferLockBaseAddress(imageBuffer, 0);
211             pixels = CVPixelBufferGetBaseAddress(imageBuffer);
212             if (pixels)
213             {
214                 memcpy(buffer, pixels, CVPixelBufferGetHeight(imageBuffer) * CVPixelBufferGetBytesPerRow(imageBuffer));
215             }
216             CVPixelBufferUnlockBaseAddress(imageBuffer, 0);
217         }
218     }
219     CVBufferRelease(imageBuffer);
220
221     if (pixels)
222         return currentPts;
223     else
224         return 0;
225 }
226
227 @end
228
229 /*****************************************************************************
230 * Struct
231 *****************************************************************************/
232
233 struct demux_sys_t
234 {
235     AVCaptureSession                *session;
236     AVCaptureDevice                 *device;
237     VLCAVDecompressedVideoOutput    *output;
238     es_out_id_t                     *p_es_video;
239     es_format_t                     fmt;
240     int                             height, width;
241     BOOL                            b_es_setup;
242 };
243
244 /*****************************************************************************
245 * Open:
246 *****************************************************************************/
247 static int Open(vlc_object_t *p_this)
248 {
249     demux_t                 *p_demux = (demux_t*)p_this;
250     demux_sys_t             *p_sys = NULL;
251
252     NSString                *avf_currdevice_uid;
253     NSArray                 *myVideoDevices;
254     NSError                 *o_returnedError;
255
256     AVCaptureDeviceInput    *input = nil;
257
258     int                     i, i_width, i_height, deviceCount, ivideo;
259
260     char                    *psz_uid = NULL;
261
262     NSAutoreleasePool       *pool = [[NSAutoreleasePool alloc] init];
263
264     /* Only when selected */
265     if ( *p_demux->psz_access == '\0' )
266         return VLC_EGENERIC;
267
268     if ( p_demux->psz_location && *p_demux->psz_location )
269         psz_uid = strdup(p_demux->psz_location);
270
271     msg_Dbg(p_demux, "avcapture uid = %s", psz_uid);
272     avf_currdevice_uid = [[NSString alloc] initWithFormat:@"%s", psz_uid];
273
274     /* Set up p_demux */
275     p_demux->pf_demux = Demux;
276     p_demux->pf_control = Control;
277     p_demux->info.i_update = 0;
278     p_demux->info.i_title = 0;
279     p_demux->info.i_seekpoint = 0;
280
281     p_demux->p_sys = p_sys = calloc(1, sizeof(demux_sys_t));
282     if ( !p_sys )
283         return VLC_ENOMEM;
284
285     myVideoDevices = [[[AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo] arrayByAddingObjectsFromArray:[AVCaptureDevice devicesWithMediaType:AVMediaTypeMuxed]] retain];
286     if ( [myVideoDevices count] == 0 )
287     {
288         dialog_FatalWait(p_demux, _("No video devices found"),
289                          _("Your Mac does not seem to be equipped with a suitable video input device. "
290                            "Please check your connectors and drivers."));
291         msg_Err(p_demux, "Can't find any suitable video device");
292         goto error;
293     }
294
295     deviceCount = [myVideoDevices count];
296     for ( ivideo = 0; ivideo < deviceCount; ivideo++ )
297     {
298         AVCaptureDevice *avf_device;
299         avf_device = [myVideoDevices objectAtIndex:ivideo];
300         msg_Dbg(p_demux, "avcapture %i/%i %s %s", ivideo, deviceCount, [[avf_device modelID] UTF8String], [[avf_device uniqueID] UTF8String]);
301         if ([[[avf_device uniqueID]stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]] isEqualToString:avf_currdevice_uid]) {
302             break;
303         }
304     }
305
306     if ( ivideo < [myVideoDevices count] )
307     {
308        p_sys->device = [myVideoDevices objectAtIndex:ivideo];
309     }
310     else
311     {
312         msg_Dbg(p_demux, "Cannot find designated device as %s, falling back to default.", [avf_currdevice_uid UTF8String]);
313         p_sys->device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
314     }
315     if ( !p_sys->device )
316     {
317         dialog_FatalWait(p_demux, _("No video devices found"),
318                         _("Your Mac does not seem to be equipped with a suitable input device. "
319                           "Please check your connectors and drivers."));
320         msg_Err(p_demux, "Can't find any suitable video device");
321         goto error;
322     }
323
324     if ( [p_sys->device isInUseByAnotherApplication] == YES )
325     {
326         msg_Err(p_demux, "default capture device is exclusively in use by another application");
327         goto error;
328     }
329
330     input = [AVCaptureDeviceInput deviceInputWithDevice:p_sys->device error:&o_returnedError];
331
332     if ( !input )
333     {
334         msg_Err(p_demux, "can't create a valid capture input facility (%ld)", [o_returnedError code]);
335         goto error;
336     }
337
338     int chroma = VLC_CODEC_RGB32;
339
340     memset(&p_sys->fmt, 0, sizeof(es_format_t));
341     es_format_Init(&p_sys->fmt, VIDEO_ES, chroma);
342
343     p_sys->session = [[AVCaptureSession alloc] init];
344     [p_sys->session addInput:input];
345
346     p_sys->output = [[VLCAVDecompressedVideoOutput alloc] initWithDemux:p_demux];
347     [p_sys->session addOutput:p_sys->output];
348
349     dispatch_queue_t queue = dispatch_queue_create("avCaptureQueue", NULL);
350     [p_sys->output setSampleBufferDelegate:(id)p_sys->output queue:queue];
351     dispatch_release(queue);
352
353     p_sys->output.videoSettings = [NSDictionary dictionaryWithObject:[NSNumber numberWithInt:kCVPixelFormatType_32BGRA] forKey:(id)kCVPixelBufferPixelFormatTypeKey];
354     [p_sys->session startRunning];
355
356     [input release];
357
358     msg_Dbg(p_demux, "AVCapture: Video device ready!");
359
360     [pool release];
361     return VLC_SUCCESS;
362 error:
363     msg_Err(p_demux, "Error");
364     [input release];
365
366     free(p_sys);
367
368     [pool release];
369     return VLC_EGENERIC;
370 }
371
372 /*****************************************************************************
373 * Close:
374 *****************************************************************************/
375 static void Close(vlc_object_t *p_this)
376 {
377     NSAutoreleasePool   *pool = [[NSAutoreleasePool alloc] init];
378     demux_t             *p_demux = (demux_t*)p_this;
379     demux_sys_t         *p_sys = p_demux->p_sys;
380
381     msg_Dbg(p_demux,"Close AVCapture");
382
383     // Perform this on main thread, as the framework itself will sometimes try to synchronously
384     // work on main thread. And this will create a dead lock.
385     [p_sys->session performSelectorOnMainThread:@selector(stopRunning) withObject:nil waitUntilDone:NO];
386     [p_sys->output performSelectorOnMainThread:@selector(release) withObject:nil waitUntilDone:NO];
387     [p_sys->session performSelectorOnMainThread:@selector(release) withObject:nil waitUntilDone:NO];
388
389     free(p_sys);
390
391     [pool release];
392 }
393
394 /*****************************************************************************
395 * Demux:
396 *****************************************************************************/
397 static int Demux(demux_t *p_demux)
398 {
399     demux_sys_t *p_sys = p_demux->p_sys;
400     block_t     *p_block;
401
402     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
403
404     @synchronized ( p_sys->output )
405     {
406         p_block = block_Alloc([p_sys->output width] * [p_sys->output bytesPerRow]);
407
408         if ( !p_block )
409         {
410             msg_Err(p_demux, "cannot get block");
411             return 0;
412         }
413
414         p_block->i_pts = [p_sys->output copyCurrentFrameToBuffer: p_block->p_buffer];
415
416         if ( !p_block->i_pts )
417         {
418             /* Nothing to display yet, just forget */
419             block_Release(p_block);
420             [pool release];
421             msleep(10000);
422             return 1;
423         }
424         else if ( !p_sys->b_es_setup )
425         {
426             p_sys->fmt.video.i_frame_rate_base = [p_sys->output timeScale];
427             msg_Dbg(p_demux, "using frame rate base: %i", p_sys->fmt.video.i_frame_rate_base);
428             p_sys->width = p_sys->fmt.video.i_width = [p_sys->output width];
429             p_sys->height = p_sys->fmt.video.i_height = [p_sys->output height];
430             p_sys->p_es_video = es_out_Add(p_demux->out, &p_sys->fmt);
431             msg_Dbg(p_demux, "added new video es %4.4s %dx%d", (char*)&p_sys->fmt.i_codec, p_sys->width, p_sys->height);
432             p_sys->b_es_setup = YES;
433         }
434     }
435
436     es_out_Control(p_demux->out, ES_OUT_SET_PCR, p_block->i_pts);
437     es_out_Send(p_demux->out, p_sys->p_es_video, p_block);
438
439     [pool release];
440     return 1;
441 }
442
443 /*****************************************************************************
444 * Control:
445 *****************************************************************************/
446 static int Control(demux_t *p_demux, int i_query, va_list args)
447 {
448     bool        *pb;
449     int64_t     *pi64;
450
451     switch( i_query )
452     {
453         /* Special for access_demux */
454         case DEMUX_CAN_PAUSE:
455         case DEMUX_CAN_SEEK:
456         case DEMUX_SET_PAUSE_STATE:
457         case DEMUX_CAN_CONTROL_PACE:
458            pb = (bool*)va_arg(args, bool *);
459            *pb = false;
460            return VLC_SUCCESS;
461
462         case DEMUX_GET_PTS_DELAY:
463            pi64 = (int64_t*)va_arg(args, int64_t *);
464            *pi64 = INT64_C(1000) * var_InheritInteger(p_demux, "live-caching");
465            return VLC_SUCCESS;
466
467         case DEMUX_GET_TIME:
468             pi64 = (int64_t*)va_arg(args, int64_t *);
469             *pi64 = mdate();
470             return VLC_SUCCESS;
471
472         default:
473            return VLC_EGENERIC;
474     }
475     return VLC_EGENERIC;
476 }