Index: media/capture/video/mac/video_capture_device_qtkit_mac.mm |
diff --git a/media/capture/video/mac/video_capture_device_qtkit_mac.mm b/media/capture/video/mac/video_capture_device_qtkit_mac.mm |
deleted file mode 100644 |
index 847c61acdf2e4cb9ea11870a2fdf30b8a7656135..0000000000000000000000000000000000000000 |
--- a/media/capture/video/mac/video_capture_device_qtkit_mac.mm |
+++ /dev/null |
@@ -1,372 +0,0 @@ |
-// Copyright (c) 2012 The Chromium Authors. All rights reserved. |
-// Use of this source code is governed by a BSD-style license that can be |
-// found in the LICENSE file. |
- |
-#import "media/capture/video/mac/video_capture_device_qtkit_mac.h" |
- |
-#import <QTKit/QTKit.h> |
-#include <stddef.h> |
- |
-#include "base/debug/crash_logging.h" |
-#include "base/location.h" |
-#include "base/logging.h" |
-#include "media/base/timestamp_constants.h" |
-#include "media/base/video_capture_types.h" |
-#include "media/capture/video/mac/video_capture_device_mac.h" |
-#include "media/capture/video/video_capture_device.h" |
-#include "ui/gfx/geometry/size.h" |
- |
-@implementation VideoCaptureDeviceQTKit |
- |
-#pragma mark Class methods |
- |
-+ (void)getDeviceNames:(NSMutableDictionary*)deviceNames { |
- // Third-party drivers often throw exceptions. The following catches any |
- // exceptions and continues in an orderly fashion with no devices detected. |
- NSArray* captureDevices = nil; |
- @try { |
- captureDevices = |
- [QTCaptureDevice inputDevicesWithMediaType:QTMediaTypeVideo]; |
- } @catch (id exception) { |
- } |
- |
- for (QTCaptureDevice* device in captureDevices) { |
- if ([[device attributeForKey:QTCaptureDeviceSuspendedAttribute] boolValue]) |
- continue; |
- DeviceNameAndTransportType* nameAndTransportType = [[ |
- [DeviceNameAndTransportType alloc] |
- initWithName:[device localizedDisplayName] |
- transportType:media::kIOAudioDeviceTransportTypeUnknown] autorelease]; |
- [deviceNames setObject:nameAndTransportType forKey:[device uniqueID]]; |
- } |
-} |
- |
-+ (NSDictionary*)deviceNames { |
- NSMutableDictionary* deviceNames = |
- [[[NSMutableDictionary alloc] init] autorelease]; |
- |
- // TODO(shess): Post to the main thread to see if that helps |
- // http://crbug.com/139164 |
- [self performSelectorOnMainThread:@selector(getDeviceNames:) |
- withObject:deviceNames |
- waitUntilDone:YES]; |
- return deviceNames; |
-} |
- |
-#pragma mark Public methods |
- |
-- (id)initWithFrameReceiver:(media::VideoCaptureDeviceMac*)frameReceiver { |
- self = [super init]; |
- if (self) { |
- frameReceiver_ = frameReceiver; |
- lock_ = [[NSLock alloc] init]; |
- } |
- return self; |
-} |
- |
-- (void)dealloc { |
- [captureSession_ release]; |
- [captureDeviceInput_ release]; |
- [super dealloc]; |
-} |
- |
-- (void)setFrameReceiver:(media::VideoCaptureDeviceMac*)frameReceiver { |
- [lock_ lock]; |
- frameReceiver_ = frameReceiver; |
- [lock_ unlock]; |
-} |
- |
-- (BOOL)setCaptureDevice:(NSString*)deviceId { |
- if (deviceId) { |
- // Set the capture device. |
- if (captureDeviceInput_) { |
- DLOG(ERROR) << "Video capture device already set."; |
- return NO; |
- } |
- |
- // TODO(mcasas): Consider using [QTCaptureDevice deviceWithUniqueID] instead |
- // of explicitly forcing reenumeration of devices. |
- NSArray* captureDevices = |
- [QTCaptureDevice inputDevicesWithMediaType:QTMediaTypeVideo]; |
- NSArray* captureDevicesNames = [captureDevices valueForKey:@"uniqueID"]; |
- NSUInteger index = [captureDevicesNames indexOfObject:deviceId]; |
- if (index == NSNotFound) { |
- [self sendErrorString:[NSString stringWithUTF8String: |
- "Video capture device not found."]]; |
- return NO; |
- } |
- QTCaptureDevice* device = [captureDevices objectAtIndex:index]; |
- if ([[device |
- attributeForKey:QTCaptureDeviceSuspendedAttribute] boolValue]) { |
- [self sendErrorString: |
- [NSString stringWithUTF8String: |
- "Cannot open suspended video capture device."]]; |
- return NO; |
- } |
- NSError* error; |
- if (![device open:&error]) { |
- [self sendErrorString: |
- [NSString stringWithFormat: |
- @"Could not open video capture device (%@): %@", |
- [error localizedDescription], |
- [error localizedFailureReason]]]; |
- return NO; |
- } |
- captureDeviceInput_ = [[QTCaptureDeviceInput alloc] initWithDevice:device]; |
- captureSession_ = [[QTCaptureSession alloc] init]; |
- |
- QTCaptureDecompressedVideoOutput* captureDecompressedOutput = |
- [[[QTCaptureDecompressedVideoOutput alloc] init] autorelease]; |
- [captureDecompressedOutput setDelegate:self]; |
- [captureDecompressedOutput setAutomaticallyDropsLateVideoFrames:YES]; |
- if (![captureSession_ addOutput:captureDecompressedOutput error:&error]) { |
- [self |
- sendErrorString: |
- [NSString stringWithFormat: |
- @"Could not connect video capture output (%@): %@", |
- [error localizedDescription], |
- [error localizedFailureReason]]]; |
- return NO; |
- } |
- |
- // This key can be used to check if video capture code was related to a |
- // particular crash. |
- base::debug::SetCrashKeyValue("VideoCaptureDeviceQTKit", "OpenedDevice"); |
- |
- // Set the video pixel format to 2VUY (a.k.a UYVY, packed 4:2:2). |
- NSDictionary* captureDictionary = [NSDictionary |
- dictionaryWithObject: |
- [NSNumber numberWithUnsignedInt:kCVPixelFormatType_422YpCbCr8] |
- forKey:(id)kCVPixelBufferPixelFormatTypeKey]; |
- [captureDecompressedOutput setPixelBufferAttributes:captureDictionary]; |
- |
- return YES; |
- } else { |
- // Remove the previously set capture device. |
- if (!captureDeviceInput_) { |
- // Being here means stopping a device that never started OK in the first |
- // place, log it. |
- [self sendLogString:[NSString |
- stringWithUTF8String: |
- "No video capture device set, on removal."]]; |
- return YES; |
- } |
- // Tear down input and output, stop the capture and deregister observers. |
- [self stopCapture]; |
- [captureSession_ release]; |
- captureSession_ = nil; |
- [captureDeviceInput_ release]; |
- captureDeviceInput_ = nil; |
- return YES; |
- } |
-} |
- |
-- (BOOL)setCaptureHeight:(int)height |
- width:(int)width |
- frameRate:(float)frameRate { |
- if (!captureDeviceInput_) { |
- [self sendErrorString: |
- [NSString stringWithUTF8String:"No video capture device set."]]; |
- return NO; |
- } |
- if ([[captureSession_ outputs] count] != 1) { |
- [self sendErrorString:[NSString |
- stringWithUTF8String: |
- "Video capture capabilities already set."]]; |
- return NO; |
- } |
- if (frameRate <= 0.0f) { |
- [self sendErrorString:[NSString stringWithUTF8String:"Wrong frame rate."]]; |
- return NO; |
- } |
- |
- frameRate_ = frameRate; |
- |
- QTCaptureDecompressedVideoOutput* output = |
- [[captureSession_ outputs] objectAtIndex:0]; |
- |
- // Set up desired output properties. The old capture dictionary is used to |
- // retrieve the initial pixel format, which must be maintained. |
- NSDictionary* videoSettingsDictionary = @{ |
- (id)kCVPixelBufferWidthKey : @(width), (id) |
- kCVPixelBufferHeightKey : @(height), (id) |
- kCVPixelBufferPixelFormatTypeKey : [[output pixelBufferAttributes] |
- valueForKey:(id)kCVPixelBufferPixelFormatTypeKey] |
- }; |
- [output setPixelBufferAttributes:videoSettingsDictionary]; |
- |
- [output setMinimumVideoFrameInterval:(NSTimeInterval)1 / frameRate]; |
- return YES; |
-} |
- |
-- (BOOL)startCapture { |
- if ([[captureSession_ outputs] count] == 0) { |
- // Capture properties not set. |
- [self |
- sendErrorString:[NSString stringWithUTF8String: |
- "Video capture device not initialized."]]; |
- return NO; |
- } |
- if ([[captureSession_ inputs] count] == 0) { |
- NSError* error; |
- if (![captureSession_ addInput:captureDeviceInput_ error:&error]) { |
- [self |
- sendErrorString: |
- [NSString stringWithFormat: |
- @"Could not connect video capture device (%@): %@", |
- [error localizedDescription], |
- [error localizedFailureReason]]]; |
- |
- return NO; |
- } |
- NSNotificationCenter* notificationCenter = |
- [NSNotificationCenter defaultCenter]; |
- [notificationCenter addObserver:self |
- selector:@selector(handleNotification:) |
- name:QTCaptureSessionRuntimeErrorNotification |
- object:captureSession_]; |
- [captureSession_ startRunning]; |
- } |
- return YES; |
-} |
- |
-- (void)stopCapture { |
- // QTKit achieves thread safety and asynchronous execution by posting messages |
- // to the main thread, e.g. -addOutput:. Both -removeOutput: and -removeInput: |
- // post a message to the main thread while holding a lock that the |
- // notification handler might need. To avoid a deadlock, we perform those |
- // tasks in the main thread. See bugs http://crbug.com/152757 and |
- // http://crbug.com/399792. |
- [self performSelectorOnMainThread:@selector(stopCaptureOnUIThread:) |
- withObject:nil |
- waitUntilDone:YES]; |
- [[NSNotificationCenter defaultCenter] removeObserver:self]; |
-} |
- |
-- (void)stopCaptureOnUIThread:(id)dummy { |
- if ([[captureSession_ inputs] count] > 0) { |
- DCHECK_EQ([[captureSession_ inputs] count], 1u); |
- [captureSession_ removeInput:captureDeviceInput_]; |
- [captureSession_ stopRunning]; |
- } |
- if ([[captureSession_ outputs] count] > 0) { |
- DCHECK_EQ([[captureSession_ outputs] count], 1u); |
- id output = [[captureSession_ outputs] objectAtIndex:0]; |
- [output setDelegate:nil]; |
- [captureSession_ removeOutput:output]; |
- } |
-} |
- |
-// |captureOutput| is called by the capture device to deliver a new frame. |
-- (void)captureOutput:(QTCaptureOutput*)captureOutput |
- didOutputVideoFrame:(CVImageBufferRef)videoFrame |
- withSampleBuffer:(QTSampleBuffer*)sampleBuffer |
- fromConnection:(QTCaptureConnection*)connection { |
- [lock_ lock]; |
- if (!frameReceiver_) { |
- [lock_ unlock]; |
- return; |
- } |
- |
- // Lock the frame and calculate frame size. |
- const int kLockFlags = 0; |
- if (CVPixelBufferLockBaseAddress(videoFrame, kLockFlags) == |
- kCVReturnSuccess) { |
- void* baseAddress = CVPixelBufferGetBaseAddress(videoFrame); |
- size_t bytesPerRow = CVPixelBufferGetBytesPerRow(videoFrame); |
- size_t frameWidth = CVPixelBufferGetWidth(videoFrame); |
- size_t frameHeight = CVPixelBufferGetHeight(videoFrame); |
- size_t frameSize = bytesPerRow * frameHeight; |
- |
- // TODO(shess): bytesPerRow may not correspond to frameWidth_*2, |
- // but VideoCaptureController::OnIncomingCapturedData() requires |
- // it to do so. Plumbing things through is intrusive, for now |
- // just deliver an adjusted buffer. |
- // TODO(nick): This workaround could probably be eliminated by using |
- // VideoCaptureController::OnIncomingCapturedVideoFrame, which supports |
- // pitches. |
- UInt8* addressToPass = static_cast<UInt8*>(baseAddress); |
- // UYVY is 2 bytes per pixel. |
- size_t expectedBytesPerRow = frameWidth * 2; |
- if (bytesPerRow > expectedBytesPerRow) { |
- // TODO(shess): frameHeight and frameHeight_ are not the same, |
- // try to do what the surrounding code seems to assume. |
- // Ironically, captureCapability and frameSize are ignored |
- // anyhow. |
- adjustedFrame_.resize(expectedBytesPerRow * frameHeight); |
- // std::vector is contiguous according to standard. |
- UInt8* adjustedAddress = &adjustedFrame_[0]; |
- |
- for (size_t y = 0; y < frameHeight; ++y) { |
- memcpy(adjustedAddress + y * expectedBytesPerRow, |
- addressToPass + y * bytesPerRow, expectedBytesPerRow); |
- } |
- |
- addressToPass = adjustedAddress; |
- frameSize = frameHeight * expectedBytesPerRow; |
- } |
- |
- media::VideoCaptureFormat captureFormat( |
- gfx::Size(frameWidth, frameHeight), frameRate_, |
- media::PIXEL_FORMAT_UYVY); |
- |
- // The aspect ratio dictionary is often missing, in which case we report |
- // a pixel aspect ratio of 0:0. |
- int aspectNumerator = 0, aspectDenominator = 0; |
- CFDictionaryRef aspectRatioDict = (CFDictionaryRef)CVBufferGetAttachment( |
- videoFrame, kCVImageBufferPixelAspectRatioKey, NULL); |
- if (aspectRatioDict) { |
- CFNumberRef aspectNumeratorRef = (CFNumberRef)CFDictionaryGetValue( |
- aspectRatioDict, kCVImageBufferPixelAspectRatioHorizontalSpacingKey); |
- CFNumberRef aspectDenominatorRef = (CFNumberRef)CFDictionaryGetValue( |
- aspectRatioDict, kCVImageBufferPixelAspectRatioVerticalSpacingKey); |
- DCHECK(aspectNumeratorRef && aspectDenominatorRef) |
- << "Aspect Ratio dictionary missing its entries."; |
- CFNumberGetValue(aspectNumeratorRef, kCFNumberIntType, &aspectNumerator); |
- CFNumberGetValue(aspectDenominatorRef, kCFNumberIntType, |
- &aspectDenominator); |
- } |
- |
- // Deliver the captured video frame. |
- const QTTime qt_timestamp = [sampleBuffer presentationTime]; |
- base::TimeDelta timestamp; |
- if (!(qt_timestamp.flags & kQTTimeIsIndefinite) && qt_timestamp.timeScale) { |
- timestamp = base::TimeDelta::FromMicroseconds( |
- qt_timestamp.timeValue * base::TimeTicks::kMicrosecondsPerSecond / |
- qt_timestamp.timeScale); |
- } else { |
- timestamp = media::kNoTimestamp(); |
- } |
- frameReceiver_->ReceiveFrame(addressToPass, frameSize, captureFormat, |
- aspectNumerator, aspectDenominator, timestamp); |
- |
- CVPixelBufferUnlockBaseAddress(videoFrame, kLockFlags); |
- } |
- [lock_ unlock]; |
-} |
- |
-- (void)handleNotification:(NSNotification*)errorNotification { |
- NSError* error = (NSError*) |
- [[errorNotification userInfo] objectForKey:QTCaptureSessionErrorKey]; |
- [self sendErrorString: |
- [NSString stringWithFormat:@"%@: %@", [error localizedDescription], |
- [error localizedFailureReason]]]; |
-} |
- |
-- (void)sendErrorString:(NSString*)error { |
- DLOG(ERROR) << [error UTF8String]; |
- [lock_ lock]; |
- if (frameReceiver_) |
- frameReceiver_->ReceiveError(FROM_HERE, [error UTF8String]); |
- [lock_ unlock]; |
-} |
- |
-- (void)sendLogString:(NSString*)message { |
- DVLOG(1) << [message UTF8String]; |
- [lock_ lock]; |
- if (frameReceiver_) |
- frameReceiver_->LogMessage([message UTF8String]); |
- [lock_ unlock]; |
-} |
- |
-@end |