]> sjero.net Git - linphone/blob - linphone/mediastreamer2/src/msv4m.m
remote ortp and add it as a submodule instead.
[linphone] / linphone / mediastreamer2 / src / msv4m.m
1 /*
2 mediastreamer2 library - modular sound and video processing and streaming
3 Copyright (C) 2006  Simon MORLAT (simon.morlat@linphone.org)
4
5 This program is free software; you can redistribute it and/or
6 modify it under the terms of the GNU General Public License
7 as published by the Free Software Foundation; either version 2
8 of the License, or (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
18 */
19
20 #ifdef __APPLE__
21
22 #include "mediastreamer-config.h"
23
24 #include <fcntl.h>
25 #include <unistd.h>
26 #include <sys/stat.h>
27 #include <sys/types.h>
28 #include <sys/ioctl.h>
29 #include <errno.h>
30 #include <string.h>
31
32 #include "mediastreamer2/msvideo.h"
33 #include "mediastreamer2/msticker.h"
34 #include "mediastreamer2/msv4l.h"
35 #include "nowebcam.h"
36 #include "mediastreamer2/mswebcam.h"
37
38 // build for carbon
39 #define TARGET_API_MAC_CARBON 1
40
41 #if __APPLE_CC__
42   #include <Carbon/Carbon.h>
43   #include <QuicKTime/QuickTime.h>
44   #include <AppKit/AppKit.h>
45 #else
46   #include <ConditionalMacros.h>
47   #include <QuickTimeComponents.h>
48   #include <TextUtils.h>
49   #include <AppKit/AppKit.h>
50   #include <stdio.h>
51 #endif
52
53 typedef struct v4mState{
54   char * name;
55   char * id;        
56   SeqGrabComponent seqgrab;
57   SGChannel sgchanvideo;
58   GWorldPtr pgworld;
59   ImageSequence   decomseq;
60
61   char *mmapdbuf;
62   int msize;/*mmapped size*/
63   MSVideoSize vsize;
64   MSVideoSize got_vsize;
65   int pix_fmt;
66   int int_pix_fmt; /*internal pixel format */
67   mblk_t *mire;
68   queue_t rq;
69   ms_mutex_t mutex;
70   int frame_ind;
71   int frame_max;
72   float fps;
73   float start_time;
74   int frame_count;
75   int queued;
76   bool_t run;
77   bool_t usemire;
78 }v4mState;
79
80 static void v4m_init(MSFilter *f){
81         v4mState *s=ms_new0(v4mState,1);
82         s->seqgrab=NULL;
83         s->sgchanvideo=NULL;
84         s->pgworld=NULL;
85         s->decomseq=0;
86
87         s->run=FALSE;
88         s->mmapdbuf=NULL;
89         s->vsize.width=MS_VIDEO_SIZE_CIF_W;
90         s->vsize.height=MS_VIDEO_SIZE_CIF_H;
91         s->pix_fmt=MS_RGB24;
92         qinit(&s->rq);
93         s->mire=NULL;
94         ms_mutex_init(&s->mutex,NULL);
95         s->start_time=0;
96         s->frame_count=-1;
97         s->fps=15;
98         s->usemire=(getenv("DEBUG")!=NULL);
99         s->queued=0;
100         f->data=s;
101 }
102
103
104 #define BailErr(x) {err = x; if(err != noErr) goto bail;}
105
106 pascal OSErr sgdata_callback(SGChannel c, Ptr p, long len, long *offset, long chRefCon, TimeValue time, short writeType, long refCon);
107 pascal OSErr sgdata_callback(SGChannel c, Ptr p, long len, long *offset, long chRefCon, TimeValue time, short writeType, long refCon)
108 {
109 #pragma unused(offset,chRefCon,time,writeType)
110     
111     CodecFlags     ignore;
112     v4mState *s=(v4mState *)refCon;
113     ComponentResult err = noErr;
114     
115     if (!s) goto bail;
116    
117     Rect boundsRect = {0, 0, s->vsize.height, s->vsize.width}; /* 240 , 320*/
118     if (s->pgworld) {
119
120       if (s->decomseq == 0) {
121         Rect sourceRect = { 0, 0 };
122         MatrixRecord scaleMatrix;
123         ImageDescriptionHandle imageDesc = (ImageDescriptionHandle)NewHandle(0);
124         
125         err = SGGetChannelSampleDescription(c,(Handle)imageDesc);
126         BailErr(err);
127         
128         // make a scaling matrix for the sequence
129         sourceRect.right = (**imageDesc).width;
130         sourceRect.bottom = (**imageDesc).height;
131         RectMatrix(&scaleMatrix, &sourceRect, &boundsRect);
132             
133         err = DecompressSequenceBegin(&s->decomseq,  // pointer to field to receive unique ID for sequence
134                                       imageDesc,        // handle to image description structure
135                                       s->pgworld,    // port for the DESTINATION image
136                                       NULL,            // graphics device handle, if port is set, set to NULL
137                                       NULL,            // source rectangle defining the portion of the image to decompress
138                                       &scaleMatrix,        // transformation matrix
139                                       srcCopy,          // transfer mode specifier
140                                       NULL,            // clipping region in dest. coordinate system to use as a mask
141                                       0,            // flags
142                                       codecNormalQuality,    // accuracy in decompression
143                                       bestSpeedCodec);      // compressor identifier or special identifiers ie. bestSpeedCodec
144         BailErr(err);
145         
146         DisposeHandle((Handle)imageDesc);
147         imageDesc = NULL;
148       }
149       
150       // decompress a frame into the GWorld - can queue a frame for async decompression when passed in a completion proc
151       // once the image is in the GWorld it can be manipulated at will
152       err = DecompressSequenceFrameS(s->decomseq,  // sequence ID returned by DecompressSequenceBegin
153                                      p,            // pointer to compressed image data
154                                      len,          // size of the buffer
155                                      0,            // in flags
156                                      &ignore,        // out flags
157                                      NULL);          // async completion proc
158         BailErr(err);
159         
160     {
161       unsigned line;
162       mblk_t *buf;
163       int size = s->vsize.width * s->vsize.height * 3;
164       buf=allocb(size,0);
165       
166       PixMap * pixmap = *GetGWorldPixMap(s->pgworld);
167       uint8_t * data;
168       unsigned rowBytes = pixmap->rowBytes & (((unsigned short) 0xFFFF) >> 2);
169       unsigned pixelSize = pixmap->pixelSize / 8; // Pixel size in bytes
170       unsigned lineOffset = rowBytes - s->vsize.width * pixelSize;
171       
172       data = (uint8_t *) GetPixBaseAddr(GetGWorldPixMap(s->pgworld));
173       
174       for (line = 0 ; line < s->vsize.height ; line++) {
175         unsigned offset = line * (s->vsize.width * pixelSize + lineOffset);
176         memcpy(buf->b_wptr + ((line * s->vsize.width) * pixelSize), data + offset, (rowBytes - lineOffset));
177       }
178
179       if (s->pix_fmt==MS_RGB24)
180         {
181           /* Conversion from top down bottom up (BGR to RGB and flip) */
182           unsigned long Index,nPixels;
183           unsigned char *blue;
184           unsigned char tmp;
185           short iPixelSize;
186
187           blue=buf->b_wptr;
188
189           nPixels=s->vsize.width*s->vsize.height;
190           iPixelSize=24/8;
191
192           for(Index=0;Index!=nPixels;Index++)  // For each pixel
193             {
194               tmp=*blue;
195               *blue=*(blue+2);
196               *(blue+2)=tmp;
197               blue+=iPixelSize;
198             }
199         }
200
201       buf->b_wptr+=size;
202       //ms_mutex_lock(&s->mutex); /* called during SGIdle? */
203       putq(&s->rq, buf);
204       //ms_mutex_unlock(&s->mutex);
205     }
206   }
207
208 bail:
209   return err;
210 }
211
212 static int v4m_close(v4mState *s)
213 {
214   if(s->seqgrab)
215     CloseComponent(s->seqgrab);
216   s->seqgrab=NULL;
217   if (s->decomseq)
218     CDSequenceEnd(s->decomseq);
219   s->decomseq=0;
220   if (s->pgworld!=NULL)
221     DisposeGWorld(s->pgworld);
222   s->pgworld=NULL;
223   return 0;
224 }
225 unsigned char *stdToPascalString(char *buffer, char * str) {
226         if (strlen(str) <= 255) {
227                 buffer[0] = strlen(str);
228
229                 memcpy(buffer + 1, str, strlen(str));
230
231                 return (unsigned char*)buffer;
232         } else {
233                 return NULL;
234         }
235 }
236
237 static int sequence_grabber_start(v4mState *s)
238 {
239    OSErr err = noErr;
240    char *camName;
241
242    ms_warning("Opening component");
243         s->seqgrab = OpenDefaultComponent(SeqGrabComponentType, 0);
244         if (s->seqgrab == NULL) {
245                 ms_warning("can't get default sequence grabber component");
246                 return -1;
247         }
248
249         ms_warning("Initializing component");
250         err = SGInitialize(s->seqgrab);
251         if (err != noErr) {
252                 ms_warning("can't initialize sequence grabber component");
253                 return -1;
254         }
255
256         ms_warning("SetDataRef");
257         err = SGSetDataRef(s->seqgrab, 0, 0, seqGrabDontMakeMovie);
258         if (err != noErr) {
259                 ms_warning("can't set the destination data reference");
260                 return -1;
261         }
262
263         ms_warning("Creating new channel");
264         err = SGNewChannel(s->seqgrab, VideoMediaType, &s->sgchanvideo);
265         if (err != noErr) {
266                 ms_warning("can't create a video channel");
267                 return -1;
268         }
269     
270         
271         camName = alloca(strlen(s->name) + 1);
272
273     err = SGSetChannelDevice(s->sgchanvideo, stdToPascalString(camName, s->name));
274     if (err != noErr) {
275         ms_warning("can't set channel device");
276         return -1;
277     }
278
279     short input = atoi(s->id);
280     
281     err = SGSetChannelDeviceInput(s->sgchanvideo,input);
282     if (err != noErr) {
283         ms_warning("can't set channel device input");
284         return -1;
285     }
286     
287     ms_warning("createGWorld");
288         Rect        theRect = {0, 0, s->vsize.height, s->vsize.width};
289
290   err = QTNewGWorld(&(s->pgworld),  // returned GWorld
291                     k24BGRPixelFormat,
292                     &theRect,      // bounding rectangle
293                     0,             // color table
294                     NULL,          // graphic device handle
295                     0);            // flags
296   if (err!=noErr)
297     {
298       return -1;
299     }
300
301   if(!LockPixels(GetPortPixMap(s->pgworld)))
302     {
303       v4m_close(s);
304       return -1;
305     }
306   
307   err = SGSetGWorld(s->seqgrab, s->pgworld, GetMainDevice());
308         if (err != noErr) {
309                 ms_warning("can't set GWorld");
310                 return -1;
311         }
312
313         ms_warning("SGSetDataProc");
314         err = SGSetDataProc(s->seqgrab,NewSGDataUPP(sgdata_callback),(long)s);
315         if (err != noErr) {
316                 ms_warning("can't set data proc");
317                 return -1;
318         }
319
320         ms_warning("SGSetChannelUsage");
321         err = SGSetChannelUsage(s->sgchanvideo, seqGrabRecord);
322         if (err != noErr) {
323                 ms_warning("can't set channel usage");
324                 return -1;
325         }
326
327         ms_warning("SGPrepare");
328         err = SGPrepare(s->seqgrab,  false, true);
329         if (err != noErr) {
330                 ms_warning("can't prepare sequence grabber component");
331                 return -1;
332         }
333      
334   err = SGSetChannelBounds(s->sgchanvideo, &theRect);
335   if (err!=noErr)
336     {
337       v4m_close(s);
338       return -1;
339     }
340
341   err = SGStartRecord(s->seqgrab);
342   if (err!=noErr)
343     {
344       v4m_close(s);
345       return -1;
346     }
347
348   return 0;
349 }
350
351 static int v4m_start(MSFilter *f, void *arg)
352 {
353         v4mState *s=(v4mState*)f->data;
354         int err=0;
355
356         err = sequence_grabber_start(s);
357
358         if (err!=0)
359           {
360             s->pix_fmt=MS_YUV420P;
361             s->vsize.width=MS_VIDEO_SIZE_CIF_W;
362             s->vsize.height=MS_VIDEO_SIZE_CIF_H;
363             return 0;
364           }
365
366         ms_message("v4m video device opened.");
367         s->pix_fmt=MS_RGB24;
368
369         return 0;
370 }
371
372 static void v4m_start_capture(v4mState *s){
373         if (s->seqgrab!=NULL){
374                 s->run=TRUE;
375         }
376 }
377
378 static int v4m_stop(MSFilter *f, void *arg){
379         v4mState *s=(v4mState*)f->data;
380         if (s->seqgrab!=NULL){
381           ms_mutex_lock(&s->mutex);
382           SGStop(s->seqgrab);
383           v4m_close(s);
384           flushq(&s->rq,0);
385           ms_mutex_unlock(&s->mutex);
386         }
387         return 0;
388 }
389
390 static void v4m_stop_capture(v4mState *s){
391         if (s->run){
392                 s->run=FALSE;
393                 ms_message("v4m capture stopped.");
394         }
395 }
396
397
398 static void v4m_uninit(MSFilter *f){
399         v4mState *s=(v4mState*)f->data;
400         if (s->seqgrab!=NULL) v4m_stop(f,NULL);
401         //ms_free(s->dev);
402         flushq(&s->rq,0);
403         ms_mutex_destroy(&s->mutex);
404         freemsg(s->mire);
405         ms_free(s);
406 }
407
408 static mblk_t * v4m_make_mire(v4mState *s){
409         unsigned char *data;
410         int i,j,line,pos;
411         int patternw=s->vsize.width/6; 
412         int patternh=s->vsize.height/6;
413         int red,green=0,blue=0;
414         if (s->mire==NULL){
415                 s->mire=allocb(s->vsize.width*s->vsize.height*3,0);
416                 s->mire->b_wptr=s->mire->b_datap->db_lim;
417         }
418         data=s->mire->b_rptr;
419         for (i=0;i<s->vsize.height;++i){
420                 line=i*s->vsize.width*3;
421                 if ( ((i+s->frame_ind)/patternh) & 0x1) red=255;
422                 else red= 0;
423                 for (j=0;j<s->vsize.width;++j){
424                         pos=line+(j*3);
425                         
426                         if ( ((j+s->frame_ind)/patternw) & 0x1) blue=255;
427                         else blue= 0;
428                         
429                         data[pos]=red;
430                         data[pos+1]=green;
431                         data[pos+2]=blue;
432                 }
433         }
434         s->frame_ind++;
435         return s->mire;
436 }
437
438 static mblk_t * v4m_make_nowebcam(v4mState *s){
439         if (s->mire==NULL && s->frame_ind==0){
440                 s->mire=ms_load_nowebcam(&s->vsize, -1);
441         }
442         s->frame_ind++;
443         return s->mire;
444 }
445
446 static void v4m_process(MSFilter * obj){
447         v4mState *s=(v4mState*)obj->data;
448         uint32_t timestamp;
449         int cur_frame;
450         if (s->frame_count==-1){
451                 s->start_time=obj->ticker->time;
452                 s->frame_count=0;
453         }
454
455         ms_mutex_lock(&s->mutex);
456            
457         if (s->seqgrab!=NULL)
458         {
459           SGIdle(s->seqgrab);
460         }
461
462         cur_frame=((obj->ticker->time-s->start_time)*s->fps/1000.0);
463         if (cur_frame>=s->frame_count){
464                 mblk_t *om=NULL;
465                 /*keep the most recent frame if several frames have been captured */
466                 if (s->seqgrab!=NULL){
467                         om=getq(&s->rq);
468                 }else{
469                   if (s->pix_fmt==MS_YUV420P
470                       && s->vsize.width==MS_VIDEO_SIZE_CIF_W
471                       && s->vsize.height==MS_VIDEO_SIZE_CIF_H)
472                     {
473                         if (s->usemire){
474                                 om=dupmsg(v4m_make_mire(s));
475                         }else {
476                                 mblk_t *tmpm=v4m_make_nowebcam(s);
477                                 if (tmpm) om=dupmsg(tmpm);
478                         }
479                     }
480                 }
481                 if (om!=NULL){
482                         timestamp=obj->ticker->time*90;/* rtp uses a 90000 Hz clockrate for video*/
483                         mblk_set_timestamp_info(om,timestamp);
484                         mblk_set_marker_info(om,TRUE);  
485                         ms_queue_put(obj->outputs[0],om);
486                         /*ms_message("picture sent");*/
487                         s->frame_count++;
488                 }
489         }else flushq(&s->rq,0);
490
491         ms_mutex_unlock(&s->mutex);
492 }
493
494 static void v4m_preprocess(MSFilter *f){
495         v4mState *s=(v4mState*)f->data;
496         
497         if(s->seqgrab == NULL)
498             v4m_start(f,NULL);
499         
500         v4m_start_capture(s);
501 }
502
503 static void v4m_postprocess(MSFilter *f){
504         v4mState *s=(v4mState*)f->data;
505         v4m_stop_capture(s);
506 }
507
508 static int v4m_set_fps(MSFilter *f, void *arg){
509         v4mState *s=(v4mState*)f->data;
510         s->fps=*((float*)arg);
511         s->frame_count=-1;
512         return 0;
513 }
514
515 static int v4m_get_pix_fmt(MSFilter *f,void *arg){
516         v4mState *s=(v4mState*)f->data;
517         *((MSPixFmt*)arg) = s->pix_fmt;
518         return 0;
519 }
520
521 static int v4m_set_vsize(MSFilter *f, void *arg){
522         v4mState *s=(v4mState*)f->data;
523         s->vsize=*((MSVideoSize*)arg);
524         return 0;
525 }
526
527 static int v4m_get_vsize(MSFilter *f, void *arg){
528         v4mState *s=(v4mState*)f->data;
529         *(MSVideoSize*)arg=s->vsize;
530         return 0;
531 }
532
533 static MSFilterMethod methods[]={
534         {       MS_FILTER_SET_FPS       ,       v4m_set_fps     },
535         {       MS_FILTER_GET_PIX_FMT   ,       v4m_get_pix_fmt },
536         {       MS_FILTER_SET_VIDEO_SIZE,       v4m_set_vsize   },
537         {       MS_V4L_START                    ,       v4m_start       },
538         {       MS_V4L_STOP                     ,       v4m_stop        },
539         {       MS_FILTER_GET_VIDEO_SIZE,       v4m_get_vsize },
540         {       0       ,       NULL                    }
541 };
542
543 MSFilterDesc ms_v4m_desc={
544         .id=MS_V4L_ID,
545         .name="MSV4m",
546         .text="A video for macosx compatible source filter to stream pictures.",
547         .ninputs=0,
548         .noutputs=1,
549         .category=MS_FILTER_OTHER,
550         .init=v4m_init,
551         .preprocess=v4m_preprocess,
552         .process=v4m_process,
553         .postprocess=v4m_postprocess,
554         .uninit=v4m_uninit,
555         .methods=methods
556 };
557
558 MS_FILTER_DESC_EXPORT(ms_v4m_desc)
559         
560 static void ms_v4m_detect(MSWebCamManager *obj);
561
562 static void ms_v4m_cam_init(MSWebCam *cam)
563 {
564
565 }
566
567 static int v4m_set_device(MSFilter *f, void *arg){
568         v4mState *s=(v4mState*)f->data;
569         
570          s->id = (char*) malloc(sizeof(char)*strlen((char*)arg));
571         strcpy(s->id,(char*)arg);
572
573         return 0;
574 }
575
576 static int v4m_set_name(MSFilter *f, void *arg){
577         v4mState *s=(v4mState*)f->data;
578         
579         s->name = (char*) malloc(sizeof(char)*strlen((char*)arg));
580         strcpy(s->name,(char*)arg);
581
582         return 0;
583 }
584
585 static MSFilter *ms_v4m_create_reader(MSWebCam *obj)
586 {
587         MSFilter *f= ms_filter_new_from_desc(&ms_v4m_desc); 
588         
589         v4m_set_device(f,obj->id);
590         v4m_set_name(f,obj->data);
591         
592         return f;
593 }
594
595 MSWebCamDesc ms_v4m_cam_desc={
596         "VideoForMac grabber",
597         &ms_v4m_detect,
598         &ms_v4m_cam_init,
599         &ms_v4m_create_reader,
600         NULL
601 };
602
603 char * genDeviceName(unsigned char * device,short inputIndex, unsigned char * input) 
604 {
605         static char buffer[32];
606         snprintf(buffer,sizeof(buffer), "%s:%d:%s", device,inputIndex,input);
607         return buffer;
608 }
609
610 /*
611  * convert pascal string to c string
612  */
613 static char* pas2cstr(const char *pstr)
614 {
615     char *cstr = ms_malloc(pstr[0] + 1);
616     memcpy(cstr, pstr+1, pstr[0]);
617     cstr[(int)pstr[0]] = 0;
618     
619     return cstr;
620     
621 }
622
623 static void ms_v4m_detect(MSWebCamManager *obj){
624   
625         
626         SGDeviceList sgDeviceList;
627         OSErr err = noErr;
628         SGChannel _SGChanVideo;
629
630         SeqGrabComponent _seqGrab;
631         NSApplicationLoad();
632         
633         if (_SGChanVideo) {
634                 SGDisposeChannel(_seqGrab, _SGChanVideo);
635         }
636         
637         if (_seqGrab) {
638                 CloseComponent(_seqGrab);
639         }
640         
641         _seqGrab = OpenDefaultComponent(SeqGrabComponentType, 0);
642         if (_seqGrab == NULL) {
643                 ms_warning("can't get default sequence grabber component");
644                 return;
645         }
646
647         err = SGInitialize(_seqGrab);
648         if (err != noErr) {
649                 ms_warning("can't initialize sequence grabber component");
650                 return;
651         }
652
653         err = SGSetDataRef(_seqGrab, 0, 0, seqGrabDontMakeMovie);
654         if (err != noErr) {
655                 ms_warning("can't set the destination data reference");
656                 return;
657         }
658
659         err = SGNewChannel(_seqGrab, VideoMediaType, &_SGChanVideo);
660         if (err != noErr) {
661                 ms_warning("can't create a video channel");
662                 return;
663         }
664
665         err = SGGetChannelDeviceList(_SGChanVideo, sgDeviceListIncludeInputs, &sgDeviceList);
666         if (err != noErr)
667                 ms_warning("can't get device list");
668
669         register short i = 0;
670         for (; i < (*sgDeviceList)->count ; i++) 
671         {
672                 if (!((*sgDeviceList)->entry[i].flags & sgDeviceNameFlagDeviceUnavailable)) 
673                 {
674                         SGDeviceInputList inputs = (*sgDeviceList)->entry[i].inputs;
675
676                         register short j = 0;
677                         for (; j < (*inputs)->count ; j++) 
678                         {
679                                 if (!((*inputs)->entry[j].flags & sgDeviceInputNameFlagInputUnavailable)) 
680                                 {
681                                         MSWebCam *cam=ms_web_cam_new(&ms_v4m_cam_desc);
682                                         char * buffer = (char*)malloc(sizeof(char)*32);
683                                         sprintf(buffer,"%d",(*inputs)->selectedIndex);
684                                         cam->name=pas2cstr((*inputs)->entry[j].name);
685                                         cam->id = buffer;
686                                         cam->data = pas2cstr((*sgDeviceList)->entry[i].name);
687                                         ms_web_cam_manager_add_cam(obj,cam);
688                                         ms_warning("web cam found : %s:%s:%s",cam->name,cam->id,cam->data);
689                                 }
690                         }
691                 }
692         }
693
694         SGDisposeDeviceList(_seqGrab, sgDeviceList);
695
696
697         if (_SGChanVideo) {
698                 SGDisposeChannel(_seqGrab, _SGChanVideo);
699         }
700
701         if (_seqGrab) {
702                 CloseComponent(_seqGrab);
703         }
704 }
705         
706 #endif