From: Pierre d'Herbemont Date: Mon, 26 May 2008 00:22:38 +0000 (+0200) Subject: qtcapture: New access_demux module (QTKit based) to capture video from your iSight... X-Git-Tag: 0.9.0-test0~738 X-Git-Url: https://git.sesse.net/?a=commitdiff_plain;h=9dcd81a1dc9e0cfa685c61f67f43fa85cb7f9d5b;hp=3dfed6db962960e045721ff09fc7ef624635c3df;p=vlc qtcapture: New access_demux module (QTKit based) to capture video from your iSight Camera on Mac OS X. Usage: 'vlc qtcapture://'. This is a 2 hours hacked module. We don't grab sound yet, and there are a lot of FIXME. Note: For some reason QTKit needs a the main thread to be a cocoa thread. Don't expect it to run with -I dummy for instance. --- diff --git a/configure.ac b/configure.ac index 053075c821..1e78013a16 100644 --- a/configure.ac +++ b/configure.ac @@ -5528,18 +5528,20 @@ AC_ARG_ENABLE(macosx, if test "x${enable_macosx}" = "xyes" then # VLC_ADD_LDFLAGS([access_eyetv], [-Wl,-framework,Foundation]) - VLC_ADD_LDFLAGS([macosx minimal_macosx opengllayer],[-Wl,-framework,Cocoa]) + VLC_ADD_LDFLAGS([macosx minimal_macosx opengllayer qtcapture],[-Wl,-framework,Cocoa]) VLC_ADD_LDFLAGS([macosx minimal_macosx opengllayer],[-Wl,-framework,OpenGL]) VLC_ADD_LDFLAGS([macosx minimal_macosx], [-Wl,-framework,Carbon]) VLC_ADD_LDFLAGS([macosx minimal_macosx], [-Wl,-framework,AGL]) VLC_ADD_LDFLAGS([macosx], [-Wl,-framework,IOKit]) VLC_ADD_LDFLAGS([macosx], [-Wl,-framework,QuickTime]) - VLC_ADD_LDFLAGS([macosx], [-Wl,-framework,QTKit]) + VLC_ADD_LDFLAGS([macosx qtcapture], [-Wl,-framework,QTKit]) VLC_ADD_LDFLAGS([macosx], [-Wl,-framework,WebKit]) - VLC_ADD_LDFLAGS([opengllayer], [-Wl,-framework,QuartzCore]) + VLC_ADD_LDFLAGS([opengllayer qtcapture], [-Wl,-framework,QuartzCore]) + VLC_ADD_LDFLAGS([qtcapture], [-Wl,-framework,CoreVideo]) VLC_ADD_OBJCFLAGS([macosx minimal_macosx opengllayer growl], [-fobjc-exceptions] ) # VLC_ADD_PLUGIN([access_eyetv]) + VLC_ADD_PLUGIN([qtcapture]) VLC_ADD_PLUGIN([macosx]) VLC_ADD_PLUGIN([minimal_macosx]) diff --git a/modules/access/Modules.am b/modules/access/Modules.am index 6cc515d3e0..c91bfd0bcf 100644 --- a/modules/access/Modules.am +++ b/modules/access/Modules.am @@ -29,6 +29,7 @@ SOURCES_dc1394 = dc1394.c SOURCES_access_fake = fake.c SOURCES_pvr = pvr.c videodev2.h SOURCES_v4l = v4l.c videodev_mjpeg.h +SOURCES_qtcapture = qtcapture.m SOURCES_cdda = \ cdda.c \ vcd/cdrom.c \ diff --git a/modules/access/qtcapture.m b/modules/access/qtcapture.m new file mode 100644 index 0000000000..2cad815d2a --- /dev/null +++ b/modules/access/qtcapture.m @@ -0,0 +1,362 @@ +/***************************************************************************** +* qtcapture.m: qtkit (Mac OS X) based capture module +***************************************************************************** +* Copyright (C) 2008 the VideoLAN team +* +* Authors: Pierre d'Herbemont +* +***************************************************************************** +* 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 + +#include +#include +#include +#include +#include + +#import + +/***************************************************************************** +* 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" ); + set_capability( "access_demux", 10 ); + set_callbacks( Open, Close ); +vlc_module_end(); + + +/***************************************************************************** +* QTKit Bridge +*****************************************************************************/ +@interface VLCDecompressedVideoOutput : QTCaptureDecompressedVideoOutput +{ + CVImageBufferRef currentImageBuffer; +} +- (id)init; +- (void)outputVideoFrame:(CVImageBufferRef)videoFrame withSampleBuffer:(QTSampleBuffer *)sampleBuffer fromConnection:(QTCaptureConnection *)connection; +- (BOOL)copyCurrentFrameToBuffer:(void *)buffer; +@end + +/* Apple sample code */ +@implementation VLCDecompressedVideoOutput : QTCaptureDecompressedVideoOutput +- (id)init +{ + if( self = [super init] ) + { + currentImageBuffer = nil; + } + return self; +} +- (void)dealloc +{ + @synchronized (self) { + CVBufferRelease(currentImageBuffer); + currentImageBuffer = nil; + } + [super dealloc]; +} + +- (void)outputVideoFrame:(CVImageBufferRef)videoFrame withSampleBuffer:(QTSampleBuffer *)sampleBuffer fromConnection:(QTCaptureConnection *)connection +{ + // 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; + + CVBufferRetain(videoFrame); + + @synchronized (self) { + imageBufferToRelease = currentImageBuffer; + currentImageBuffer = videoFrame; + } + CVBufferRelease(imageBufferToRelease); +} + +- (BOOL)copyCurrentFrameToBuffer:(void *)buffer +{ + CVImageBufferRef imageBuffer; + + @synchronized (self) { + if(!currentImageBuffer) return NO; + imageBuffer = CVBufferRetain(currentImageBuffer); + } + + CVPixelBufferLockBaseAddress(imageBuffer, 0); + void * pixels = CVPixelBufferGetBaseAddress(imageBuffer); + memcpy( buffer, pixels, CVPixelBufferGetBytesPerRow(imageBuffer) * CVPixelBufferGetHeight(imageBuffer) ); + CVPixelBufferUnlockBaseAddress(imageBuffer, 0); + + CVBufferRelease(imageBuffer); + + return YES; +} + +@end + +struct demux_sys_t { + QTCaptureSession * session; + VLCDecompressedVideoOutput * output; + int height, width; + es_out_id_t * p_es_video; +}; + + +int qtchroma_to_fourcc( int i_qt ) +{ + 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; + + /* 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; + + NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; + + msg_Dbg( p_demux, "QTCapture Probed" ); + + 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 ) ); + + QTCaptureDeviceInput * input = nil; + QTCaptureSession * session = nil; + VLCDecompressedVideoOutput * output = nil; + + QTCaptureDevice * device = [QTCaptureDevice defaultInputDeviceWithMediaType: QTMediaTypeVideo]; + if( !device ) + { + msg_Err( p_demux, "Can't open any Video device" ); + goto error; + } + + if( ![device open: nil /* FIXME */] ) + { + msg_Err( p_demux, "Can't open any Video device" ); + goto error; + } + + input = [[QTCaptureDeviceInput alloc] initWithDevice: device]; + if( !device ) + { + msg_Err( p_demux, "Can't create a capture session" ); + goto error; + } + + output = [[VLCDecompressedVideoOutput alloc] init]; + + session = [[QTCaptureSession alloc] init]; + + bool ret = [session addInput:input error:nil /* FIXME */]; + if( !ret ) + { + msg_Err( p_demux, "Can't add the video device as input" ); + goto error; + } + + ret = [session addOutput:output error:nil /* FIXME */]; + if( !ret ) + { + msg_Err( p_demux, "Can't get any output output" ); + goto error; + } + + [session startRunning]; + + int qtchroma = [[[device formatDescriptions] objectAtIndex: 0] formatType]; /* FIXME */ + int chroma = qtchroma_to_fourcc( qtchroma ); + if( !chroma ) + { + msg_Err( p_demux, "Unknown qt chroma %4.4s provided by camera", (char*)&qtchroma ); + goto error; + } + + es_format_Init( &fmt, VIDEO_ES, chroma ); + + NSSize size = [[device attributeForKey:QTFormatDescriptionVideoEncodedPixelsSizeAttribute] sizeValue]; + p_sys->width = fmt.video.i_width = 640;/* size.width; FIXME */ + p_sys->height = fmt.video.i_height = 480;/* size.height; FIXME */ + + msg_Err( p_demux, "added new video es %4.4s %dx%d", + (char*)&fmt.i_codec, fmt.video.i_width, fmt.video.i_height ); + + p_sys->p_es_video = es_out_Add( p_demux->out, &fmt ); + + p_sys->output = [output retain]; + p_sys->session = [session retain]; + + [input release]; + [output release]; + [session release]; + [pool release]; + + msg_Dbg( p_demux, "QTCapture: We have a video device ready!" ); + + return VLC_SUCCESS; +error: + [input release]; + [session release]; + [input release]; + [output 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; + [p_sys->output release]; + [p_sys->session release]; + 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]; + + if( ![p_sys->output copyCurrentFrameToBuffer: p_block->p_buffer] ) + { + /* Nothing to display yet, just forget */ + block_Release( p_block ); + [pool release]; + return 1; + } + + p_block->i_pts = mdate(); /* FIXME */ + + 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; +}