blob: 69fd67a308cb90e57e01d3ba34c31948df6611a5 [file] [log] [blame]
andrew@webrtc.orga7b57da2012-10-22 18:19:23 +00001/*
2 * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
3 *
4 * Use of this source code is governed by a BSD-style license
5 * that can be found in the LICENSE file in the root of the source
6 * tree. An additional intellectual property rights grant can be found
7 * in the file PATENTS. All contributing project authors may
8 * be found in the AUTHORS file in the root of the source tree.
9 */
10
11/*
12 * video_capture_quick_time.cc
13 *
14 */
15
16
17#include "video_capture_quick_time.h"
18
19#include "CriticalSectionWrapper.h"
20#include "event_wrapper.h"
21#include "thread_wrapper.h"
22#include "tick_util.h"
23#include "trace.h"
24#include <unistd.h>
25
26namespace webrtc
27{
28
29VideoCaptureMacQuickTime::VideoCaptureMacQuickTime(WebRtc_Word32 iID) :
30 VideoCaptureImpl(iID), // super class constructor
31 _id(iID),
32 _isCapturing(false),
33 _captureCapability(),
34 _grabberCritsect(CriticalSectionWrapper::CreateCriticalSection()),
35 _videoMacCritsect(CriticalSectionWrapper::CreateCriticalSection()),
36 _terminated(true), _grabberUpdateThread(NULL),
37 _grabberUpdateEvent(NULL), _captureGrabber(NULL), _captureDevice(NULL),
38 _captureVideoType(kVideoUnknown), _captureIsInitialized(false),
39 _gWorld(NULL), _captureChannel(0), _captureSequence(NULL),
40 _sgPrepared(false), _sgStarted(false), _trueCaptureWidth(0),
41 _trueCaptureHeight(0), _captureDeviceList(),
42 _captureDeviceListTime(0), _captureCapabilityList()
43
44{
45 _captureCapability.width = START_CODEC_WIDTH;
46 _captureCapability.height = START_CODEC_HEIGHT;
47 memset(_captureDeviceDisplayName, 0, sizeof(_captureDeviceDisplayName));
48}
49
50VideoCaptureMacQuickTime::~VideoCaptureMacQuickTime()
51{
52
53
54 VideoCaptureTerminate();
55
56 if (_videoMacCritsect)
57 {
58 delete _videoMacCritsect;
59 }
60 if (_grabberCritsect)
61 {
62 delete _grabberCritsect;
63 }
64
65}
66
67WebRtc_Word32 VideoCaptureMacQuickTime::Init(
68 const WebRtc_Word32 id, const char* deviceUniqueIdUTF8)
69{
70
71 const WebRtc_Word32 nameLength =
72 (WebRtc_Word32) strlen((char*) deviceUniqueIdUTF8);
73 if (nameLength > kVideoCaptureUniqueNameLength)
74 return -1;
75
76 // Store the device name
77 _deviceUniqueId = new char[nameLength + 1];
78 memset(_deviceUniqueId, 0, nameLength + 1);
79 memcpy(_deviceUniqueId, deviceUniqueIdUTF8, nameLength + 1);
80
81 // Check OSX version
82 OSErr err = noErr;
83 long version;
84
85 _videoMacCritsect->Enter();
86 if (!_terminated)
87 {
88 _videoMacCritsect->Leave();
89 WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id,
90 "%s:%d Already Initialized", __FUNCTION__, __LINE__);
91 return -1;
92 }
93
94 err = Gestalt(gestaltSystemVersion, &version);
95 if (err != noErr)
96 {
97 _videoMacCritsect->Leave();
98 WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id,
99 "%s:%d Could not retrieve OS version", __FUNCTION__,
100 __LINE__);
101 return -1;
102 }
103
104 WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideoCapture, _id,
105 "%s:%d OS X version: %x,", __FUNCTION__, __LINE__, version);
106 if (version < 0x00001040) // Older version than Mac OSX 10.4
107 {
108 _videoMacCritsect->Leave();
109 WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id,
110 "%s:%d OS version not supported", __FUNCTION__, __LINE__);
111 return -1;
112 }
113
114 err = Gestalt(gestaltQuickTime, &version);
115 if (err != noErr)
116 {
117 _videoMacCritsect->Leave();
118 WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id,
119 "%s:%d Could not retrieve QuickTime version",
120 __FUNCTION__, __LINE__);
121 return -1;
122 }
123
124 WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideoCapture, _id,
125 "%s:%d QuickTime version: %x", __FUNCTION__, __LINE__,
126 version);
127 if (version < 0x07000000) // QT v. 7.x or newer (QT 5.0.2 0x05020000)
128 {
129 _videoMacCritsect->Leave();
130 return -1;
131 }
132
133 WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCapture, _id,
134 "%s:%d EnterMovies()", __FUNCTION__, __LINE__);
135 EnterMovies();
136
137 if (VideoCaptureSetCaptureDevice((char*) deviceUniqueIdUTF8,
138 kVideoCaptureProductIdLength) == -1)
139 {
140 WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id,
141 "%s:%d failed to set capture device: %s", __FUNCTION__,
142 __LINE__, deviceUniqueIdUTF8);
143 _videoMacCritsect->Leave();
144 return -1;
145 }
146
147 _terminated = false;
148
149 WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideoCapture, _id,
150 "%s:%d successful initialization", __FUNCTION__, __LINE__);
151 _videoMacCritsect->Leave();
152
153 return 0;
154}
155
156WebRtc_Word32 VideoCaptureMacQuickTime::StartCapture(
157 const VideoCaptureCapability& capability)
158{
159 WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideoCapture, _id, "%s:%d "
160 "capability.width=%d, capability.height=%d ,capability.maxFPS=%d "
161 "capability.expectedCaptureDelay=%d, capability.interlaced=%d",
162 __FUNCTION__, __LINE__, capability.width, capability.height,
163 capability.maxFPS, capability.expectedCaptureDelay,
164 capability.interlaced);
165
166 _captureCapability.width = capability.width;
167 _captureCapability.height = capability.height;
168 _captureDelay = 120;
169
170 if (VideoCaptureRun() == -1)
171 {
172 return -1;
173 }
174
175 return 0;
176}
177
178WebRtc_Word32 VideoCaptureMacQuickTime::StopCapture()
179{
180
181 if (VideoCaptureStop() == -1)
182 {
183 return -1;
184 }
185
186 return 0;
187}
188
189bool VideoCaptureMacQuickTime::CaptureStarted()
190{
191 return _isCapturing;
192}
193
194WebRtc_Word32 VideoCaptureMacQuickTime::CaptureSettings(
195 VideoCaptureCapability& settings)
196{
197 settings.width = _captureCapability.width;
198 settings.height = _captureCapability.height;
199 settings.maxFPS = 0;
200 return 0;
201}
202
203int VideoCaptureMacQuickTime::VideoCaptureTerminate()
204{
205 VideoCaptureStop();
206
207 _videoMacCritsect->Enter();
208 if (_terminated)
209 {
210 _videoMacCritsect->Leave();
211 WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id,
212 "%s:%d Already terminated", __FUNCTION__, __LINE__);
213 return -1;
214 }
215
216 _grabberCritsect->Enter();
217
218 // Stop the camera/sequence grabber
219 // Resets: _captureSequence, _sgStarted
220 StopQuickTimeCapture();
221
222 // Remove local video settings
223 // Resets: _gWorld, _captureCapability.width, _captureCapability.height
224 RemoveLocalGWorld();
225 DisconnectCaptureDevice();
226
227 if (_grabberUpdateThread)
228 _grabberUpdateThread->SetNotAlive();
229
230 _grabberCritsect->Leave();
231
232 if (_grabberUpdateEvent)
233 _grabberUpdateEvent->Set();
234
235 SLEEP(1);
236 _grabberCritsect->Enter();
237
238 if (_grabberUpdateThread)
239 {
240 _grabberUpdateThread->Stop();
241 delete _grabberUpdateThread;
242 _grabberUpdateThread = NULL;
243 }
244 if (_grabberUpdateEvent)
245 {
246 delete _grabberUpdateEvent;
247 _grabberUpdateEvent = NULL;
248 }
249
250 // Close the sequence grabber
251 if (_captureGrabber)
252 {
253 SGRelease(_captureGrabber);
254 _captureGrabber = NULL;
255 CloseComponent(_captureGrabber);
256 _captureDevice = NULL;
257 }
258 _captureVideoType = kVideoUnknown;
259
260 // Delete capture device list
261 ListItem* item = _captureDeviceList.First();
262 while (item)
263 {
264 delete static_cast<unsigned char*> (item->GetItem());
265 _captureDeviceList.Erase(item);
266 item = _captureDeviceList.First();
267 }
268 _captureDeviceListTime = 0;
269
270 _terminated = true;
271
272 _grabberCritsect->Leave();
273 _videoMacCritsect->Leave();
274
275 return 0;
276}
277
278int VideoCaptureMacQuickTime::UpdateCaptureSettings(int channel,
279 webrtc::VideoCodec& inst,
280 bool def)
281{
282
283 if (channel < 0)
284 {
285 WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id,
286 "%s:%d Invalid channel number: %d", __FUNCTION__,
287 __LINE__, channel);
288 return -1;
289 }
290
291 // the size has changed, we need to change our setup
292 _videoMacCritsect->Enter();
293
294 // Stop capturing, if we are...
295 _grabberCritsect->Enter();
296
297 bool wasCapturing = false;
298 StopQuickTimeCapture(&wasCapturing);
299
300 // Create a new offline GWorld to receive captured frames
301 RemoveLocalGWorld();
302
303 if (CreateLocalGWorld(inst.width, inst.height) == -1)
304 {
305 _grabberCritsect->Leave();
306 _videoMacCritsect->Leave();
307 // Error already logged
308 return -1;
309 }
310 _captureCapability.width = inst.width;
311 _captureCapability.height = inst.height;
312
313 // Connect the capture device to our offline GWorld
314 // if we already have a capture device selected.
315 if (_captureDevice)
316 {
317 DisconnectCaptureDevice();
318 if (ConnectCaptureDevice() == -1)
319 {
320 // Error already logged
321 _grabberCritsect->Leave();
322 _videoMacCritsect->Leave();
323 return -1;
324 }
325 }
326
327 // Start capture if we did before
328 if (wasCapturing)
329 {
330 if (StartQuickTimeCapture() == -1)
331 {
332 _grabberCritsect->Leave();
333 _videoMacCritsect->Leave();
334 WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id,
335 "%s:%d Failed to start capturing", __FUNCTION__,
336 __LINE__);
337 return -1;
338 }
339 }
340 _grabberCritsect->Leave();
341 _videoMacCritsect->Leave();
342
343 return 0;
344}
345
346// Creates an off screen graphics world used for converting
347// captured video frames if we can't get a format we want.
348// Assumed protected by critsects
349int VideoCaptureMacQuickTime::CreateLocalGWorld(int width, int height)
350{
351 if (_gWorld)
352 {
353 WEBRTC_TRACE(webrtc::kTraceWarning, webrtc::kTraceVideoCapture, _id,
354 "%s:%d GWorld already created", __FUNCTION__, __LINE__);
355 return -1;
356 }
357 if (width == 0 || height == 0)
358 {
359 return -1;
360 }
361
362 Rect captureRect;
363 captureRect.left = 0;
364 captureRect.top = 0;
365 captureRect.right = width;
366 captureRect.bottom = height;
367
368 // Create a GWorld in same size as we want to send to the codec
369 if (QTNewGWorld(&(_gWorld), k2vuyPixelFormat, &captureRect, 0, NULL, 0)
370 != noErr)
371 {
372 return -1;
373 }
374 _captureCapability.width = width;
375 _captureCapability.height = height;
376
377 if (!LockPixels(GetGWorldPixMap(_gWorld)))
378 {
379 WEBRTC_TRACE(webrtc::kTraceWarning, webrtc::kTraceVideoCapture, _id,
380 "%s:%d Could not lock pixmap. Continuing anyhow",
381 __FUNCTION__, __LINE__);
382 }
383
384 CGrafPtr theOldPort;
385 GDHandle theOldDevice;
386 GetGWorld(&theOldPort, &theOldDevice); // Gets the result from QTGetNewGWorld
387 SetGWorld(_gWorld, NULL); // Sets the new GWorld
388 BackColor( blackColor); // Changes the color on the graphic port
389 ForeColor( whiteColor);
390 EraseRect(&captureRect);
391 SetGWorld(theOldPort, theOldDevice);
392
393 return 0;
394}
395
396// Assumed critsect protected
397int VideoCaptureMacQuickTime::RemoveLocalGWorld()
398{
399 if (!_gWorld)
400 {
401 WEBRTC_TRACE(webrtc::kTraceWarning, webrtc::kTraceVideoCapture, _id,
402 "%s:%d !gWorld", __FUNCTION__, __LINE__);
403 return -1;
404 }
405
406 DisposeGWorld(_gWorld);
407 _gWorld = NULL;
408 _captureCapability.width = START_CODEC_WIDTH;
409 _captureCapability.height = START_CODEC_HEIGHT;
410
411 WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideoCapture, _id,
412 "%s:%d GWorld has been removed", __FUNCTION__, __LINE__);
413 return 0;
414}
415
416// ConnectCaptureDevice
417// This function prepares the capture device
418// with the wanted settings, but the capture
419// device isn't started.
420//
421// Assumed critsect protected
422int VideoCaptureMacQuickTime::ConnectCaptureDevice()
423{
424 // Prepare the capture grabber if a capture device is already set
425 if (!_captureGrabber)
426 {
427 WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id,
428 "%s:%d No capture device is selected", __FUNCTION__,
429 __LINE__);
430 return -1;
431 }
432 if (_captureIsInitialized)
433 {
434 WEBRTC_TRACE(webrtc::kTraceWarning, webrtc::kTraceVideoCapture, _id,
435 "%s:%d Capture device is already initialized",
436 __FUNCTION__, __LINE__);
437 return -1;
438 }
439 if (!_gWorld)
440 {
441 WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id,
442 "%s:%d No GWorld is created", __FUNCTION__, __LINE__);
443 return -1;
444 }
445
446 OSErr err = noErr;
447 long flags = 0;
448
449 // Connect the camera to our offline GWorld
450 // We won't use the GWorld if we get the format we want
451 // from the camera.
452 if (SGSetGWorld(_captureGrabber, _gWorld, NULL ) != noErr)
453 {
454 WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id,
455 "%s:%d Could not connect capture device", __FUNCTION__,
456 __LINE__);
457 return -1;
458 }
459 if (SGSetDataRef(_captureGrabber, 0, 0, seqGrabDontMakeMovie) != noErr)
460 {
461 WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id,
462 "%s:%d Could not configure capture device", __FUNCTION__,
463 __LINE__);
464 return -1;
465 }
466
467 // Set our capture callback
468 if (SGSetDataProc(_captureGrabber, NewSGDataUPP(SendProcess), (long) this)
469 != noErr)
470 {
471 WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id,
472 "%s:%d Could not set capture callback. Unable to receive "
473 "frames", __FUNCTION__, __LINE__);
474 return -1;
475 }
476
477 // Create a video channel to the sequence grabber
478 if (SGNewChannel(_captureGrabber, VideoMediaType, &_captureChannel)
479 != noErr) // Takes time!!!
480 {
481 return -1;
482 }
483
484 // Get a list with all capture devices to choose the one we want.
485 SGDeviceList deviceList = NULL;
486 if (SGGetChannelDeviceList(_captureChannel, sgDeviceListIncludeInputs,
487 &deviceList) != noErr)
488 {
489
490 }
491
492 int numDevicesTypes = (*deviceList)->count;
493 bool captureDeviceFound = false;
494 WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCapture, _id,
495 "%s:%d Found %d channel devices", __FUNCTION__, __LINE__,
496 numDevicesTypes);
497
498 // Loop through all devices to get the one we want.
499 for (int i = 0; i < numDevicesTypes; i++)
500 {
501 SGDeviceName deviceTypeName = (*deviceList)->entry[i];
502 WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCapture, _id,
503 "%s:%d Inspecting device number: %d", __FUNCTION__,
504 __LINE__, i);
505 // Get the list with input devices
506 if (deviceTypeName.inputs)
507 {
508 SGDeviceInputList inputList = deviceTypeName.inputs;
509 int numInputDev = (*inputList)->count;
510 WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCapture, _id,
511 "%s:%d Device has %d inputs", __FUNCTION__, __LINE__,
512 numInputDev);
513 for (int inputDevIndex = 0;
514 inputDevIndex < numInputDev;
515 inputDevIndex++)
516 {
517 WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCapture,
518 _id, "%s:%d Inspecting input number: %d",
519 __FUNCTION__, __LINE__, inputDevIndex);
520 SGDeviceInputName deviceInputName =
521 (*inputList)->entry[inputDevIndex];
522 char devInName[64];
523 memset(devInName, 0, 64);
524
525 // SGDeviceInputName::name is a Str63, defined as a Pascal string.
526 // (Refer to MacTypes.h)
527 CFIndex devInNameLength =
528 PascalStringToCString(deviceInputName.name, devInName,
529 sizeof(devInName));
530 WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCapture,
531 _id,
532 "%s:%d Converted pascal string with length:%d "
533 "to: %s", __FUNCTION__, __LINE__,
534 sizeof(devInName), devInName);
535 if (devInNameLength < 0)
536 {
537 WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture,
538 _id,
539 "%s:%d Failed to convert device name from "
540 "pascal string to c string", __FUNCTION__,
541 __LINE__);
542 return -1;
543 }
544
545 if (!strcmp(devInName, _captureDeviceDisplayName))
546 {
547 WEBRTC_TRACE(webrtc::kTraceDebug,
548 webrtc::kTraceVideoCapture, _id,
549 "%s:%d We have found our device: %s",
550 __FUNCTION__, __LINE__,
551 _captureDeviceDisplayName);
552
553 if (SGSetChannelDevice(_captureChannel, deviceTypeName.name)
554 != noErr)
555 {
556 WEBRTC_TRACE(webrtc::kTraceError,
557 webrtc::kTraceVideoCapture, _id,
558 "%s:%d Could not set capture device type: "
559 "%s",__FUNCTION__, __LINE__,
560 deviceTypeName.name);
561 return -1;
562 }
563
564 WEBRTC_TRACE(webrtc::kTraceInfo,
565 webrtc::kTraceVideoCapture, _id,
566 "%s:%d Capture device type is: %s",
567 __FUNCTION__, __LINE__, deviceTypeName.name);
568 if (SGSetChannelDeviceInput(_captureChannel, inputDevIndex)
569 != noErr)
570 {
571 WEBRTC_TRACE(webrtc::kTraceError,
572 webrtc::kTraceVideoCapture, _id,
573 "%s:%d Could not set SG device",
574 __FUNCTION__, __LINE__);
575 return -1;
576 }
577
578 WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideoCapture,
579 _id,
580 "%s:%d Capture device: %s has successfully "
581 "been set", __FUNCTION__, __LINE__,
582 _captureDeviceDisplayName);
583 captureDeviceFound = true;
584 break;
585 }
586 }
587 if (captureDeviceFound)
588 {
589 WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCapture,
590 _id,
591 "%s:%d Capture device found, breaking from loops",
592 __FUNCTION__, __LINE__);
593 break;
594 }
595 }
596 }
597 err = SGDisposeDeviceList(_captureGrabber, deviceList);
598
599 if (!captureDeviceFound)
600 {
601 WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id,
602 "%s:%d Failed to find capture device: %s. Returning -1",
603 __FUNCTION__, __LINE__, _captureDeviceDisplayName);
604 return -1;
605 }
606
607 // Set the size we want from the capture device
608 Rect captureSize;
609 captureSize.left = 0;
610 captureSize.top = 0;
611 captureSize.right = _captureCapability.width;
612 captureSize.bottom = _captureCapability.height;
613
614 WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideoCapture, _id,
615 "%s:%d Using capture rect: l:%d t:%d r:%d b:%d", __FUNCTION__,
616 __LINE__, captureSize.left, captureSize.top,
617 captureSize.right, captureSize.bottom);
618
619 err = SGSetChannelBounds(_captureChannel, &captureSize);
620 if (err == noErr)
621 {
622 err = SGSetChannelUsage(_captureChannel, flags | seqGrabRecord);
623 }
624 if (err != noErr)
625 {
626 SGDisposeChannel(_captureGrabber, _captureChannel);
627 WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id,
628 "%s:%d Error setting SG channel to device", __FUNCTION__,
629 __LINE__);
630 return -1;
631 }
632
633 // Find out what video format we'll get from the capture device.
634 OSType compType;
635 err = SGGetVideoCompressorType(_captureChannel, &compType);
636
637 // Convert the Apple video format name to a VideoCapture name.
638 if (compType == k2vuyPixelFormat)
639 {
640 _captureVideoType = kVideoUYVY;
641 WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideoCapture, _id,
642 "%s:%d Device delivers UYUV formatted frames",
643 __FUNCTION__, __LINE__);
644 }
645 else if (compType == kYUVSPixelFormat)
646 {
647 WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideoCapture, _id,
648 "%s:%d Device delivers YUY2 formatted frames",
649 __FUNCTION__, __LINE__);
650 _captureVideoType = kVideoYUY2;
651 }
652 else
653 {
654 WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCapture, _id,
655 "%s:%d Device delivers frames in an unknown format: 0x%x. "
656 "Consult QuickdrawTypes.h",
657 __FUNCTION__, __LINE__, compType);
658 WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideoCapture, _id,
659 "%s:%d Device delivers frames in an unknown format.",
660 __FUNCTION__, __LINE__);
661 _captureVideoType = kVideoUnknown;
662 }
663
664 if (SGPrepare(_captureGrabber, false, true) != noErr)
665 {
666 _grabberCritsect->Leave();
667 WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id,
668 "%s:%d Error starting sequence grabber", __FUNCTION__,
669 __LINE__);
670 return -1;
671 }
672
673 // Try to set the codec size as capture size.
674 err = SGSetChannelBounds(_captureChannel, &captureSize);
675
676 // Check if we really will get the size we asked for.
677 ImageDescriptionHandle imageDesc = (ImageDescriptionHandle) NewHandle(0);
678 err = SGGetChannelSampleDescription(_captureChannel, (Handle) imageDesc);
679
680 _trueCaptureWidth = (**imageDesc).width;
681 _trueCaptureHeight = (**imageDesc).height;
682
683 DisposeHandle((Handle) imageDesc);
684
685 _captureIsInitialized = true;
686 _sgPrepared = true;
687
688 WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideoCapture, _id,
689 "%s:%d Success starting sequence grabber", __FUNCTION__,
690 __LINE__);
691
692 return 0;
693}
694
695// Assumed critsect protected
696int VideoCaptureMacQuickTime::DisconnectCaptureDevice()
697{
698 if (_sgStarted)
699 {
700 WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id,
701 "%s:%d Capture device is still running. Returning -1",
702 __FUNCTION__, __LINE__);
703 return -1;
704 }
705 if (!_sgPrepared)
706 {
707 WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id,
708 "%s:%d No capture device connected", __FUNCTION__,
709 __LINE__);
710 return -1;
711 }
712
713 // Close the capture channel
714 SGStop(_captureGrabber);
715 WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCapture, _id,
716 "%s:%d !!!! releasing sg stuff", __FUNCTION__, __LINE__);
717 SGDisposeChannel(_captureGrabber, _captureChannel);
718 SGRelease(_captureGrabber);
719 CloseComponent(_captureGrabber);
720
721 // Reset all values
722 _captureChannel = NULL;
723 _captureVideoType = kVideoUnknown;
724 _trueCaptureWidth = 0;
725 _trueCaptureHeight = 0;
726 _captureIsInitialized = false;
727 _sgPrepared = false;
728
729 WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideoCapture, _id,
730 "%s:%d Sequence grabber removed", __FUNCTION__, __LINE__);
731
732 return 0;
733}
734
735// StartQuickTimeCapture
736//
737// Actually starts the camera
738//
739int VideoCaptureMacQuickTime::StartQuickTimeCapture()
740{
741 _grabberCritsect->Enter();
742 WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideoCapture, _id,
743 "%s:%d Attempting to start sequence grabber", __FUNCTION__,
744 __LINE__);
745
746 if (_sgStarted)
747 {
748 _grabberCritsect->Leave();
749 WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id,
750 "%s:%d Sequence grabber already started", __FUNCTION__,
751 __LINE__);
752 return 0;
753 }
754 if (!_sgPrepared)
755 {
756 _grabberCritsect->Leave();
757 WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id,
758 "%s:%d Sequence grabber not prepared properly",
759 __FUNCTION__, __LINE__);
760 return 0;
761 }
762
763 if (SGStartRecord(_captureGrabber) != noErr)
764 {
765 _grabberCritsect->Leave();
766 WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id,
767 "%s:%d Error starting sequence grabber", __FUNCTION__,
768 __LINE__);
769 return -1;
770 }
771
772 Rect captureRect = { 0, 0, 0, 0 };
773 MatrixRecord scaleMatrix;
774 ImageDescriptionHandle imageDesc = (ImageDescriptionHandle) NewHandle(0);
775
776 // Get the sample description for the channel, which is the same as for the
777 // capture device
778 if (SGGetChannelSampleDescription(_captureChannel, (Handle) imageDesc)
779 != noErr)
780 {
781 _grabberCritsect->Leave();
782 WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id,
783 "%s:%d Error accessing device properties", __FUNCTION__,
784 __LINE__);
785 return -1;
786 }
787
788 // Create a scale matrix to scale the captured image
789 // Needed if we don't get the size wanted from the camera
790 captureRect.right = (**imageDesc).width;
791 captureRect.bottom = (**imageDesc).height;
792
793 Rect codecRect;
794 codecRect.left = 0;
795 codecRect.top = 0;
796 codecRect.right = _captureCapability.width;
797 codecRect.bottom = _captureCapability.height;
798 RectMatrix(&scaleMatrix, &captureRect, &codecRect);
799
800 // Start grabbing images from the capture device to _gWorld
801 if (DecompressSequenceBegin(&_captureSequence, imageDesc, _gWorld, NULL,
802 NULL, &scaleMatrix, srcCopy, (RgnHandle) NULL,
803 NULL, codecNormalQuality, bestSpeedCodec)
804 != noErr)
805 {
806 _grabberCritsect->Leave();
807 WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideoCapture, _id,
808 "%s:%d Error starting decompress sequence", __FUNCTION__,
809 __LINE__);
810 return -1;
811 }
812 DisposeHandle((Handle) imageDesc);
813 _sgStarted = true;
814 _grabberCritsect->Leave();
815 return 0;
816}
817
818int VideoCaptureMacQuickTime::StopQuickTimeCapture(bool* wasCapturing)
819{
820 _grabberCritsect->Enter();
821 WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideoCapture, _id,
822 "%s:%d wasCapturing=%d", __FUNCTION__, __LINE__, wasCapturing);
823
824 if (!_sgStarted)
825 {
826 if (wasCapturing)
827 *wasCapturing = false;
828
829 _grabberCritsect->Leave();
830 WEBRTC_TRACE(webrtc::kTraceWarning, webrtc::kTraceVideoCapture, _id,
831 "%s:%d Sequence grabber was never started", __FUNCTION__,
832 __LINE__);
833 return 0;
834 }
835
836 if (wasCapturing)
837 *wasCapturing = true;
838
839 OSErr error = noErr;
840 error = SGStop(_captureGrabber);
841 CDSequenceEnd(_captureSequence);
842 _captureSequence = NULL;
843 _sgStarted = false;
844
845 _grabberCritsect->Leave();
846 if (error != noErr)
847 {
848 WEBRTC_TRACE(webrtc::kTraceWarning, webrtc::kTraceVideoCapture, _id,
849 "%s:%d Could not stop sequence grabber", __FUNCTION__,
850 __LINE__);
851 return -1;
852 }
853
854 return 0;
855}
856
857//-------------------------------------------------
858//
859// Thread/function to keep capture device working
860//
861//-------------------------------------------------
862
863
864//
865// GrabberUpdateThread / GrabberUpdateProcess
866//
867// Called at a certain time interval to tell
868// the capture device / SequenceGrabber to
869// actually work.
870bool VideoCaptureMacQuickTime::GrabberUpdateThread(void* obj)
871{
872 return static_cast<VideoCaptureMacQuickTime*> (obj)->GrabberUpdateProcess();
873}
874
875bool VideoCaptureMacQuickTime::GrabberUpdateProcess()
876{
877 _grabberUpdateEvent->Wait(30);
878
879 if (_isCapturing == false)
880 return false;
881
882 _grabberCritsect->Enter();
883 if (_captureGrabber)
884 {
885 if (SGIdle(_captureGrabber) != noErr)
886 {
887 }
888 }
889 _grabberCritsect->Leave();
890 return true;
891}
892
893//
894// VideoCaptureStop
895//
896// Stops the capture device
897//
898int VideoCaptureMacQuickTime::VideoCaptureStop()
899{
900 if (_grabberUpdateThread)
901 {
902 _grabberUpdateThread->Stop();
903 }
904
905 _videoMacCritsect->Enter();
906 _grabberCritsect->Enter();
907 int retVal = StopQuickTimeCapture();
908 _grabberCritsect->Leave();
909 _videoMacCritsect->Leave();
910 if (retVal == -1)
911 {
912 return -1;
913 }
914
915 _isCapturing = false;
916 return 0;
917}
918
919//
920// VideoCaptureRun
921//
922// Starts the capture device and creates
923// the update thread.
924//
925int VideoCaptureMacQuickTime::VideoCaptureRun()
926{
927 _videoMacCritsect->Enter();
928 _grabberCritsect->Enter();
929
930 int res = StartQuickTimeCapture();
931
932 // Create the thread for updating sequence grabber if not created earlier
933 if (!_grabberUpdateThread)
934 {
935 _grabberUpdateEvent = EventWrapper::Create();
936 _grabberUpdateThread = ThreadWrapper::CreateThread(
937 VideoCaptureMacQuickTime::GrabberUpdateThread, this, kHighPriority);
938 unsigned int id;
939 _grabberUpdateThread->Start(id);
940 }
941 else
942 {
943 unsigned int id;
944 _grabberUpdateThread->Start(id);
945 }
946
947 _grabberCritsect->Leave();
948 _videoMacCritsect->Leave();
949
950 _isCapturing = true;
951 return res;
952}
953
954// ----------------------------------------------------------------------
955//
956// SendProcess
957// sequence grabber data procedure
958//
959// This function is called by the capture device as soon as a new
960// frame is available.
961//
962//
963// SendFrame
964//
965// The non-static function used by the capture device callback
966//
967// Input:
968// sgChannel: the capture device channel generating the callback
969// data: the video frame
970// length: the data length in bytes
971// grabTime: time stamp generated by the capture device / sequece grabber
972//
973// ----------------------------------------------------------------------
974
975OSErr VideoCaptureMacQuickTime::SendProcess(SGChannel sgChannel, Ptr p,
976 long len, long* /*offset*/,
977 long /*chRefCon*/, TimeValue time,
978 short /*writeType*/, long refCon)
979{
980 VideoCaptureMacQuickTime* videoEngine =
981 reinterpret_cast<VideoCaptureMacQuickTime*> (refCon);
982 return videoEngine->SendFrame(sgChannel, (char*) p, len, time);
983}
984
985int VideoCaptureMacQuickTime::SendFrame(SGChannel /*sgChannel*/, char* data,
986 long length, TimeValue /*grabTime*/)
987{
988 if (!_sgPrepared)
989 {
990 WEBRTC_TRACE(webrtc::kTraceWarning, webrtc::kTraceVideoCapture, _id,
991 "%s:%d Sequence Grabber is not initialized", __FUNCTION__,
992 __LINE__);
993 return 0;
994 }
995
996 WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCapture, _id,
997 "%s:%d Frame has been delivered\n", __FUNCTION__, __LINE__);
998
999 CodecFlags ignore;
1000 _grabberCritsect->Enter();
1001 if (_gWorld)
1002 {
1003 // Will be set to true if we don't recognize the size and/or video
1004 // format.
1005 bool convertFrame = false;
1006 WebRtc_Word32 width = 352;
1007 WebRtc_Word32 height = 288;
1008 WebRtc_Word32 frameSize = 0;
1009
1010 VideoCaptureCapability captureCapability;
1011 captureCapability.width = width;
1012 captureCapability.height = height;
1013 captureCapability.maxFPS = 30;
1014
1015 switch (_captureVideoType)
1016 {
1017 case kVideoUYVY:
1018 captureCapability.rawType = kVideoUYVY;
1019 break;
1020 case kVideoYUY2:
1021 captureCapability.rawType = kVideoYUY2;
1022 break;
1023 case kVideoI420:
1024 captureCapability.rawType = kVideoI420;
1025 break;
1026 default:
1027 captureCapability.rawType = kVideoI420;
1028 WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCapture,
1029 _id, "%s:%d raw = I420 by default\n",
1030 __FUNCTION__, __LINE__);
1031 break;
1032 }
1033
1034 // Convert the camera video type to something VideoEngine can work with
1035 // Check if we need to downsample the incomming frame.
1036 switch (_captureVideoType)
1037 {
1038 case kVideoUYVY:
1039 case kVideoYUY2:
1040 frameSize = (width * height * 16) >> 3; // 16 is for YUY2 format
1041 if (width == _captureCapability.width || height
1042 == _captureCapability.height)
1043 {
1044 // Ok format and size, send the frame to super class
1045 IncomingFrame((WebRtc_UWord8*) data,
1046 (WebRtc_Word32) frameSize, captureCapability,
1047 TickTime::MillisecondTimestamp());
1048
1049 }
1050 else if (width == _trueCaptureWidth && height
1051 == _trueCaptureHeight)
1052 {
1053 // We need to scale the picture to correct size...
1054 // This happens for cameras not supporting all sizes.
1055 // E.g. older built-in iSight doesn't support QCIF.
1056
1057 // Convert the incoming frame into our GWorld.
1058 int res =
1059 DecompressSequenceFrameS(_captureSequence, data,
1060 length, 0, &ignore, NULL);
1061 if (res != noErr && res != -8976) // 8796 == black frame
1062 {
1063 WEBRTC_TRACE(webrtc::kTraceWarning,
1064 webrtc::kTraceVideoCapture, _id,
1065 "%s:%d Captured black frame. Not "
1066 "processing it", __FUNCTION__, __LINE__);
1067 _grabberCritsect->Leave();
1068 return 0;
1069 }
1070
1071 // Copy the frame from the PixMap to our video buffer
1072 PixMapHandle pixMap = GetGWorldPixMap(_gWorld);
1073
1074 // Lock the image data in the GWorld.
1075 LockPixels(pixMap);
1076
1077 // Get a pointer to the pixel data.
1078 Ptr capturedFrame = GetPixBaseAddr(pixMap);
1079
1080 // Send the converted frame out to super class
1081 IncomingFrame((WebRtc_UWord8*) data,
1082 (WebRtc_Word32) frameSize, captureCapability,
1083 TickTime::MillisecondTimestamp());
1084
1085 // Unlock the image data to get ready for the next frame.
1086 UnlockPixels(pixMap);
1087 }
1088 else
1089 {
1090 // Not a size we recognize, use the Mac internal scaling...
1091 convertFrame = true;
1092 WEBRTC_TRACE(webrtc::kTraceDebug,
1093 webrtc::kTraceVideoCapture, _id,
1094 "%s:%d Not correct incoming stream size for "
1095 "the format and configured size",
1096 __FUNCTION__, __LINE__);
1097 }
1098 break;
1099 default:
1100
1101 // Not a video format we recognize, use the Mac internal scaling
1102 WEBRTC_TRACE(webrtc::kTraceWarning, webrtc::kTraceVideoCapture,
1103 _id, "%s:%d Unknown video frame format (default)",
1104 __FUNCTION__, __LINE__);
1105 convertFrame = true;
1106 break;
1107 }
1108
1109 if (convertFrame)
1110 {
1111 WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCapture, _id,
1112 "%s:%d Unrecognized frame format. Converting frame",
1113 __FUNCTION__, __LINE__);
1114
1115 // We don't recognise the input format. Convert to UYVY, I420 is not
1116 // supported on osx. Decompress the grabbed frame into the GWorld,
1117 // i.e from webcam format to ARGB (RGB24), and extract the frame.
1118 int res = DecompressSequenceFrameS(_captureSequence, data, length,
1119 0, &ignore, NULL);
1120 if (res != noErr && res != -8976) // 8796 means a black frame
1121 {
1122 _grabberCritsect->Leave();
1123 return 0;
1124 }
1125
1126 // Copy the frame from the PixMap to our video buffer
1127 PixMapHandle rgbPixMap = GetGWorldPixMap(_gWorld);
1128 LockPixels(rgbPixMap);
1129 Ptr capturedFrame = GetPixBaseAddr(rgbPixMap);
1130
1131 // Get the picture size
1132 int width = (*rgbPixMap)->bounds.right;
1133 int height = (*rgbPixMap)->bounds.bottom;
1134
1135 // 16 is for YUY2 format.
1136 WebRtc_Word32 frameSize = (width * height * 16) >> 3;
1137
1138 // Ok format and size, send the frame to super class
1139 IncomingFrame((WebRtc_UWord8*) data, (WebRtc_Word32) frameSize,
1140 captureCapability, TickTime::MillisecondTimestamp());
1141
1142 UnlockPixels(rgbPixMap);
1143 }
1144
1145 // Tell the capture device it's ok to update.
1146 SGUpdate(_captureGrabber, NULL);
1147 }
1148 else
1149 {
1150 WEBRTC_TRACE(webrtc::kTraceWarning, webrtc::kTraceVideoCapture, _id,
1151 "%s:%d No GWorld created, but frames are being delivered",
1152 __FUNCTION__, __LINE__);
1153 }
1154
1155 _grabberCritsect->Leave();
1156 return 0;
1157}
1158
1159int VideoCaptureMacQuickTime::VideoCaptureInitThreadContext()
1160{
1161 WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCapture, _id,
1162 "%s:%d ", __FUNCTION__, __LINE__);
1163 _videoMacCritsect->Enter();
1164 EnterMoviesOnThread( kQTEnterMoviesFlagDontSetComponentsThreadMode);
1165 _videoMacCritsect->Leave();
1166 return 0;
1167}
1168
1169//
1170//
1171// Functions for handling capture devices
1172//
1173//
1174
1175VideoCaptureMacQuickTime::VideoCaptureMacName::VideoCaptureMacName() :
1176 _size(0)
1177{
1178 memset(_name, 0, kVideoCaptureMacNameMaxSize);
1179}
1180
1181int VideoCaptureMacQuickTime::VideoCaptureSetCaptureDevice(
1182 const char* deviceName, int size)
1183{
1184
1185
1186 _videoMacCritsect->Enter();
1187 bool wasCapturing = false;
1188
1189 _grabberCritsect->Enter();
1190 if (_captureGrabber)
1191 {
1192 // Stop grabbing, disconnect and close the old capture device
1193 StopQuickTimeCapture(&wasCapturing);
1194 DisconnectCaptureDevice();
1195 CloseComponent(_captureGrabber);
1196 _captureDevice = NULL;
1197 _captureGrabber = NULL;
1198 WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id,
1199 "%s:%d Old capture device removed", __FUNCTION__,
1200 __LINE__);
1201 }
1202
1203 if (deviceName == NULL || size == 0)
1204 {
1205 _grabberCritsect->Leave();
1206 _videoMacCritsect->Leave();
1207 return 0;
1208 }
1209
1210 if (size < 0)
1211 {
1212 _grabberCritsect->Leave();
1213 _videoMacCritsect->Leave();
1214 WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id,
1215 "%s:%d 'size' is not valid", __FUNCTION__, __LINE__);
1216 return 0;
1217 }
1218
1219 ComponentDescription compCaptureType;
1220
1221 // Define the component we want to open
1222 compCaptureType.componentType = SeqGrabComponentType;
1223 compCaptureType.componentSubType = 0;
1224 compCaptureType.componentManufacturer = 0;
1225 compCaptureType.componentFlags = 0;
1226 compCaptureType.componentFlagsMask = 0;
1227
1228 long numSequenceGrabbers = CountComponents(&compCaptureType);
1229
1230 // loop through the available grabbers and open the first possible
1231 for (int i = 0; i < numSequenceGrabbers; i++)
1232 {
1233 _captureDevice = FindNextComponent(0, &compCaptureType);
1234 _captureGrabber = OpenComponent(_captureDevice);
1235 if (_captureGrabber != NULL)
1236 {
1237 // We've found a sequencegrabber that we could open
1238 if (SGInitialize(_captureGrabber) != noErr)
1239 {
1240 _grabberCritsect->Leave();
1241 _videoMacCritsect->Leave();
1242 WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture,
1243 _id,
1244 "%s:%d Could not initialize sequence grabber",
1245 __FUNCTION__, __LINE__);
1246 return -1;
1247 }
1248 break;
1249 }
1250 if (i == numSequenceGrabbers - 1)
1251 {
1252 // Couldn't open a sequence grabber
1253 _grabberCritsect->Leave();
1254 _videoMacCritsect->Leave();
1255 WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id,
1256 "%s:%d Could not open a sequence grabber",
1257 __FUNCTION__, __LINE__);
1258 return -1;
1259 }
1260 }
1261
1262 if (!_gWorld)
1263 {
1264 // We don't have a GWorld. Create one to enable early preview
1265 // without calling SetSendCodec
1266 if (CreateLocalGWorld(_captureCapability.width,
1267 _captureCapability.height) == -1)
1268 {
1269 // Error already logged
1270 _grabberCritsect->Leave();
1271 _videoMacCritsect->Leave();
1272 return -1;
1273 }
1274 }
1275 // Connect the camera with our GWorld
1276 int cpySize = size;
1277 if ((unsigned int) size > sizeof(_captureDeviceDisplayName))
1278 {
1279 cpySize = sizeof(_captureDeviceDisplayName);
1280 }
1281 WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCapture, _id,
1282 "%s:%d Copying %d chars from deviceName to "
1283 "_captureDeviceDisplayName (size=%d)\n",
1284 __FUNCTION__, __LINE__, cpySize, size);
1285 memcpy(_captureDeviceDisplayName, deviceName, cpySize);
1286 if (ConnectCaptureDevice() == -1)
1287 {
1288 // Error already logged
1289 _grabberCritsect->Leave();
1290 _videoMacCritsect->Leave();
1291 return -1;
1292 }
1293
1294 if (StartQuickTimeCapture() == -1)
1295 {
1296 // Error already logged
1297 _grabberCritsect->Leave();
1298 _videoMacCritsect->Leave();
1299 return -1;
1300 }
1301 _grabberCritsect->Leave();
1302 _videoMacCritsect->Leave();
1303 return 0;
1304}
1305
1306bool VideoCaptureMacQuickTime::IsCaptureDeviceSelected()
1307{
1308 _grabberCritsect->Leave();
1309 return (_captureIsInitialized) ? true : false;
1310 _grabberCritsect->Leave();
1311}
1312
1313/**
1314 Convert a Pascal string to a C string.
1315
1316 \param[in] pascalString
1317 Pascal string to convert. Pascal strings contain the number of
1318 characters in the first byte and are not null-terminated.
1319
1320 \param[out] cString
1321 The C string buffer into which to copy the converted string.
1322
1323 \param[in] bufferSize
1324 The size of the C string buffer in bytes.
1325
1326 \return The number of characters in the string on success and -1 on failure.
1327 */
1328CFIndex VideoCaptureMacQuickTime::PascalStringToCString(
1329 const unsigned char* pascalString, char* cString, CFIndex bufferSize)
1330{
1331
1332 WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCapture, 0,
1333 "%s:%d Converting pascal string to c string", __FUNCTION__,
1334 __LINE__);
1335 if (pascalString == NULL)
1336 {
1337 return -1;
1338 }
1339
1340 if (cString == NULL)
1341 {
1342 return -1;
1343 }
1344
1345 if (bufferSize == 0)
1346 {
1347 return -1;
1348 }
1349
1350 CFIndex cStringLength = 0;
1351 CFIndex maxStringLength = bufferSize - 1;
1352
1353 CFStringRef cfString = CFStringCreateWithPascalString(
1354 NULL, pascalString, kCFStringEncodingMacRoman);
1355 if (cfString == NULL)
1356 {
1357 WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCapture, 0,
1358 "%s:%d Error in CFStringCreateWithPascalString()",
1359 __FUNCTION__, __LINE__);
1360 CFRelease(cfString);
1361 return -1;
1362 }
1363
1364 CFIndex cfLength = CFStringGetLength(cfString);
1365 cStringLength = cfLength;
1366 if (cfLength > maxStringLength)
1367 {
1368 cStringLength = maxStringLength;
1369 }
1370
1371 Boolean success = CFStringGetCString(cfString, cString, bufferSize,
1372 kCFStringEncodingMacRoman);
1373
1374 // Ensure the problem isn't insufficient buffer length.
1375 // This is fine; we will return a partial string.
1376 if (success == false && cfLength <= maxStringLength)
1377 {
1378 WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCapture, 0,
1379 "%s:%d Error in CFStringGetCString()", __FUNCTION__,
1380 __LINE__);
1381 CFRelease(cfString);
1382 return -1;
1383 }
1384
1385 CFRelease(cfString);
1386 return cStringLength;
1387}
1388} // namespace webrtc