| /* |
| * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. |
| * |
| * Use of this source code is governed by a BSD-style license |
| * that can be found in the LICENSE file in the root of the source |
| * tree. An additional intellectual property rights grant can be found |
| * in the file PATENTS. All contributing project authors may |
| * be found in the AUTHORS file in the root of the source tree. |
| */ |
| |
| /* |
| * video_capture_quick_time.cc |
| * |
| */ |
| |
| |
| #include "video_capture_quick_time.h" |
| |
| #include "CriticalSectionWrapper.h" |
| #include "event_wrapper.h" |
| #include "thread_wrapper.h" |
| #include "tick_util.h" |
| #include "trace.h" |
| #include <unistd.h> |
| |
| namespace webrtc |
| { |
| |
| VideoCaptureMacQuickTime::VideoCaptureMacQuickTime(WebRtc_Word32 iID) : |
| VideoCaptureImpl(iID), // super class constructor |
| _id(iID), |
| _isCapturing(false), |
| _captureCapability(), |
| _grabberCritsect(CriticalSectionWrapper::CreateCriticalSection()), |
| _videoMacCritsect(CriticalSectionWrapper::CreateCriticalSection()), |
| _terminated(true), _grabberUpdateThread(NULL), |
| _grabberUpdateEvent(NULL), _captureGrabber(NULL), _captureDevice(NULL), |
| _captureVideoType(kVideoUnknown), _captureIsInitialized(false), |
| _gWorld(NULL), _captureChannel(0), _captureSequence(NULL), |
| _sgPrepared(false), _sgStarted(false), _trueCaptureWidth(0), |
| _trueCaptureHeight(0), _captureDeviceList(), |
| _captureDeviceListTime(0), _captureCapabilityList() |
| |
| { |
| WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceVideoCapture, _id, |
| "%s:%d", __FUNCTION__, __LINE__); |
| _captureCapability.width = START_CODEC_WIDTH; |
| _captureCapability.height = START_CODEC_HEIGHT; |
| memset(_captureDeviceDisplayName, 0, sizeof(_captureDeviceDisplayName)); |
| } |
| |
| VideoCaptureMacQuickTime::~VideoCaptureMacQuickTime() |
| { |
| |
| WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceVideoCapture, _id, |
| "%s:%d", __FUNCTION__, __LINE__); |
| |
| VideoCaptureTerminate(); |
| |
| if (_videoMacCritsect) |
| { |
| delete _videoMacCritsect; |
| } |
| if (_grabberCritsect) |
| { |
| delete _grabberCritsect; |
| } |
| |
| } |
| |
| WebRtc_Word32 VideoCaptureMacQuickTime::Init( |
| const WebRtc_Word32 id, const WebRtc_UWord8* deviceUniqueIdUTF8) |
| { |
| WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceVideoCapture, _id, |
| "%s:%d", __FUNCTION__, __LINE__); |
| |
| const WebRtc_Word32 nameLength = |
| (WebRtc_Word32) strlen((char*) deviceUniqueIdUTF8); |
| if (nameLength > kVideoCaptureUniqueNameLength) |
| return -1; |
| |
| // Store the device name |
| _deviceUniqueId = new WebRtc_UWord8[nameLength + 1]; |
| memset(_deviceUniqueId, 0, nameLength + 1); |
| memcpy(_deviceUniqueId, deviceUniqueIdUTF8, nameLength + 1); |
| |
| // Check OSX version |
| OSErr err = noErr; |
| long version; |
| |
| _videoMacCritsect->Enter(); |
| if (!_terminated) |
| { |
| _videoMacCritsect->Leave(); |
| WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id, |
| "%s:%d Already Initialized", __FUNCTION__, __LINE__); |
| return -1; |
| } |
| |
| err = Gestalt(gestaltSystemVersion, &version); |
| if (err != noErr) |
| { |
| _videoMacCritsect->Leave(); |
| WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id, |
| "%s:%d Could not retrieve OS version", __FUNCTION__, |
| __LINE__); |
| return -1; |
| } |
| |
| WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideoCapture, _id, |
| "%s:%d OS X version: %x,", __FUNCTION__, __LINE__, version); |
| if (version < 0x00001040) // Older version than Mac OSX 10.4 |
| { |
| _videoMacCritsect->Leave(); |
| WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id, |
| "%s:%d OS version not supported", __FUNCTION__, __LINE__); |
| return -1; |
| } |
| |
| err = Gestalt(gestaltQuickTime, &version); |
| if (err != noErr) |
| { |
| _videoMacCritsect->Leave(); |
| WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id, |
| "%s:%d Could not retrieve QuickTime version", |
| __FUNCTION__, __LINE__); |
| return -1; |
| } |
| |
| WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideoCapture, _id, |
| "%s:%d QuickTime version: %x", __FUNCTION__, __LINE__, |
| version); |
| if (version < 0x07000000) // QT v. 7.x or newer (QT 5.0.2 0x05020000) |
| { |
| _videoMacCritsect->Leave(); |
| WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceVideoCapture, _id, |
| "%s:%d QuickTime version too old. Need 7 or newer", |
| __FUNCTION__, __LINE__); |
| return -1; |
| } |
| |
| WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCapture, _id, |
| "%s:%d EnterMovies()", __FUNCTION__, __LINE__); |
| EnterMovies(); |
| |
| if (VideoCaptureSetCaptureDevice((char*) deviceUniqueIdUTF8, |
| kVideoCaptureProductIdLength) == -1) |
| { |
| WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id, |
| "%s:%d failed to set capture device: %s", __FUNCTION__, |
| __LINE__, deviceUniqueIdUTF8); |
| _videoMacCritsect->Leave(); |
| return -1; |
| } |
| |
| _terminated = false; |
| |
| WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideoCapture, _id, |
| "%s:%d successful initialization", __FUNCTION__, __LINE__); |
| _videoMacCritsect->Leave(); |
| |
| return 0; |
| } |
| |
| WebRtc_Word32 VideoCaptureMacQuickTime::StartCapture( |
| const VideoCaptureCapability& capability) |
| { |
| WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideoCapture, _id, "%s:%d " |
| "capability.width=%d, capability.height=%d ,capability.maxFPS=%d " |
| "capability.expectedCaptureDelay=%d, capability.interlaced=%d", |
| __FUNCTION__, __LINE__, capability.width, capability.height, |
| capability.maxFPS, capability.expectedCaptureDelay, |
| capability.interlaced); |
| |
| _captureCapability.width = capability.width; |
| _captureCapability.height = capability.height; |
| |
| if (VideoCaptureRun() == -1) |
| { |
| return -1; |
| } |
| |
| return 0; |
| } |
| |
| WebRtc_Word32 VideoCaptureMacQuickTime::StopCapture() |
| { |
| |
| if (VideoCaptureStop() == -1) |
| { |
| return -1; |
| } |
| |
| return 0; |
| } |
| |
| bool VideoCaptureMacQuickTime::CaptureStarted() |
| { |
| return _isCapturing; |
| } |
| |
| WebRtc_Word32 VideoCaptureMacQuickTime::CaptureSettings( |
| VideoCaptureCapability& settings) |
| { |
| settings.width = _captureCapability.width; |
| settings.height = _captureCapability.height; |
| settings.maxFPS = 0; |
| return 0; |
| } |
| |
| int VideoCaptureMacQuickTime::VideoCaptureTerminate() |
| { |
| WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceVideoCapture, _id, |
| "%s:%d", __FUNCTION__, __LINE__); |
| VideoCaptureStop(); |
| |
| _videoMacCritsect->Enter(); |
| if (_terminated) |
| { |
| _videoMacCritsect->Leave(); |
| WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id, |
| "%s:%d Already terminated", __FUNCTION__, __LINE__); |
| return -1; |
| } |
| |
| _grabberCritsect->Enter(); |
| |
| // Stop the camera/sequence grabber |
| // Resets: _captureSequence, _sgStarted |
| StopQuickTimeCapture(); |
| |
| // Remove local video settings |
| // Resets: _gWorld, _captureCapability.width, _captureCapability.height |
| RemoveLocalGWorld(); |
| DisconnectCaptureDevice(); |
| |
| if (_grabberUpdateThread) |
| _grabberUpdateThread->SetNotAlive(); |
| |
| _grabberCritsect->Leave(); |
| |
| if (_grabberUpdateEvent) |
| _grabberUpdateEvent->Set(); |
| |
| SLEEP(1); |
| _grabberCritsect->Enter(); |
| |
| if (_grabberUpdateThread) |
| { |
| _grabberUpdateThread->Stop(); |
| delete _grabberUpdateThread; |
| _grabberUpdateThread = NULL; |
| } |
| if (_grabberUpdateEvent) |
| { |
| delete _grabberUpdateEvent; |
| _grabberUpdateEvent = NULL; |
| } |
| |
| // Close the sequence grabber |
| if (_captureGrabber) |
| { |
| SGRelease(_captureGrabber); |
| _captureGrabber = NULL; |
| CloseComponent(_captureGrabber); |
| _captureDevice = NULL; |
| } |
| _captureVideoType = kVideoUnknown; |
| |
| // Delete capture device list |
| ListItem* item = _captureDeviceList.First(); |
| while (item) |
| { |
| delete static_cast<unsigned char*> (item->GetItem()); |
| _captureDeviceList.Erase(item); |
| item = _captureDeviceList.First(); |
| } |
| _captureDeviceListTime = 0; |
| |
| _terminated = true; |
| |
| _grabberCritsect->Leave(); |
| _videoMacCritsect->Leave(); |
| |
| return 0; |
| } |
| |
| int VideoCaptureMacQuickTime::UpdateCaptureSettings(int channel, |
| webrtc::VideoCodec& inst, |
| bool def) |
| { |
| WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceVideoCapture, _id, |
| "%s:%d channel: %d", __FUNCTION__, __LINE__, channel); |
| |
| if (channel < 0) |
| { |
| WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id, |
| "%s:%d Invalid channel number: %d", __FUNCTION__, |
| __LINE__, channel); |
| return -1; |
| } |
| |
| // the size has changed, we need to change our setup |
| _videoMacCritsect->Enter(); |
| |
| // Stop capturing, if we are... |
| _grabberCritsect->Enter(); |
| |
| bool wasCapturing = false; |
| StopQuickTimeCapture(&wasCapturing); |
| |
| // Create a new offline GWorld to receive captured frames |
| RemoveLocalGWorld(); |
| |
| if (CreateLocalGWorld(inst.width, inst.height) == -1) |
| { |
| _grabberCritsect->Leave(); |
| _videoMacCritsect->Leave(); |
| // Error already logged |
| return -1; |
| } |
| _captureCapability.width = inst.width; |
| _captureCapability.height = inst.height; |
| |
| // Connect the capture device to our offline GWorld |
| // if we already have a capture device selected. |
| if (_captureDevice) |
| { |
| DisconnectCaptureDevice(); |
| if (ConnectCaptureDevice() == -1) |
| { |
| // Error already logged |
| _grabberCritsect->Leave(); |
| _videoMacCritsect->Leave(); |
| return -1; |
| } |
| } |
| |
| // Start capture if we did before |
| if (wasCapturing) |
| { |
| if (StartQuickTimeCapture() == -1) |
| { |
| _grabberCritsect->Leave(); |
| _videoMacCritsect->Leave(); |
| WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id, |
| "%s:%d Failed to start capturing", __FUNCTION__, |
| __LINE__); |
| return -1; |
| } |
| } |
| _grabberCritsect->Leave(); |
| _videoMacCritsect->Leave(); |
| |
| return 0; |
| } |
| |
| // Creates an off screen graphics world used for converting |
| // captured video frames if we can't get a format we want. |
| // Assumed protected by critsects |
| int VideoCaptureMacQuickTime::CreateLocalGWorld(int width, int height) |
| { |
| if (_gWorld) |
| { |
| WEBRTC_TRACE(webrtc::kTraceWarning, webrtc::kTraceVideoCapture, _id, |
| "%s:%d GWorld already created", __FUNCTION__, __LINE__); |
| return -1; |
| } |
| if (width == 0 || height == 0) |
| { |
| WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceVideoCapture, _id, |
| "%s:%d Invalid dimensions width:%d height:%d", |
| __FUNCTION__, __LINE__, width, height); |
| return -1; |
| } |
| |
| Rect captureRect; |
| captureRect.left = 0; |
| captureRect.top = 0; |
| captureRect.right = width; |
| captureRect.bottom = height; |
| |
| // Create a GWorld in same size as we want to send to the codec |
| if (QTNewGWorld(&(_gWorld), k2vuyPixelFormat, &captureRect, 0, NULL, 0) |
| != noErr) |
| { |
| return -1; |
| } |
| _captureCapability.width = width; |
| _captureCapability.height = height; |
| |
| if (!LockPixels(GetGWorldPixMap(_gWorld))) |
| { |
| WEBRTC_TRACE(webrtc::kTraceWarning, webrtc::kTraceVideoCapture, _id, |
| "%s:%d Could not lock pixmap. Continuing anyhow", |
| __FUNCTION__, __LINE__); |
| } |
| |
| CGrafPtr theOldPort; |
| GDHandle theOldDevice; |
| GetGWorld(&theOldPort, &theOldDevice); // Gets the result from QTGetNewGWorld |
| SetGWorld(_gWorld, NULL); // Sets the new GWorld |
| BackColor( blackColor); // Changes the color on the graphic port |
| ForeColor( whiteColor); |
| EraseRect(&captureRect); |
| SetGWorld(theOldPort, theOldDevice); |
| |
| return 0; |
| } |
| |
| // Assumed critsect protected |
| int VideoCaptureMacQuickTime::RemoveLocalGWorld() |
| { |
| if (!_gWorld) |
| { |
| WEBRTC_TRACE(webrtc::kTraceWarning, webrtc::kTraceVideoCapture, _id, |
| "%s:%d !gWorld", __FUNCTION__, __LINE__); |
| return -1; |
| } |
| |
| DisposeGWorld(_gWorld); |
| _gWorld = NULL; |
| _captureCapability.width = START_CODEC_WIDTH; |
| _captureCapability.height = START_CODEC_HEIGHT; |
| |
| WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideoCapture, _id, |
| "%s:%d GWorld has been removed", __FUNCTION__, __LINE__); |
| return 0; |
| } |
| |
| // ConnectCaptureDevice |
| // This function prepares the capture device |
| // with the wanted settings, but the capture |
| // device isn't started. |
| // |
| // Assumed critsect protected |
| int VideoCaptureMacQuickTime::ConnectCaptureDevice() |
| { |
| // Prepare the capture grabber if a capture device is already set |
| if (!_captureGrabber) |
| { |
| WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id, |
| "%s:%d No capture device is selected", __FUNCTION__, |
| __LINE__); |
| return -1; |
| } |
| if (_captureIsInitialized) |
| { |
| WEBRTC_TRACE(webrtc::kTraceWarning, webrtc::kTraceVideoCapture, _id, |
| "%s:%d Capture device is already initialized", |
| __FUNCTION__, __LINE__); |
| return -1; |
| } |
| if (!_gWorld) |
| { |
| WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id, |
| "%s:%d No GWorld is created", __FUNCTION__, __LINE__); |
| return -1; |
| } |
| |
| OSErr err = noErr; |
| long flags = 0; |
| |
| // Connect the camera to our offline GWorld |
| // We won't use the GWorld if we get the format we want |
| // from the camera. |
| if (SGSetGWorld(_captureGrabber, _gWorld, NULL ) != noErr) |
| { |
| WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id, |
| "%s:%d Could not connect capture device", __FUNCTION__, |
| __LINE__); |
| return -1; |
| } |
| if (SGSetDataRef(_captureGrabber, 0, 0, seqGrabDontMakeMovie) != noErr) |
| { |
| WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id, |
| "%s:%d Could not configure capture device", __FUNCTION__, |
| __LINE__); |
| return -1; |
| } |
| |
| // Set our capture callback |
| if (SGSetDataProc(_captureGrabber, NewSGDataUPP(SendProcess), (long) this) |
| != noErr) |
| { |
| WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id, |
| "%s:%d Could not set capture callback. Unable to receive " |
| "frames", __FUNCTION__, __LINE__); |
| return -1; |
| } |
| |
| // Create a video channel to the sequence grabber |
| if (SGNewChannel(_captureGrabber, VideoMediaType, &_captureChannel) |
| != noErr) // Takes time!!! |
| { |
| WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceVideoCapture, _id, |
| "%s:%d Could not create sequence grabber channel", |
| __FUNCTION__, __LINE__); |
| return -1; |
| } |
| |
| // Get a list with all capture devices to choose the one we want. |
| SGDeviceList deviceList = NULL; |
| if (SGGetChannelDeviceList(_captureChannel, sgDeviceListIncludeInputs, |
| &deviceList) != noErr) |
| { |
| |
| } |
| |
| int numDevicesTypes = (*deviceList)->count; |
| bool captureDeviceFound = false; |
| WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCapture, _id, |
| "%s:%d Found %d channel devices", __FUNCTION__, __LINE__, |
| numDevicesTypes); |
| |
| // Loop through all devices to get the one we want. |
| for (int i = 0; i < numDevicesTypes; i++) |
| { |
| SGDeviceName deviceTypeName = (*deviceList)->entry[i]; |
| WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCapture, _id, |
| "%s:%d Inspecting device number: %d", __FUNCTION__, |
| __LINE__, i); |
| // Get the list with input devices |
| if (deviceTypeName.inputs) |
| { |
| SGDeviceInputList inputList = deviceTypeName.inputs; |
| int numInputDev = (*inputList)->count; |
| WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCapture, _id, |
| "%s:%d Device has %d inputs", __FUNCTION__, __LINE__, |
| numInputDev); |
| for (int inputDevIndex = 0; |
| inputDevIndex < numInputDev; |
| inputDevIndex++) |
| { |
| WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCapture, |
| _id, "%s:%d Inspecting input number: %d", |
| __FUNCTION__, __LINE__, inputDevIndex); |
| SGDeviceInputName deviceInputName = |
| (*inputList)->entry[inputDevIndex]; |
| char devInName[64]; |
| memset(devInName, 0, 64); |
| |
| // SGDeviceInputName::name is a Str63, defined as a Pascal string. |
| // (Refer to MacTypes.h) |
| CFIndex devInNameLength = |
| PascalStringToCString(deviceInputName.name, devInName, |
| sizeof(devInName)); |
| WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCapture, |
| _id, |
| "%s:%d Converted pascal string with length:%d " |
| "to: %s", __FUNCTION__, __LINE__, |
| sizeof(devInName), devInName); |
| if (devInNameLength < 0) |
| { |
| WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, |
| _id, |
| "%s:%d Failed to convert device name from " |
| "pascal string to c string", __FUNCTION__, |
| __LINE__); |
| return -1; |
| } |
| |
| if (!strcmp(devInName, _captureDeviceDisplayName)) |
| { |
| WEBRTC_TRACE(webrtc::kTraceDebug, |
| webrtc::kTraceVideoCapture, _id, |
| "%s:%d We have found our device: %s", |
| __FUNCTION__, __LINE__, |
| _captureDeviceDisplayName); |
| |
| if (SGSetChannelDevice(_captureChannel, deviceTypeName.name) |
| != noErr) |
| { |
| WEBRTC_TRACE(webrtc::kTraceError, |
| webrtc::kTraceVideoCapture, _id, |
| "%s:%d Could not set capture device type: " |
| "%s",__FUNCTION__, __LINE__, |
| deviceTypeName.name); |
| return -1; |
| } |
| |
| WEBRTC_TRACE(webrtc::kTraceInfo, |
| webrtc::kTraceVideoCapture, _id, |
| "%s:%d Capture device type is: %s", |
| __FUNCTION__, __LINE__, deviceTypeName.name); |
| if (SGSetChannelDeviceInput(_captureChannel, inputDevIndex) |
| != noErr) |
| { |
| WEBRTC_TRACE(webrtc::kTraceError, |
| webrtc::kTraceVideoCapture, _id, |
| "%s:%d Could not set SG device", |
| __FUNCTION__, __LINE__); |
| return -1; |
| } |
| |
| WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideoCapture, |
| _id, |
| "%s:%d Capture device: %s has successfully " |
| "been set", __FUNCTION__, __LINE__, |
| _captureDeviceDisplayName); |
| captureDeviceFound = true; |
| break; |
| } |
| } |
| if (captureDeviceFound) |
| { |
| WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCapture, |
| _id, |
| "%s:%d Capture device found, breaking from loops", |
| __FUNCTION__, __LINE__); |
| break; |
| } |
| } |
| } |
| err = SGDisposeDeviceList(_captureGrabber, deviceList); |
| |
| if (!captureDeviceFound) |
| { |
| WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id, |
| "%s:%d Failed to find capture device: %s. Returning -1", |
| __FUNCTION__, __LINE__, _captureDeviceDisplayName); |
| return -1; |
| } |
| |
| // Set the size we want from the capture device |
| Rect captureSize; |
| captureSize.left = 0; |
| captureSize.top = 0; |
| captureSize.right = _captureCapability.width; |
| captureSize.bottom = _captureCapability.height; |
| |
| WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideoCapture, _id, |
| "%s:%d Using capture rect: l:%d t:%d r:%d b:%d", __FUNCTION__, |
| __LINE__, captureSize.left, captureSize.top, |
| captureSize.right, captureSize.bottom); |
| |
| err = SGSetChannelBounds(_captureChannel, &captureSize); |
| if (err == noErr) |
| { |
| err = SGSetChannelUsage(_captureChannel, flags | seqGrabRecord); |
| } |
| if (err != noErr) |
| { |
| SGDisposeChannel(_captureGrabber, _captureChannel); |
| WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id, |
| "%s:%d Error setting SG channel to device", __FUNCTION__, |
| __LINE__); |
| return -1; |
| } |
| |
| // Find out what video format we'll get from the capture device. |
| OSType compType; |
| err = SGGetVideoCompressorType(_captureChannel, &compType); |
| |
| // Convert the Apple video format name to a VideoCapture name. |
| if (compType == k2vuyPixelFormat) |
| { |
| _captureVideoType = kVideoUYVY; |
| WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideoCapture, _id, |
| "%s:%d Device delivers UYUV formatted frames", |
| __FUNCTION__, __LINE__); |
| } |
| else if (compType == kYUVSPixelFormat) |
| { |
| WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideoCapture, _id, |
| "%s:%d Device delivers YUY2 formatted frames", |
| __FUNCTION__, __LINE__); |
| _captureVideoType = kVideoYUY2; |
| } |
| else |
| { |
| WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCapture, _id, |
| "%s:%d Device delivers frames in an unknown format: 0x%x. " |
| "Consult QuickdrawTypes.h", |
| __FUNCTION__, __LINE__, compType); |
| WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideoCapture, _id, |
| "%s:%d Device delivers frames in an unknown format.", |
| __FUNCTION__, __LINE__); |
| _captureVideoType = kVideoUnknown; |
| } |
| |
| if (SGPrepare(_captureGrabber, false, true) != noErr) |
| { |
| _grabberCritsect->Leave(); |
| WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id, |
| "%s:%d Error starting sequence grabber", __FUNCTION__, |
| __LINE__); |
| return -1; |
| } |
| |
| // Try to set the codec size as capture size. |
| err = SGSetChannelBounds(_captureChannel, &captureSize); |
| |
| // Check if we really will get the size we asked for. |
| ImageDescriptionHandle imageDesc = (ImageDescriptionHandle) NewHandle(0); |
| err = SGGetChannelSampleDescription(_captureChannel, (Handle) imageDesc); |
| |
| _trueCaptureWidth = (**imageDesc).width; |
| _trueCaptureHeight = (**imageDesc).height; |
| |
| DisposeHandle((Handle) imageDesc); |
| |
| _captureIsInitialized = true; |
| _sgPrepared = true; |
| |
| WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideoCapture, _id, |
| "%s:%d Success starting sequence grabber", __FUNCTION__, |
| __LINE__); |
| |
| return 0; |
| } |
| |
| // Assumed critsect protected |
| int VideoCaptureMacQuickTime::DisconnectCaptureDevice() |
| { |
| if (_sgStarted) |
| { |
| WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id, |
| "%s:%d Capture device is still running. Returning -1", |
| __FUNCTION__, __LINE__); |
| return -1; |
| } |
| if (!_sgPrepared) |
| { |
| WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id, |
| "%s:%d No capture device connected", __FUNCTION__, |
| __LINE__); |
| return -1; |
| } |
| |
| // Close the capture channel |
| SGStop(_captureGrabber); |
| WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCapture, _id, |
| "%s:%d !!!! releasing sg stuff", __FUNCTION__, __LINE__); |
| SGDisposeChannel(_captureGrabber, _captureChannel); |
| SGRelease(_captureGrabber); |
| CloseComponent(_captureGrabber); |
| |
| // Reset all values |
| _captureChannel = NULL; |
| _captureVideoType = kVideoUnknown; |
| _trueCaptureWidth = 0; |
| _trueCaptureHeight = 0; |
| _captureIsInitialized = false; |
| _sgPrepared = false; |
| |
| WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideoCapture, _id, |
| "%s:%d Sequence grabber removed", __FUNCTION__, __LINE__); |
| |
| return 0; |
| } |
| |
| // StartQuickTimeCapture |
| // |
| // Actually starts the camera |
| // |
| int VideoCaptureMacQuickTime::StartQuickTimeCapture() |
| { |
| _grabberCritsect->Enter(); |
| WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideoCapture, _id, |
| "%s:%d Attempting to start sequence grabber", __FUNCTION__, |
| __LINE__); |
| |
| if (_sgStarted) |
| { |
| _grabberCritsect->Leave(); |
| WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id, |
| "%s:%d Sequence grabber already started", __FUNCTION__, |
| __LINE__); |
| return 0; |
| } |
| if (!_sgPrepared) |
| { |
| _grabberCritsect->Leave(); |
| WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id, |
| "%s:%d Sequence grabber not prepared properly", |
| __FUNCTION__, __LINE__); |
| return 0; |
| } |
| |
| if (SGStartRecord(_captureGrabber) != noErr) |
| { |
| _grabberCritsect->Leave(); |
| WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id, |
| "%s:%d Error starting sequence grabber", __FUNCTION__, |
| __LINE__); |
| return -1; |
| } |
| |
| Rect captureRect = { 0, 0, 0, 0 }; |
| MatrixRecord scaleMatrix; |
| ImageDescriptionHandle imageDesc = (ImageDescriptionHandle) NewHandle(0); |
| |
| // Get the sample description for the channel, which is the same as for the |
| // capture device |
| if (SGGetChannelSampleDescription(_captureChannel, (Handle) imageDesc) |
| != noErr) |
| { |
| _grabberCritsect->Leave(); |
| WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id, |
| "%s:%d Error accessing device properties", __FUNCTION__, |
| __LINE__); |
| return -1; |
| } |
| |
| // Create a scale matrix to scale the captured image |
| // Needed if we don't get the size wanted from the camera |
| captureRect.right = (**imageDesc).width; |
| captureRect.bottom = (**imageDesc).height; |
| |
| Rect codecRect; |
| codecRect.left = 0; |
| codecRect.top = 0; |
| codecRect.right = _captureCapability.width; |
| codecRect.bottom = _captureCapability.height; |
| RectMatrix(&scaleMatrix, &captureRect, &codecRect); |
| |
| // Start grabbing images from the capture device to _gWorld |
| if (DecompressSequenceBegin(&_captureSequence, imageDesc, _gWorld, NULL, |
| NULL, &scaleMatrix, srcCopy, (RgnHandle) NULL, |
| NULL, codecNormalQuality, bestSpeedCodec) |
| != noErr) |
| { |
| _grabberCritsect->Leave(); |
| WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideoCapture, _id, |
| "%s:%d Error starting decompress sequence", __FUNCTION__, |
| __LINE__); |
| return -1; |
| } |
| DisposeHandle((Handle) imageDesc); |
| _sgStarted = true; |
| _grabberCritsect->Leave(); |
| return 0; |
| } |
| |
| int VideoCaptureMacQuickTime::StopQuickTimeCapture(bool* wasCapturing) |
| { |
| _grabberCritsect->Enter(); |
| WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideoCapture, _id, |
| "%s:%d wasCapturing=%d", __FUNCTION__, __LINE__, wasCapturing); |
| |
| if (!_sgStarted) |
| { |
| if (wasCapturing) |
| *wasCapturing = false; |
| |
| _grabberCritsect->Leave(); |
| WEBRTC_TRACE(webrtc::kTraceWarning, webrtc::kTraceVideoCapture, _id, |
| "%s:%d Sequence grabber was never started", __FUNCTION__, |
| __LINE__); |
| return 0; |
| } |
| |
| if (wasCapturing) |
| *wasCapturing = true; |
| |
| OSErr error = noErr; |
| error = SGStop(_captureGrabber); |
| CDSequenceEnd(_captureSequence); |
| _captureSequence = NULL; |
| _sgStarted = false; |
| |
| _grabberCritsect->Leave(); |
| if (error != noErr) |
| { |
| WEBRTC_TRACE(webrtc::kTraceWarning, webrtc::kTraceVideoCapture, _id, |
| "%s:%d Could not stop sequence grabber", __FUNCTION__, |
| __LINE__); |
| return -1; |
| } |
| |
| return 0; |
| } |
| |
| //------------------------------------------------- |
| // |
| // Thread/function to keep capture device working |
| // |
| //------------------------------------------------- |
| |
| |
| // |
| // GrabberUpdateThread / GrabberUpdateProcess |
| // |
| // Called at a certain time interval to tell |
| // the capture device / SequenceGrabber to |
| // actually work. |
| bool VideoCaptureMacQuickTime::GrabberUpdateThread(void* obj) |
| { |
| return static_cast<VideoCaptureMacQuickTime*> (obj)->GrabberUpdateProcess(); |
| } |
| |
| bool VideoCaptureMacQuickTime::GrabberUpdateProcess() |
| { |
| _grabberUpdateEvent->Wait(30); |
| |
| if (_isCapturing == false) |
| return false; |
| |
| _grabberCritsect->Enter(); |
| if (_captureGrabber) |
| { |
| if (SGIdle(_captureGrabber) != noErr) |
| { |
| } |
| } |
| _grabberCritsect->Leave(); |
| return true; |
| } |
| |
| // |
| // VideoCaptureStop |
| // |
| // Stops the capture device |
| // |
| int VideoCaptureMacQuickTime::VideoCaptureStop() |
| { |
| if (_grabberUpdateThread) |
| { |
| _grabberUpdateThread->Stop(); |
| } |
| |
| _videoMacCritsect->Enter(); |
| _grabberCritsect->Enter(); |
| int retVal = StopQuickTimeCapture(); |
| _grabberCritsect->Leave(); |
| _videoMacCritsect->Leave(); |
| if (retVal == -1) |
| { |
| return -1; |
| } |
| |
| _isCapturing = false; |
| return 0; |
| } |
| |
| // |
| // VideoCaptureRun |
| // |
| // Starts the capture device and creates |
| // the update thread. |
| // |
| int VideoCaptureMacQuickTime::VideoCaptureRun() |
| { |
| _videoMacCritsect->Enter(); |
| _grabberCritsect->Enter(); |
| |
| int res = StartQuickTimeCapture(); |
| |
| // Create the thread for updating sequence grabber if not created earlier |
| if (!_grabberUpdateThread) |
| { |
| _grabberUpdateEvent = EventWrapper::Create(); |
| _grabberUpdateThread = ThreadWrapper::CreateThread( |
| VideoCaptureMacQuickTime::GrabberUpdateThread, this, kHighPriority); |
| unsigned int id; |
| _grabberUpdateThread->Start(id); |
| } |
| else |
| { |
| unsigned int id; |
| _grabberUpdateThread->Start(id); |
| } |
| |
| _grabberCritsect->Leave(); |
| _videoMacCritsect->Leave(); |
| |
| _isCapturing = true; |
| return res; |
| } |
| |
| // ---------------------------------------------------------------------- |
| // |
| // SendProcess |
| // sequence grabber data procedure |
| // |
| // This function is called by the capture device as soon as a new |
| // frame is available. |
| // |
| // |
| // SendFrame |
| // |
| // The non-static function used by the capture device callback |
| // |
| // Input: |
| // sgChannel: the capture device channel generating the callback |
| // data: the video frame |
| // length: the data length in bytes |
| // grabTime: time stamp generated by the capture device / sequece grabber |
| // |
| // ---------------------------------------------------------------------- |
| |
| OSErr VideoCaptureMacQuickTime::SendProcess(SGChannel sgChannel, Ptr p, |
| long len, long* /*offset*/, |
| long /*chRefCon*/, TimeValue time, |
| short /*writeType*/, long refCon) |
| { |
| VideoCaptureMacQuickTime* videoEngine = |
| reinterpret_cast<VideoCaptureMacQuickTime*> (refCon); |
| return videoEngine->SendFrame(sgChannel, (char*) p, len, time); |
| } |
| |
| int VideoCaptureMacQuickTime::SendFrame(SGChannel /*sgChannel*/, char* data, |
| long length, TimeValue /*grabTime*/) |
| { |
| if (!_sgPrepared) |
| { |
| WEBRTC_TRACE(webrtc::kTraceWarning, webrtc::kTraceVideoCapture, _id, |
| "%s:%d Sequence Grabber is not initialized", __FUNCTION__, |
| __LINE__); |
| return 0; |
| } |
| |
| WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCapture, _id, |
| "%s:%d Frame has been delivered\n", __FUNCTION__, __LINE__); |
| |
| CodecFlags ignore; |
| _grabberCritsect->Enter(); |
| if (_gWorld) |
| { |
| // Will be set to true if we don't recognize the size and/or video |
| // format. |
| bool convertFrame = false; |
| WebRtc_Word32 width = 352; |
| WebRtc_Word32 height = 288; |
| WebRtc_Word32 frameSize = 0; |
| |
| VideoCaptureCapability captureCapability; |
| captureCapability.width = width; |
| captureCapability.height = height; |
| captureCapability.maxFPS = 30; |
| |
| switch (_captureVideoType) |
| { |
| case kVideoUYVY: |
| captureCapability.rawType = kVideoUYVY; |
| break; |
| case kVideoYUY2: |
| captureCapability.rawType = kVideoYUY2; |
| break; |
| case kVideoI420: |
| captureCapability.rawType = kVideoI420; |
| break; |
| default: |
| captureCapability.rawType = kVideoI420; |
| WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCapture, |
| _id, "%s:%d raw = I420 by default\n", |
| __FUNCTION__, __LINE__); |
| break; |
| } |
| |
| // Convert the camera video type to something VideoEngine can work with |
| // Check if we need to downsample the incomming frame. |
| switch (_captureVideoType) |
| { |
| case kVideoUYVY: |
| case kVideoYUY2: |
| frameSize = (width * height * 16) >> 3; // 16 is for YUY2 format |
| if (width == _captureCapability.width || height |
| == _captureCapability.height) |
| { |
| // Ok format and size, send the frame to super class |
| IncomingFrame((WebRtc_UWord8*) data, |
| (WebRtc_Word32) frameSize, captureCapability, |
| TickTime::MillisecondTimestamp()); |
| |
| } |
| else if (width == _trueCaptureWidth && height |
| == _trueCaptureHeight) |
| { |
| // We need to scale the picture to correct size... |
| // This happens for cameras not supporting all sizes. |
| // E.g. older built-in iSight doesn't support QCIF. |
| |
| // Convert the incoming frame into our GWorld. |
| int res = |
| DecompressSequenceFrameS(_captureSequence, data, |
| length, 0, &ignore, NULL); |
| if (res != noErr && res != -8976) // 8796 == black frame |
| { |
| WEBRTC_TRACE(webrtc::kTraceWarning, |
| webrtc::kTraceVideoCapture, _id, |
| "%s:%d Captured black frame. Not " |
| "processing it", __FUNCTION__, __LINE__); |
| _grabberCritsect->Leave(); |
| return 0; |
| } |
| |
| // Copy the frame from the PixMap to our video buffer |
| PixMapHandle pixMap = GetGWorldPixMap(_gWorld); |
| |
| // Lock the image data in the GWorld. |
| LockPixels(pixMap); |
| |
| // Get a pointer to the pixel data. |
| Ptr capturedFrame = GetPixBaseAddr(pixMap); |
| |
| // Send the converted frame out to super class |
| IncomingFrame((WebRtc_UWord8*) data, |
| (WebRtc_Word32) frameSize, captureCapability, |
| TickTime::MillisecondTimestamp()); |
| |
| // Unlock the image data to get ready for the next frame. |
| UnlockPixels(pixMap); |
| } |
| else |
| { |
| // Not a size we recognize, use the Mac internal scaling... |
| convertFrame = true; |
| WEBRTC_TRACE(webrtc::kTraceDebug, |
| webrtc::kTraceVideoCapture, _id, |
| "%s:%d Not correct incoming stream size for " |
| "the format and configured size", |
| __FUNCTION__, __LINE__); |
| } |
| break; |
| default: |
| |
| // Not a video format we recognize, use the Mac internal scaling |
| WEBRTC_TRACE(webrtc::kTraceWarning, webrtc::kTraceVideoCapture, |
| _id, "%s:%d Unknown video frame format (default)", |
| __FUNCTION__, __LINE__); |
| convertFrame = true; |
| break; |
| } |
| |
| if (convertFrame) |
| { |
| WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCapture, _id, |
| "%s:%d Unrecognized frame format. Converting frame", |
| __FUNCTION__, __LINE__); |
| |
| // We don't recognise the input format. Convert to UYVY, I420 is not |
| // supported on osx. Decompress the grabbed frame into the GWorld, |
| // i.e from webcam format to ARGB (RGB24), and extract the frame. |
| int res = DecompressSequenceFrameS(_captureSequence, data, length, |
| 0, &ignore, NULL); |
| if (res != noErr && res != -8976) // 8796 means a black frame |
| { |
| _grabberCritsect->Leave(); |
| return 0; |
| } |
| |
| // Copy the frame from the PixMap to our video buffer |
| PixMapHandle rgbPixMap = GetGWorldPixMap(_gWorld); |
| LockPixels(rgbPixMap); |
| Ptr capturedFrame = GetPixBaseAddr(rgbPixMap); |
| |
| // Get the picture size |
| int width = (*rgbPixMap)->bounds.right; |
| int height = (*rgbPixMap)->bounds.bottom; |
| |
| // 16 is for YUY2 format. |
| WebRtc_Word32 frameSize = (width * height * 16) >> 3; |
| |
| // Ok format and size, send the frame to super class |
| IncomingFrame((WebRtc_UWord8*) data, (WebRtc_Word32) frameSize, |
| captureCapability, TickTime::MillisecondTimestamp()); |
| |
| UnlockPixels(rgbPixMap); |
| } |
| |
| // Tell the capture device it's ok to update. |
| SGUpdate(_captureGrabber, NULL); |
| } |
| else |
| { |
| WEBRTC_TRACE(webrtc::kTraceWarning, webrtc::kTraceVideoCapture, _id, |
| "%s:%d No GWorld created, but frames are being delivered", |
| __FUNCTION__, __LINE__); |
| } |
| |
| _grabberCritsect->Leave(); |
| return 0; |
| } |
| |
| int VideoCaptureMacQuickTime::VideoCaptureInitThreadContext() |
| { |
| WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCapture, _id, |
| "%s:%d ", __FUNCTION__, __LINE__); |
| _videoMacCritsect->Enter(); |
| EnterMoviesOnThread( kQTEnterMoviesFlagDontSetComponentsThreadMode); |
| _videoMacCritsect->Leave(); |
| return 0; |
| } |
| |
| // |
| // |
| // Functions for handling capture devices |
| // |
| // |
| |
| VideoCaptureMacQuickTime::VideoCaptureMacName::VideoCaptureMacName() : |
| _size(0) |
| { |
| memset(_name, 0, kVideoCaptureMacNameMaxSize); |
| } |
| |
| int VideoCaptureMacQuickTime::VideoCaptureSetCaptureDevice( |
| const char* deviceName, int size) |
| { |
| |
| WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceVideoCapture, _id, |
| "%s:%d deviceName=%s, size=%d", __FUNCTION__, __LINE__, |
| deviceName, size); |
| |
| _videoMacCritsect->Enter(); |
| bool wasCapturing = false; |
| |
| _grabberCritsect->Enter(); |
| if (_captureGrabber) |
| { |
| // Stop grabbing, disconnect and close the old capture device |
| StopQuickTimeCapture(&wasCapturing); |
| DisconnectCaptureDevice(); |
| CloseComponent(_captureGrabber); |
| _captureDevice = NULL; |
| _captureGrabber = NULL; |
| WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id, |
| "%s:%d Old capture device removed", __FUNCTION__, |
| __LINE__); |
| } |
| |
| if (deviceName == NULL || size == 0) |
| { |
| _grabberCritsect->Leave(); |
| _videoMacCritsect->Leave(); |
| return 0; |
| } |
| |
| if (size < 0) |
| { |
| _grabberCritsect->Leave(); |
| _videoMacCritsect->Leave(); |
| WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id, |
| "%s:%d 'size' is not valid", __FUNCTION__, __LINE__); |
| return 0; |
| } |
| |
| ComponentDescription compCaptureType; |
| |
| // Define the component we want to open |
| compCaptureType.componentType = SeqGrabComponentType; |
| compCaptureType.componentSubType = 0; |
| compCaptureType.componentManufacturer = 0; |
| compCaptureType.componentFlags = 0; |
| compCaptureType.componentFlagsMask = 0; |
| |
| long numSequenceGrabbers = CountComponents(&compCaptureType); |
| |
| // loop through the available grabbers and open the first possible |
| for (int i = 0; i < numSequenceGrabbers; i++) |
| { |
| _captureDevice = FindNextComponent(0, &compCaptureType); |
| _captureGrabber = OpenComponent(_captureDevice); |
| if (_captureGrabber != NULL) |
| { |
| // We've found a sequencegrabber that we could open |
| if (SGInitialize(_captureGrabber) != noErr) |
| { |
| _grabberCritsect->Leave(); |
| _videoMacCritsect->Leave(); |
| WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, |
| _id, |
| "%s:%d Could not initialize sequence grabber", |
| __FUNCTION__, __LINE__); |
| return -1; |
| } |
| break; |
| } |
| if (i == numSequenceGrabbers - 1) |
| { |
| // Couldn't open a sequence grabber |
| _grabberCritsect->Leave(); |
| _videoMacCritsect->Leave(); |
| WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id, |
| "%s:%d Could not open a sequence grabber", |
| __FUNCTION__, __LINE__); |
| return -1; |
| } |
| } |
| |
| if (!_gWorld) |
| { |
| // We don't have a GWorld. Create one to enable early preview |
| // without calling SetSendCodec |
| if (CreateLocalGWorld(_captureCapability.width, |
| _captureCapability.height) == -1) |
| { |
| // Error already logged |
| _grabberCritsect->Leave(); |
| _videoMacCritsect->Leave(); |
| return -1; |
| } |
| } |
| // Connect the camera with our GWorld |
| int cpySize = size; |
| if ((unsigned int) size > sizeof(_captureDeviceDisplayName)) |
| { |
| cpySize = sizeof(_captureDeviceDisplayName); |
| } |
| WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCapture, _id, |
| "%s:%d Copying %d chars from deviceName to " |
| "_captureDeviceDisplayName (size=%d)\n", |
| __FUNCTION__, __LINE__, cpySize, size); |
| memcpy(_captureDeviceDisplayName, deviceName, cpySize); |
| if (ConnectCaptureDevice() == -1) |
| { |
| // Error already logged |
| _grabberCritsect->Leave(); |
| _videoMacCritsect->Leave(); |
| return -1; |
| } |
| |
| if (StartQuickTimeCapture() == -1) |
| { |
| // Error already logged |
| _grabberCritsect->Leave(); |
| _videoMacCritsect->Leave(); |
| return -1; |
| } |
| _grabberCritsect->Leave(); |
| _videoMacCritsect->Leave(); |
| return 0; |
| } |
| |
| bool VideoCaptureMacQuickTime::IsCaptureDeviceSelected() |
| { |
| _grabberCritsect->Leave(); |
| return (_captureIsInitialized) ? true : false; |
| _grabberCritsect->Leave(); |
| } |
| |
| /** |
| Convert a Pascal string to a C string. |
| |
| \param[in] pascalString |
| Pascal string to convert. Pascal strings contain the number of |
| characters in the first byte and are not null-terminated. |
| |
| \param[out] cString |
| The C string buffer into which to copy the converted string. |
| |
| \param[in] bufferSize |
| The size of the C string buffer in bytes. |
| |
| \return The number of characters in the string on success and -1 on failure. |
| */ |
| CFIndex VideoCaptureMacQuickTime::PascalStringToCString( |
| const unsigned char* pascalString, char* cString, CFIndex bufferSize) |
| { |
| |
| WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCapture, 0, |
| "%s:%d Converting pascal string to c string", __FUNCTION__, |
| __LINE__); |
| if (pascalString == NULL) |
| { |
| return -1; |
| } |
| |
| if (cString == NULL) |
| { |
| return -1; |
| } |
| |
| if (bufferSize == 0) |
| { |
| return -1; |
| } |
| |
| CFIndex cStringLength = 0; |
| CFIndex maxStringLength = bufferSize - 1; |
| |
| CFStringRef cfString = CFStringCreateWithPascalString( |
| NULL, pascalString, kCFStringEncodingMacRoman); |
| if (cfString == NULL) |
| { |
| WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCapture, 0, |
| "%s:%d Error in CFStringCreateWithPascalString()", |
| __FUNCTION__, __LINE__); |
| CFRelease(cfString); |
| return -1; |
| } |
| |
| CFIndex cfLength = CFStringGetLength(cfString); |
| cStringLength = cfLength; |
| if (cfLength > maxStringLength) |
| { |
| cStringLength = maxStringLength; |
| } |
| |
| Boolean success = CFStringGetCString(cfString, cString, bufferSize, |
| kCFStringEncodingMacRoman); |
| |
| // Ensure the problem isn't insufficient buffer length. |
| // This is fine; we will return a partial string. |
| if (success == false && cfLength <= maxStringLength) |
| { |
| WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCapture, 0, |
| "%s:%d Error in CFStringGetCString()", __FUNCTION__, |
| __LINE__); |
| CFRelease(cfString); |
| return -1; |
| } |
| |
| CFRelease(cfString); |
| return cStringLength; |
| } |
| } // namespace webrtc |