qtcapture.m 11.3 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
/*****************************************************************************
* qtcapture.m: qtkit (Mac OS X) based capture module
*****************************************************************************
* Copyright (C) 2008 the VideoLAN team
*
* Authors: Pierre d'Herbemont <pdherbemont@videolan.org>
*
*****************************************************************************
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2 of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*
*****************************************************************************/

/*****************************************************************************
* Preamble
*****************************************************************************/

#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

33
#include <vlc_common.h>
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
#include <vlc_plugin.h>
#include <vlc_input.h>
#include <vlc_vout.h>
#include <vlc_demux.h>

#import <QTKit/QTKit.h>

/*****************************************************************************
* Local prototypes
*****************************************************************************/
static int Open( vlc_object_t *p_this );
static void Close( vlc_object_t *p_this );
static int Demux( demux_t *p_demux );
static int Control( demux_t *, int, va_list );

/*****************************************************************************
* Module descriptor
*****************************************************************************/
vlc_module_begin();
   set_shortname( N_("Quicktime Capture") );
   set_description( N_("Quicktime Capture") );
   set_category( CAT_INPUT );
   set_subcategory( SUBCAT_INPUT_ACCESS );
   add_shortcut( "qtcapture" );
58
   set_capability( "access_demux", 10 );
59
60
61
62
63
64
65
66
67
   set_callbacks( Open, Close );
vlc_module_end();


/*****************************************************************************
* QTKit Bridge
*****************************************************************************/
@interface VLCDecompressedVideoOutput : QTCaptureDecompressedVideoOutput
{
68
69
    CVImageBufferRef currentImageBuffer;
    mtime_t currentPts;
70
71
72
}
- (id)init;
- (void)outputVideoFrame:(CVImageBufferRef)videoFrame withSampleBuffer:(QTSampleBuffer *)sampleBuffer fromConnection:(QTCaptureConnection *)connection;
Pierre d'Herbemont's avatar
Pierre d'Herbemont committed
73
- (mtime_t)copyCurrentFrameToBuffer:(void *)buffer;
74
75
76
77
78
79
@end

/* Apple sample code */
@implementation VLCDecompressedVideoOutput : QTCaptureDecompressedVideoOutput
- (id)init
{
80
81
82
83
84
85
    if( self = [super init] )
    {
        currentImageBuffer = nil;
        currentPts = 0;
    }
    return self;
86
87
88
}
- (void)dealloc
{
89
90
    @synchronized (self)
    {
91
92
93
94
        CVBufferRelease(currentImageBuffer);
        currentImageBuffer = nil;
    }
    [super dealloc];
95
96
97
98
}

- (void)outputVideoFrame:(CVImageBufferRef)videoFrame withSampleBuffer:(QTSampleBuffer *)sampleBuffer fromConnection:(QTCaptureConnection *)connection
{
99
100
101
    // Store the latest frame
    // This must be done in a @synchronized block because this delegate method is not called on the main thread
    CVImageBufferRef imageBufferToRelease;
102

103
    CVBufferRetain(videoFrame);
104

105
106
    @synchronized (self)
    {
107
108
        imageBufferToRelease = currentImageBuffer;
        currentImageBuffer = videoFrame;
109
        currentPts = [sampleBuffer presentationTime].timeValue;
110
111
    }
    CVBufferRelease(imageBufferToRelease);
112
113
}

114
- (mtime_t)copyCurrentFrameToBuffer:(void *)buffer
115
{
116
117
    CVImageBufferRef imageBuffer;
    mtime_t pts;
118
119
120
121
122
123

    if(!currentImageBuffer)
        return 0;

    @synchronized (self)
    {
124
125
        imageBuffer = CVBufferRetain(currentImageBuffer);
        pts = currentPts;
126

127
128
129
130
        CVPixelBufferLockBaseAddress(imageBuffer, 0);
        void * pixels = CVPixelBufferGetBaseAddress(imageBuffer);
        memcpy( buffer, pixels, CVPixelBufferGetBytesPerRow(imageBuffer) * CVPixelBufferGetHeight(imageBuffer) );
        CVPixelBufferUnlockBaseAddress(imageBuffer, 0);
131
    }
132

133
    CVBufferRelease(imageBuffer);
134

135
    return currentPts;
136
137
138
139
}

