| 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
|
|
|