source: mediastreamer2/src/qtcapture.m @ 1367:eb5d88eb5a20

Last change on this file since 1367:eb5d88eb5a20 was 1367:eb5d88eb5a20, checked in by Guillaume Beraudo <guillaume.beraudo@…>, 2 years ago

First version: non plannar, SDL 1.3 HG, FFMPEG GIT LGPL.

  • Latest SDL 1.3 required (fix crashes on setVideoMode)
  • Latest FFMPEG required (previous ones doesn't compile with SDL HG)
  • Plannar captured images under investigation.
File size: 11.0 KB
Line 
1#ifdef __APPLE__
2
3#include "mediastreamer-config.h"
4#include "mediastreamer2/msvideo.h"
5#include "mediastreamer2/msticker.h"
6#include "mediastreamer2/msv4l.h"
7#include "mediastreamer2/mswebcam.h"
8#include "nowebcam.h"
9
10#import <QTKit/QTKit.h>
11
12struct v4mState;
13
14static MSPixFmt ostype_to_pix_fmt(OSType pixelFormat, bool printFmtName){
15        // ms_message("OSType= %i", pixelFormat);
16        switch(pixelFormat){
17                case kCVPixelFormatType_420YpCbCr8Planar:
18                        if (printFmtName) ms_message("FORMAT = MS_YUV420P");
19                        return MS_YUV420P;
20                case kYUVSPixelFormat:
21                        if (printFmtName) ms_message("FORMAT = MS_YUY2");
22                        return MS_YUY2;
23                case kUYVY422PixelFormat:
24                        if (printFmtName) ms_message("FORMAT = MS_UYVY");
25                        return MS_UYVY;
26                case k32RGBAPixelFormat:
27                        if (printFmtName) ms_message("FORMAT = MS_RGBA32");
28                        return MS_RGBA32;
29                default:
30                        if (printFmtName) ms_message("Format unknown: %i", (UInt32) pixelFormat);
31                        return MS_PIX_FMT_UNKNOWN;
32        }
33}
34
35@interface NsMsWebCam :NSObject
36{
37        NSAutoreleasePool *globalPool;
38        QTCaptureDeviceInput *input;
39        QTCaptureDecompressedVideoOutput * output;
40        QTCaptureSession *session;
41       
42        ms_mutex_t mutex;
43        queue_t rq;
44};
45
46-(id) init;
47-(void) dealloc;
48-(int) start;
49-(int) stop;
50-(void) setSize:(MSVideoSize) size;
51-(MSVideoSize) getSize;
52-(void) setName:(char*) name;
53-(int) getPixFmt;
54
55-(QTCaptureSession *) session;
56-(queue_t*) rq;
57-(ms_mutex_t *) mutex;
58
59@end
60
61
62
63
64@implementation NsMsWebCam
65
66- (void)captureOutput:(QTCaptureOutput *)captureOutput didOutputVideoFrame:(CVImageBufferRef)frame withSampleBuffer:(QTSampleBuffer *)sampleBuffer fromConnection:(QTCaptureConnection *)connection
67{
68        NSAutoreleasePool* myPool = [[NSAutoreleasePool alloc] init];
69        ms_mutex_lock(&mutex); 
70
71        CVReturn status = CVPixelBufferLockBaseAddress(frame, 0);
72        if (kCVReturnSuccess != status) {
73                ms_error("Error locking base address: %i", status);
74                return;
75        }
76
77        OSType pixelFormat = CVPixelBufferGetPixelFormatType(frame);
78        MSPixFmt msfmt = ostype_to_pix_fmt(pixelFormat, false);
79
80        if (CVPixelBufferIsPlanar(frame)) {
81                size_t numberOfPlanes = CVPixelBufferGetPlaneCount(frame);
82                MSPicture pict;
83                size_t w = CVPixelBufferGetWidth(frame);
84                size_t h = CVPixelBufferGetHeight(frame);
85                mblk_t *yuv_block = ms_yuv_buf_alloc(&pict, w, h);
86
87                //memset(pict.planes[0], 0, (w*h*3)/2);
88                int p;
89                for (p=0; p < numberOfPlanes; p++) {
90                        size_t fullrow_width = CVPixelBufferGetBytesPerRowOfPlane(frame, p);
91                        size_t plane_width = CVPixelBufferGetWidthOfPlane(frame, p);
92                        size_t plane_height = CVPixelBufferGetHeightOfPlane(frame, p);
93                        uint8_t *dst_plane = pict.planes[p];
94                        uint8_t *src_plane = CVPixelBufferGetBaseAddressOfPlane(frame, p);
95                        ms_message("CVPixelBuffer %ix%i; Plane %i %ix%i (%i)", w, h, p, plane_width, plane_height, fullrow_width);
96                        int l;
97                        for (l=0; l<plane_height; l++) {
98                                memcpy(dst_plane, src_plane, plane_width);
99                                src_plane += fullrow_width;
100                                dst_plane += plane_width;
101                        }
102                }
103                putq(&rq, yuv_block);
104        } else {
105                // Buffer doesn't contain a plannar image.
106                uint8_t * data = (uint8_t *)[sampleBuffer bytesForAllSamples];
107                int size = [sampleBuffer lengthForAllSamples];
108                mblk_t *buf=allocb(size,0);
109                memcpy(buf->b_wptr, data, size);
110                buf->b_wptr+=size;
111                putq(&rq, buf);
112        }
113
114
115        CVPixelBufferUnlockBaseAddress(frame, 0);
116        ms_mutex_unlock(&mutex);
117
118        [myPool drain];
119}
120
121-(id) init {
122        qinit(&rq);
123        ms_mutex_init(&mutex,NULL);
124       
125        globalPool = [[NSAutoreleasePool alloc] init];
126        session = [[QTCaptureSession alloc] init];
127        output = [[QTCaptureDecompressedVideoOutput alloc] init];
128        [output automaticallyDropsLateVideoFrames];
129        [output setDelegate: self];
130       
131       
132        return self;
133}
134
135-(void) dealloc {
136        [self stop];
137       
138        if(session)
139        {               
140                [session release];
141                session = nil;
142        }
143               
144        if(input)
145        {
146                [input release];
147                input = nil;
148        }
149       
150        if(output)
151        {
152                [output release];
153                output = nil;
154        }
155       
156        flushq(&rq,0);
157
158        [globalPool drain];
159        ms_mutex_destroy(&mutex);
160
161        [super dealloc];
162}
163
164-(int) start {
165       
166        [session startRunning];
167       
168        return 0;
169}
170
171-(int) stop {
172       
173        [session stopRunning];
174       
175        return 0;
176}
177
178-(int) getPixFmt{
179       
180        QTCaptureDevice *device = [input device];
181        if([device isOpen]) {   
182                NSArray * array = [device formatDescriptions];
183       
184                NSEnumerator *enumerator = [array objectEnumerator];
185                QTFormatDescription *desc;
186                while ((desc = [enumerator nextObject])) {
187                        if ([desc mediaType] == QTMediaTypeVideo) {
188                                UInt32 fmt = [desc formatType];
189                                MSPixFmt format = ostype_to_pix_fmt(fmt, true);
190                                if (format != MS_PIX_FMT_UNKNOWN) return format;
191                        }
192                }
193        } else {
194                ms_warning("The camera wasn't opened");
195        }
196        ms_warning("No format found, using MS_YUV420P pixel format");
197        return MS_YUV420P;
198}
199
200-(void) setName:(char*) name
201{
202        NSError *error = nil;
203        unsigned int i = 0;
204         
205        QTCaptureDevice * device = [QTCaptureDevice defaultInputDeviceWithMediaType:QTMediaTypeVideo];
206       
207        if(name != nil)
208        {
209                NSArray * array = [QTCaptureDevice inputDevicesWithMediaType:QTMediaTypeVideo];
210         
211                for(i = 0 ; i < [array count]; i++)
212                {
213                        QTCaptureDevice * deviceTmp = [array objectAtIndex:i];
214                        if(!strcmp([[deviceTmp localizedDisplayName] UTF8String], name))
215                        {
216                                device = deviceTmp;
217                                break;
218                        }
219                }
220        }
221       
222        bool success = [device open:&error];
223        if (success) ms_message("Device opened");
224        else {
225                ms_error("%s", [[error localizedDescription] UTF8String]);
226                return;
227        }
228
229        input = [[QTCaptureDeviceInput alloc] initWithDevice:device];
230       
231        success = [session addInput:input error:&error];
232        if (success) ms_message("Input added to session");
233        else ms_error("%s", [[error localizedDescription] UTF8String]);
234       
235
236        success = [session addOutput:output error:&error];
237        if (success) ms_message("Output added to session");
238        else ms_error("%s", [[error localizedDescription] UTF8String]);
239}
240
241-(void) setSize:(MSVideoSize) size
242{       
243        ms_message("Set size w=%i, h=%i", size.width, size.height);
244        NSDictionary * dic = [NSDictionary dictionaryWithObjectsAndKeys:
245         [NSNumber numberWithInteger:size.width], (id)kCVPixelBufferWidthKey,
246         [NSNumber numberWithInteger:size.height],(id)kCVPixelBufferHeightKey,
247//       [NSNumber numberWithInteger:kCVPixelFormatType_420YpCbCr8Planar], (id)kCVPixelBufferPixelFormatTypeKey, // force pixel format to plannar
248                                                  nil];
249       
250        [output setPixelBufferAttributes:dic];
251}
252
253-(MSVideoSize) getSize
254{
255        MSVideoSize size;
256       
257        size.width = MS_VIDEO_SIZE_QCIF_W;
258        size.height = MS_VIDEO_SIZE_QCIF_H;
259       
260        if(output)
261        {
262                NSDictionary * dic = [output pixelBufferAttributes];
263               
264                size.width = [[dic objectForKey:(id)kCVPixelBufferWidthKey] integerValue];
265                size.height = [[dic objectForKey:(id)kCVPixelBufferHeightKey] integerValue];
266        }
267                ms_message("get size w=%i, h=%i", size.width, size.height);
268        return size;
269}
270
271-(QTCaptureSession *) session
272{
273        return  session;
274}
275
276-(queue_t*) rq
277{
278        return &rq;
279}
280
281-(ms_mutex_t *) mutex
282{
283        return &mutex;
284}
285
286@end
287
288typedef struct v4mState{
289        NsMsWebCam * webcam;
290        int frame_ind;
291        float fps;
292        float start_time;
293        int frame_count;
294}v4mState;
295
296
297
298static void v4m_init(MSFilter *f){
299        v4mState *s=ms_new0(v4mState,1);
300        s->webcam= [[NsMsWebCam alloc] init];
301//      [s->webcam retain];
302        s->start_time=0;
303        s->frame_count=-1;
304        s->fps=15;
305        f->data=s;
306}
307
308static int v4m_start(MSFilter *f, void *arg) {
309        v4mState *s=(v4mState*)f->data;
310        [s->webcam start];
311        ms_message("v4m video device opened.");
312        return 0;
313}
314
315static int v4m_stop(MSFilter *f, void *arg){
316        v4mState *s=(v4mState*)f->data;
317        [s->webcam stop];
318        ms_message("v4m video device closed.");
319       
320        return 0;
321}
322
323static void v4m_uninit(MSFilter *f){
324        v4mState *s=(v4mState*)f->data;
325        v4m_stop(f,NULL);
326       
327        [s->webcam release];
328        ms_free(s);
329}
330
331static void v4m_process(MSFilter * obj){
332        v4mState *s=(v4mState*)obj->data;
333        uint32_t timestamp;
334        int cur_frame;
335        if (s->frame_count==-1){
336                s->start_time=obj->ticker->time;
337                s->frame_count=0;
338        }
339
340        ms_mutex_lock([s->webcam mutex]);
341
342        cur_frame=((obj->ticker->time-s->start_time)*s->fps/1000.0);
343        if (cur_frame>=s->frame_count)
344        {
345                mblk_t *om=NULL;
346                /*keep the most recent frame if several frames have been captured */
347                if ([[s->webcam session] isRunning])
348                {
349                        om=getq([s->webcam rq]);
350                }
351
352                if (om!=NULL)
353                {
354                        timestamp=obj->ticker->time*90;/* rtp uses a 90000 Hz clockrate for video*/
355                        mblk_set_timestamp_info(om,timestamp);
356                        mblk_set_marker_info(om,TRUE); 
357                        ms_queue_put(obj->outputs[0],om);
358                        s->frame_count++;
359                }
360        }
361        else
362                flushq([s->webcam rq],0);
363
364        ms_mutex_unlock([s->webcam mutex]);
365}
366
367static void v4m_preprocess(MSFilter *f){
368        v4m_start(f,NULL);
369       
370}
371
372static void v4m_postprocess(MSFilter *f){
373        v4m_stop(f,NULL);
374}
375
376static int v4m_set_fps(MSFilter *f, void *arg){
377        v4mState *s=(v4mState*)f->data;
378        s->fps=*((float*)arg);
379        s->frame_count=-1;
380        return 0;
381}
382
383static int v4m_get_pix_fmt(MSFilter *f,void *arg){
384        v4mState *s=(v4mState*)f->data;
385        *((MSPixFmt*)arg) = [s->webcam getPixFmt];
386        return 0;
387}
388
389static int v4m_set_vsize(MSFilter *f, void *arg){
390        v4mState *s=(v4mState*)f->data;
391        [s->webcam setSize:*((MSVideoSize*)arg)];
392        return 0;
393}
394
395static int v4m_get_vsize(MSFilter *f, void *arg){
396        v4mState *s=(v4mState*)f->data;
397        *(MSVideoSize*)arg = [s->webcam getSize];
398        return 0;
399}
400
401static MSFilterMethod methods[]={
402        {       MS_FILTER_SET_FPS               ,       v4m_set_fps             },
403        {       MS_FILTER_GET_PIX_FMT   ,       v4m_get_pix_fmt },
404        {       MS_FILTER_SET_VIDEO_SIZE,       v4m_set_vsize   },
405        {       MS_V4L_START                    ,       v4m_start               },
406        {       MS_V4L_STOP                             ,       v4m_stop                },
407        {       MS_FILTER_GET_VIDEO_SIZE,       v4m_get_vsize   },
408        {       0                                               ,       NULL                    }
409};
410
411MSFilterDesc ms_v4m_desc={
412        .id=MS_V4L_ID,
413        .name="MSV4m",
414        .text="A video for macosx compatible source filter to stream pictures.",
415        .ninputs=0,
416        .noutputs=1,
417        .category=MS_FILTER_OTHER,
418        .init=v4m_init,
419        .preprocess=v4m_preprocess,
420        .process=v4m_process,
421        .postprocess=v4m_postprocess,
422        .uninit=v4m_uninit,
423        .methods=methods
424};
425
426MS_FILTER_DESC_EXPORT(ms_v4m_desc)
427       
428static void ms_v4m_detect(MSWebCamManager *obj);
429
430static void ms_v4m_cam_init(MSWebCam *cam)
431{
432}
433
434static int v4m_set_device(MSFilter *f, void *arg)
435{       
436        v4mState *s=(v4mState*)f->data;
437        return 0;
438}
439
440static int v4m_set_name(MSFilter *f, void *arg){
441       
442        v4mState *s=(v4mState*)f->data;
443        [s->webcam setName:(char*)arg];
444
445        return 0;
446}
447
448static MSFilter *ms_v4m_create_reader(MSWebCam *obj)
449{       
450        MSFilter *f= ms_filter_new_from_desc(&ms_v4m_desc);
451       
452        v4m_set_device(f,obj->id);
453        v4m_set_name(f,obj->data);
454       
455        return f;
456}
457
458MSWebCamDesc ms_v4m_cam_desc={
459        "VideoForMac grabber",
460        &ms_v4m_detect,
461        &ms_v4m_cam_init,
462        &ms_v4m_create_reader,
463        NULL
464};
465
466
467static void ms_v4m_detect(MSWebCamManager *obj){
468 
469        unsigned int i = 0;
470        NSAutoreleasePool* myPool = [[NSAutoreleasePool alloc] init];
471       
472        NSArray * array = [QTCaptureDevice inputDevicesWithMediaType:QTMediaTypeVideo];
473       
474        for(i = 0 ; i < [array count]; i++)
475        {
476                QTCaptureDevice * device = [array objectAtIndex:i];
477                MSWebCam *cam=ms_web_cam_new(&ms_v4m_cam_desc);
478               
479                cam->name= ms_strdup([[device localizedDisplayName] UTF8String]);
480                cam->id = ms_strdup([[device uniqueID] UTF8String]);
481                cam->data = NULL;
482                ms_web_cam_manager_add_cam(obj,cam);
483        }
484        [myPool drain];
485}
486       
487#endif       
Note: See TracBrowser for help on using the repository browser.