@end

140
141
142
143
/*****************************************************************************
* Struct
*****************************************************************************/

144
struct demux_sys_t {
145
146
147
148
149
    QTCaptureSession * session;
    QTCaptureDevice * device;
    VLCDecompressedVideoOutput * output;
    int height, width;
    es_out_id_t * p_es_video;
150
151
152
};


153
154
155
156
/*****************************************************************************
* qtchroma_to_fourcc
*****************************************************************************/
static int qtchroma_to_fourcc( int i_qt )
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
{
    static struct
    {
        unsigned int i_qt;
        int i_fourcc;
    } qtchroma_to_fourcc[] =
    {
        /* Raw data types */
        { k422YpCbCr8CodecType,    VLC_FOURCC('U','Y','V','Y') },
        { 0, 0 }
    };
    int i;
    for( i = 0; qtchroma_to_fourcc[i].i_qt; i++ )
    {
        if( qtchroma_to_fourcc[i].i_qt == i_qt )
            return qtchroma_to_fourcc[i].i_fourcc;
    }
    return 0;
}

/*****************************************************************************
* Open:
*****************************************************************************/
static int Open( vlc_object_t *p_this )
{
    demux_t     *p_demux = (demux_t*)p_this;
    demux_sys_t *p_sys = NULL;
    es_format_t fmt;
    int i;
    int i_width;
    int i_height;
    int i_aspect;
    int result = 0;

191
192
193
    /* Only when selected */
    if( *p_demux->psz_access == '\0' )
        return VLC_EGENERIC;
194
    
195
196
    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];

197
198
199
200
201
202
203
204
205
206
207
208
209
    /* 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 = malloc( sizeof( demux_sys_t ) );
    if( !p_sys ) return VLC_ENOMEM;
    
    memset( p_sys, 0, sizeof( demux_sys_t ) );
    memset( &fmt, 0, sizeof( es_format_t ) );    
    
210
211
212
213
    msg_Dbg( p_demux, "QTCapture Probed" );

    QTCaptureDeviceInput * input = nil;

214
215
    p_sys->device = [QTCaptureDevice defaultInputDeviceWithMediaType: QTMediaTypeVideo];
    if( !p_sys->device )
216
    {
217
        msg_Err( p_demux, "Can't find any Video device" );
218
219
220
        goto error;
    }

221
    if( ![p_sys->device open: nil  /* FIXME */] )
222
223
224
225
226
    {
        msg_Err( p_demux, "Can't open any Video device" );
        goto error;
    }

227
228
    input = [[QTCaptureDeviceInput alloc] initWithDevice: p_sys->device];
    if( !p_sys->device )
229
230
231
232
233
    {
        msg_Err( p_demux, "Can't create a capture session" );
        goto error;
    }

234
    p_sys->output = [[VLCDecompressedVideoOutput alloc] init];
235

236
    /* Hack - This will lower CPU consumption for some reason */
237
    [p_sys->output setPixelBufferAttributes: [NSDictionary dictionaryWithObjectsAndKeys:
238
239
240
        [NSNumber numberWithInt:480], kCVPixelBufferHeightKey,
        [NSNumber numberWithInt:640], kCVPixelBufferWidthKey, nil]];

241
    p_sys->session = [[QTCaptureSession alloc] init];
242

243
    bool ret = [p_sys->session addInput:input error:nil  /* FIXME */];
244
245
246
247
248
249
    if( !ret )
    {
        msg_Err( p_demux, "Can't add the video device as input" );
        goto error;
    }

