2 mediastreamer2 library - modular sound and video processing and streaming
3 Copyright (C) 2006 Simon MORLAT (simon.morlat@linphone.org)
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.
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.
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.
22 #include "mediastreamer-config.h"
27 #include <sys/types.h>
28 #include <sys/ioctl.h>
32 #include "mediastreamer2/msvideo.h"
33 #include "mediastreamer2/msticker.h"
34 #include "mediastreamer2/msv4l.h"
36 #include "mediastreamer2/mswebcam.h"
39 #define TARGET_API_MAC_CARBON 1
42 #include <Carbon/Carbon.h>
43 #include <QuicKTime/QuickTime.h>
44 #include <AppKit/AppKit.h>
46 #include <ConditionalMacros.h>
47 #include <QuickTimeComponents.h>
48 #include <TextUtils.h>
49 #include <AppKit/AppKit.h>
53 typedef struct v4mState{
56 SeqGrabComponent seqgrab;
57 SGChannel sgchanvideo;
59 ImageSequence decomseq;
62 int msize;/*mmapped size*/
64 MSVideoSize got_vsize;
66 int int_pix_fmt; /*internal pixel format */
80 static void v4m_init(MSFilter *f){
81 v4mState *s=ms_new0(v4mState,1);
89 s->vsize.width=MS_VIDEO_SIZE_CIF_W;
90 s->vsize.height=MS_VIDEO_SIZE_CIF_H;
94 ms_mutex_init(&s->mutex,NULL);
98 s->usemire=(getenv("DEBUG")!=NULL);
104 #define BailErr(x) {err = x; if(err != noErr) goto bail;}
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)
109 #pragma unused(offset,chRefCon,time,writeType)
112 v4mState *s=(v4mState *)refCon;
113 ComponentResult err = noErr;
117 Rect boundsRect = {0, 0, s->vsize.height, s->vsize.width}; /* 240 , 320*/
120 if (s->decomseq == 0) {
121 Rect sourceRect = { 0, 0 };
122 MatrixRecord scaleMatrix;
123 ImageDescriptionHandle imageDesc = (ImageDescriptionHandle)NewHandle(0);
125 err = SGGetChannelSampleDescription(c,(Handle)imageDesc);
128 // make a scaling matrix for the sequence
129 sourceRect.right = (**imageDesc).width;
130 sourceRect.bottom = (**imageDesc).height;
131 RectMatrix(&scaleMatrix, &sourceRect, &boundsRect);
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
142 codecNormalQuality, // accuracy in decompression
143 bestSpeedCodec); // compressor identifier or special identifiers ie. bestSpeedCodec
146 DisposeHandle((Handle)imageDesc);
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
156 &ignore, // out flags
157 NULL); // async completion proc
163 int size = s->vsize.width * s->vsize.height * 3;
166 PixMap * pixmap = *GetGWorldPixMap(s->pgworld);
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;
172 data = (uint8_t *) GetPixBaseAddr(GetGWorldPixMap(s->pgworld));
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));
179 if (s->pix_fmt==MS_RGB24)
181 /* Conversion from top down bottom up (BGR to RGB and flip) */
182 unsigned long Index,nPixels;
189 nPixels=s->vsize.width*s->vsize.height;
192 for(Index=0;Index!=nPixels;Index++) // For each pixel
202 //ms_mutex_lock(&s->mutex); /* called during SGIdle? */
204 //ms_mutex_unlock(&s->mutex);
212 static int v4m_close(v4mState *s)
215 CloseComponent(s->seqgrab);
218 CDSequenceEnd(s->decomseq);
220 if (s->pgworld!=NULL)
221 DisposeGWorld(s->pgworld);
225 unsigned char *stdToPascalString(char *buffer, char * str) {
226 if (strlen(str) <= 255) {
227 buffer[0] = strlen(str);
229 memcpy(buffer + 1, str, strlen(str));
231 return (unsigned char*)buffer;
237 static int sequence_grabber_start(v4mState *s)
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");
249 ms_warning("Initializing component");
250 err = SGInitialize(s->seqgrab);
252 ms_warning("can't initialize sequence grabber component");
256 ms_warning("SetDataRef");
257 err = SGSetDataRef(s->seqgrab, 0, 0, seqGrabDontMakeMovie);
259 ms_warning("can't set the destination data reference");
263 ms_warning("Creating new channel");
264 err = SGNewChannel(s->seqgrab, VideoMediaType, &s->sgchanvideo);
266 ms_warning("can't create a video channel");
271 camName = alloca(strlen(s->name) + 1);
273 err = SGSetChannelDevice(s->sgchanvideo, stdToPascalString(camName, s->name));
275 ms_warning("can't set channel device");
279 short input = atoi(s->id);
281 err = SGSetChannelDeviceInput(s->sgchanvideo,input);
283 ms_warning("can't set channel device input");
287 ms_warning("createGWorld");
288 Rect theRect = {0, 0, s->vsize.height, s->vsize.width};
290 err = QTNewGWorld(&(s->pgworld), // returned GWorld
292 &theRect, // bounding rectangle
294 NULL, // graphic device handle
301 if(!LockPixels(GetPortPixMap(s->pgworld)))
307 err = SGSetGWorld(s->seqgrab, s->pgworld, GetMainDevice());
309 ms_warning("can't set GWorld");
313 ms_warning("SGSetDataProc");
314 err = SGSetDataProc(s->seqgrab,NewSGDataUPP(sgdata_callback),(long)s);
316 ms_warning("can't set data proc");
320 ms_warning("SGSetChannelUsage");
321 err = SGSetChannelUsage(s->sgchanvideo, seqGrabRecord);
323 ms_warning("can't set channel usage");
327 ms_warning("SGPrepare");
328 err = SGPrepare(s->seqgrab, false, true);
330 ms_warning("can't prepare sequence grabber component");
334 err = SGSetChannelBounds(s->sgchanvideo, &theRect);
341 err = SGStartRecord(s->seqgrab);
351 static int v4m_start(MSFilter *f, void *arg)
353 v4mState *s=(v4mState*)f->data;
356 err = sequence_grabber_start(s);
360 s->pix_fmt=MS_YUV420P;
361 s->vsize.width=MS_VIDEO_SIZE_CIF_W;
362 s->vsize.height=MS_VIDEO_SIZE_CIF_H;
366 ms_message("v4m video device opened.");
372 static void v4m_start_capture(v4mState *s){
373 if (s->seqgrab!=NULL){
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);
385 ms_mutex_unlock(&s->mutex);
390 static void v4m_stop_capture(v4mState *s){
393 ms_message("v4m capture stopped.");
398 static void v4m_uninit(MSFilter *f){
399 v4mState *s=(v4mState*)f->data;
400 if (s->seqgrab!=NULL) v4m_stop(f,NULL);
403 ms_mutex_destroy(&s->mutex);
408 static mblk_t * v4m_make_mire(v4mState *s){
411 int patternw=s->vsize.width/6;
412 int patternh=s->vsize.height/6;
413 int red,green=0,blue=0;
415 s->mire=allocb(s->vsize.width*s->vsize.height*3,0);
416 s->mire->b_wptr=s->mire->b_datap->db_lim;
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;
423 for (j=0;j<s->vsize.width;++j){
426 if ( ((j+s->frame_ind)/patternw) & 0x1) blue=255;
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);
446 static void v4m_process(MSFilter * obj){
447 v4mState *s=(v4mState*)obj->data;
450 if (s->frame_count==-1){
451 s->start_time=obj->ticker->time;
455 ms_mutex_lock(&s->mutex);
457 if (s->seqgrab!=NULL)
462 cur_frame=((obj->ticker->time-s->start_time)*s->fps/1000.0);
463 if (cur_frame>=s->frame_count){
465 /*keep the most recent frame if several frames have been captured */
466 if (s->seqgrab!=NULL){
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)
474 om=dupmsg(v4m_make_mire(s));
476 mblk_t *tmpm=v4m_make_nowebcam(s);
477 if (tmpm) om=dupmsg(tmpm);
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");*/
489 }else flushq(&s->rq,0);
491 ms_mutex_unlock(&s->mutex);
494 static void v4m_preprocess(MSFilter *f){
495 v4mState *s=(v4mState*)f->data;
497 if(s->seqgrab == NULL)
500 v4m_start_capture(s);
503 static void v4m_postprocess(MSFilter *f){
504 v4mState *s=(v4mState*)f->data;
508 static int v4m_set_fps(MSFilter *f, void *arg){
509 v4mState *s=(v4mState*)f->data;
510 s->fps=*((float*)arg);
515 static int v4m_get_pix_fmt(MSFilter *f,void *arg){
516 v4mState *s=(v4mState*)f->data;
517 *((MSPixFmt*)arg) = s->pix_fmt;
521 static int v4m_set_vsize(MSFilter *f, void *arg){
522 v4mState *s=(v4mState*)f->data;
523 s->vsize=*((MSVideoSize*)arg);
527 static int v4m_get_vsize(MSFilter *f, void *arg){
528 v4mState *s=(v4mState*)f->data;
529 *(MSVideoSize*)arg=s->vsize;
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 },
543 MSFilterDesc ms_v4m_desc={
546 .text="A video for macosx compatible source filter to stream pictures.",
549 .category=MS_FILTER_OTHER,
551 .preprocess=v4m_preprocess,
552 .process=v4m_process,
553 .postprocess=v4m_postprocess,
558 MS_FILTER_DESC_EXPORT(ms_v4m_desc)
560 static void ms_v4m_detect(MSWebCamManager *obj);
562 static void ms_v4m_cam_init(MSWebCam *cam)
567 static int v4m_set_device(MSFilter *f, void *arg){
568 v4mState *s=(v4mState*)f->data;
570 s->id = (char*) malloc(sizeof(char)*strlen((char*)arg));
571 strcpy(s->id,(char*)arg);
576 static int v4m_set_name(MSFilter *f, void *arg){
577 v4mState *s=(v4mState*)f->data;
579 s->name = (char*) malloc(sizeof(char)*strlen((char*)arg));
580 strcpy(s->name,(char*)arg);
585 static MSFilter *ms_v4m_create_reader(MSWebCam *obj)
587 MSFilter *f= ms_filter_new_from_desc(&ms_v4m_desc);
589 v4m_set_device(f,obj->id);
590 v4m_set_name(f,obj->data);
595 MSWebCamDesc ms_v4m_cam_desc={
596 "VideoForMac grabber",
599 &ms_v4m_create_reader,
603 char * genDeviceName(unsigned char * device,short inputIndex, unsigned char * input)
605 static char buffer[32];
606 snprintf(buffer,sizeof(buffer), "%s:%d:%s", device,inputIndex,input);
611 * convert pascal string to c string
613 static char* pas2cstr(const char *pstr)
615 char *cstr = ms_malloc(pstr[0] + 1);
616 memcpy(cstr, pstr+1, pstr[0]);
617 cstr[(int)pstr[0]] = 0;
623 static void ms_v4m_detect(MSWebCamManager *obj){
626 SGDeviceList sgDeviceList;
628 SGChannel _SGChanVideo;
630 SeqGrabComponent _seqGrab;
634 SGDisposeChannel(_seqGrab, _SGChanVideo);
638 CloseComponent(_seqGrab);
641 _seqGrab = OpenDefaultComponent(SeqGrabComponentType, 0);
642 if (_seqGrab == NULL) {
643 ms_warning("can't get default sequence grabber component");
647 err = SGInitialize(_seqGrab);
649 ms_warning("can't initialize sequence grabber component");
653 err = SGSetDataRef(_seqGrab, 0, 0, seqGrabDontMakeMovie);
655 ms_warning("can't set the destination data reference");
659 err = SGNewChannel(_seqGrab, VideoMediaType, &_SGChanVideo);
661 ms_warning("can't create a video channel");
665 err = SGGetChannelDeviceList(_SGChanVideo, sgDeviceListIncludeInputs, &sgDeviceList);
667 ms_warning("can't get device list");
669 register short i = 0;
670 for (; i < (*sgDeviceList)->count ; i++)
672 if (!((*sgDeviceList)->entry[i].flags & sgDeviceNameFlagDeviceUnavailable))
674 SGDeviceInputList inputs = (*sgDeviceList)->entry[i].inputs;
676 register short j = 0;
677 for (; j < (*inputs)->count ; j++)
679 if (!((*inputs)->entry[j].flags & sgDeviceInputNameFlagInputUnavailable))
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);
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);
694 SGDisposeDeviceList(_seqGrab, sgDeviceList);
698 SGDisposeChannel(_seqGrab, _SGChanVideo);
702 CloseComponent(_seqGrab);