250
    ret = [p_sys->session addOutput:p_sys->output error:nil  /* FIXME */];
251
252
253
254
255
256
    if( !ret )
    {
        msg_Err( p_demux, "Can't get any output output" );
        goto error;
    }

257
    [p_sys->session startRunning];
258

259

260
    int qtchroma = [[[p_sys->device formatDescriptions] objectAtIndex: 0] formatType]; /* FIXME */
261
262
263
264
265
266
267
    int chroma = qtchroma_to_fourcc( qtchroma );
    if( !chroma )
    {
        msg_Err( p_demux, "Unknown qt chroma %4.4s provided by camera", (char*)&qtchroma );
        goto error;
    }

268
269
    /* Now we can init */

270
271
    es_format_Init( &fmt, VIDEO_ES, chroma );

272
    NSSize size = [[p_sys->device attributeForKey:QTFormatDescriptionVideoEncodedPixelsSizeAttribute] sizeValue];
273
274
275
    p_sys->width = fmt.video.i_width = 640;/* size.width; FIXME */
    p_sys->height = fmt.video.i_height = 480;/* size.height; FIXME */

276
    msg_Dbg( p_demux, "added new video es %4.4s %dx%d",
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
            (char*)&fmt.i_codec, fmt.video.i_width, fmt.video.i_height );

    p_sys->p_es_video = es_out_Add( p_demux->out, &fmt );

    [input release];
    [pool release];

    msg_Dbg( p_demux, "QTCapture: We have a video device ready!" );

    return VLC_SUCCESS;
error:
    [input release];
    [pool release];

    free( p_sys );

    return VLC_EGENERIC;
}

/*****************************************************************************
* Close:
*****************************************************************************/
static void Close( vlc_object_t *p_this )
{
    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];

    demux_t     *p_demux = (demux_t*)p_this;
    demux_sys_t *p_sys = p_demux->p_sys;
305
306

    [p_sys->session stopRunning];
307
308
    [p_sys->output release];
    [p_sys->session release];
309
    [p_sys->device release];
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
    free( p_sys );

    [pool release];
}


/*****************************************************************************
* Demux:
*****************************************************************************/
static int Demux( demux_t *p_demux )
{
    demux_sys_t *p_sys = p_demux->p_sys;
    block_t *p_block;

    p_block = block_New( p_demux, p_sys->width *
                            p_sys->height * 2 /* FIXME */ );
    if( !p_block )
    {
        msg_Err( p_demux, "cannot get block" );
        return 0;
    }

    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];

334
335
    @synchronized (p_sys->output)
    {
336
    p_block->i_pts = [p_sys->output copyCurrentFrameToBuffer: p_block->p_buffer];
337
    }
338
339

    if( !p_block->i_pts )
340
341
342
343
344
345
346
    {
        /* Nothing to display yet, just forget */
        block_Release( p_block );
        [pool release];
        return 1;
    }

347
348
    /* FIXME */
    p_block->i_pts = mdate();
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391

    es_out_Control( p_demux->out, ES_OUT_SET_PCR, p_block->i_pts );
    es_out_Send( p_demux->out, p_sys->p_es_video, p_block );

    [pool release];
    return 1;
}

/*****************************************************************************
* Control:
*****************************************************************************/
static int Control( demux_t *p_demux, int i_query, va_list args )
{
    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 = (bool*)va_arg( args, bool * );
           *pb = false;
           return VLC_SUCCESS;

        case DEMUX_GET_PTS_DELAY:
           pi64 = (int64_t*)va_arg( args, int64_t * );
           *pi64 = (int64_t)DEFAULT_PTS_DELAY;
           return VLC_SUCCESS;

        case DEMUX_GET_TIME:
           pi64 = (int64_t*)va_arg( args, int64_t * );
           *pi64 = mdate();
           return VLC_SUCCESS;

        /* TODO implement others */
        default:
           return VLC_EGENERIC;
    }
    return VLC_EGENERIC;
}