Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #import "media/video/capture/mac/video_capture_device_qtkit_mac.h" | 5 #import "media/video/capture/mac/video_capture_device_qtkit_mac.h" |
| 6 | 6 |
| 7 #import <QTKit/QTKit.h> | 7 #import <QTKit/QTKit.h> |
| 8 | 8 |
| 9 #include "base/debug/crash_logging.h" | 9 #include "base/debug/crash_logging.h" |
| 10 #include "base/logging.h" | 10 #include "base/logging.h" |
| (...skipping 60 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 71 } | 71 } |
| 72 | 72 |
| 73 - (BOOL)setCaptureDevice:(NSString*)deviceId { | 73 - (BOOL)setCaptureDevice:(NSString*)deviceId { |
| 74 if (deviceId) { | 74 if (deviceId) { |
| 75 // Set the capture device. | 75 // Set the capture device. |
| 76 if (captureDeviceInput_) { | 76 if (captureDeviceInput_) { |
| 77 DLOG(ERROR) << "Video capture device already set."; | 77 DLOG(ERROR) << "Video capture device already set."; |
| 78 return NO; | 78 return NO; |
| 79 } | 79 } |
| 80 | 80 |
| 81 // TODO(mcasas): Consider using [QTCaptureDevice deviceWithUniqueID] instead | |
| 82 // of explicitly forcing reenumeration of devices. | |
| 81 NSArray *captureDevices = | 83 NSArray *captureDevices = |
| 82 [QTCaptureDevice inputDevicesWithMediaType:QTMediaTypeVideo]; | 84 [QTCaptureDevice inputDevicesWithMediaType:QTMediaTypeVideo]; |
| 83 NSArray *captureDevicesNames = | 85 NSArray *captureDevicesNames = |
| 84 [captureDevices valueForKey:@"uniqueID"]; | 86 [captureDevices valueForKey:@"uniqueID"]; |
| 85 NSUInteger index = [captureDevicesNames indexOfObject:deviceId]; | 87 NSUInteger index = [captureDevicesNames indexOfObject:deviceId]; |
| 86 if (index == NSNotFound) { | 88 if (index == NSNotFound) { |
| 87 DLOG(ERROR) << "Video capture device not found."; | 89 [self sendErrorString:[NSString |
| 90 stringWithUTF8String:"Video capture device not found."]]; | |
| 88 return NO; | 91 return NO; |
| 89 } | 92 } |
| 90 QTCaptureDevice *device = [captureDevices objectAtIndex:index]; | 93 QTCaptureDevice *device = [captureDevices objectAtIndex:index]; |
| 91 if ([[device attributeForKey:QTCaptureDeviceSuspendedAttribute] | 94 if ([[device attributeForKey:QTCaptureDeviceSuspendedAttribute] |
| 92 boolValue]) { | 95 boolValue]) { |
| 93 DLOG(ERROR) << "Cannot open suspended video capture device."; | 96 [self sendErrorString:[NSString |
| 97 stringWithUTF8String:"Cannot open suspended video capture device."]]; | |
| 94 return NO; | 98 return NO; |
| 95 } | 99 } |
| 96 NSError *error; | 100 NSError *error; |
| 97 if (![device open:&error]) { | 101 if (![device open:&error]) { |
| 98 DLOG(ERROR) << "Could not open video capture device." | 102 [self sendErrorString:[NSString |
| 99 << [[error localizedDescription] UTF8String]; | 103 stringWithFormat:@"Could not open video capture device: %@ %@", |
|
Henrik Grunell
2014/05/30 11:48:32
Clarify by for example "...: %@, reason: %@". Here
mcasas
2014/05/30 12:15:27
Used the original "Big error (%@): %@", this shoul
Henrik Grunell
2014/05/30 12:30:48
SGTM
| |
| 104 [error localizedDescription], | |
| 105 [error localizedFailureReason]]]; | |
| 100 return NO; | 106 return NO; |
| 101 } | 107 } |
| 102 captureDeviceInput_ = [[QTCaptureDeviceInput alloc] initWithDevice:device]; | 108 captureDeviceInput_ = [[QTCaptureDeviceInput alloc] initWithDevice:device]; |
| 103 captureSession_ = [[QTCaptureSession alloc] init]; | 109 captureSession_ = [[QTCaptureSession alloc] init]; |
| 104 | 110 |
| 105 QTCaptureDecompressedVideoOutput *captureDecompressedOutput = | 111 QTCaptureDecompressedVideoOutput *captureDecompressedOutput = |
| 106 [[[QTCaptureDecompressedVideoOutput alloc] init] autorelease]; | 112 [[[QTCaptureDecompressedVideoOutput alloc] init] autorelease]; |
| 107 [captureDecompressedOutput setDelegate:self]; | 113 [captureDecompressedOutput setDelegate:self]; |
| 108 if (![captureSession_ addOutput:captureDecompressedOutput error:&error]) { | 114 if (![captureSession_ addOutput:captureDecompressedOutput error:&error]) { |
| 109 DLOG(ERROR) << "Could not connect video capture output." | 115 [self sendErrorString:[NSString |
| 110 << [[error localizedDescription] UTF8String]; | 116 stringWithFormat:@"Could not connect video capture output: %@ %@", |
| 117 [error localizedDescription], | |
| 118 [error localizedFailureReason]]]; | |
| 111 return NO; | 119 return NO; |
| 112 } | 120 } |
| 113 | 121 |
| 114 // This key can be used to check if video capture code was related to a | 122 // This key can be used to check if video capture code was related to a |
| 115 // particular crash. | 123 // particular crash. |
| 116 base::debug::SetCrashKeyValue("VideoCaptureDeviceQTKit", "OpenedDevice"); | 124 base::debug::SetCrashKeyValue("VideoCaptureDeviceQTKit", "OpenedDevice"); |
| 117 | 125 |
| 118 // Set the video pixel format to 2VUY (a.k.a UYVY, packed 4:2:2). | 126 // Set the video pixel format to 2VUY (a.k.a UYVY, packed 4:2:2). |
| 119 NSDictionary *captureDictionary = [NSDictionary | 127 NSDictionary *captureDictionary = [NSDictionary |
| 120 dictionaryWithObject: | 128 dictionaryWithObject: |
| 121 [NSNumber numberWithUnsignedInt:kCVPixelFormatType_422YpCbCr8] | 129 [NSNumber numberWithUnsignedInt:kCVPixelFormatType_422YpCbCr8] |
| 122 forKey:(id)kCVPixelBufferPixelFormatTypeKey]; | 130 forKey:(id)kCVPixelBufferPixelFormatTypeKey]; |
| 123 [captureDecompressedOutput setPixelBufferAttributes:captureDictionary]; | 131 [captureDecompressedOutput setPixelBufferAttributes:captureDictionary]; |
| 124 | 132 |
| 125 return YES; | 133 return YES; |
| 126 } else { | 134 } else { |
| 127 // Remove the previously set capture device. | 135 // Remove the previously set capture device. |
| 128 if (!captureDeviceInput_) { | 136 if (!captureDeviceInput_) { |
| 129 DLOG(ERROR) << "No video capture device set."; | 137 [self sendErrorString:[NSString |
| 138 stringWithUTF8String:"No video capture device set, on removal."]]; | |
| 130 return YES; | 139 return YES; |
| 131 } | 140 } |
| 132 if ([[captureSession_ inputs] count] > 0) { | 141 if ([[captureSession_ inputs] count] > 0) { |
| 133 // The device is still running. | 142 // The device is still running. |
| 134 [self stopCapture]; | 143 [self stopCapture]; |
| 135 } | 144 } |
| 136 if ([[captureSession_ outputs] count] > 0) { | 145 if ([[captureSession_ outputs] count] > 0) { |
| 137 // Only one output is set for |captureSession_|. | 146 // Only one output is set for |captureSession_|. |
| 138 DCHECK_EQ([[captureSession_ outputs] count], 1u); | 147 DCHECK_EQ([[captureSession_ outputs] count], 1u); |
| 139 id output = [[captureSession_ outputs] objectAtIndex:0]; | 148 id output = [[captureSession_ outputs] objectAtIndex:0]; |
| 140 [output setDelegate:nil]; | 149 [output setDelegate:nil]; |
| 141 | 150 |
| 142 // TODO(shess): QTKit achieves thread safety by posting messages | 151 // TODO(shess): QTKit achieves thread safety by posting messages to the |
| 143 // to the main thread. As part of -addOutput:, it posts a | 152 // main thread. As part of -addOutput:, it posts a message to the main |
| 144 // message to the main thread which in turn posts a notification | 153 // thread which in turn posts a notification which will run in a future |
| 145 // which will run in a future spin after the original method | 154 // spin after the original method returns. -removeOutput: can post a |
| 146 // returns. -removeOutput: can post a main-thread message in | 155 // main-thread message in between while holding a lock which the |
| 147 // between while holding a lock which the notification handler | 156 // notification handler will need. Posting either -addOutput: or |
| 148 // will need. Posting either -addOutput: or -removeOutput: to | 157 // -removeOutput: to the main thread should fix it, remove is likely |
| 149 // the main thread should fix it, remove is likely safer. | 158 // safer. http://crbug.com/152757 |
| 150 // http://crbug.com/152757 | |
| 151 [captureSession_ performSelectorOnMainThread:@selector(removeOutput:) | 159 [captureSession_ performSelectorOnMainThread:@selector(removeOutput:) |
| 152 withObject:output | 160 withObject:output |
| 153 waitUntilDone:YES]; | 161 waitUntilDone:YES]; |
| 154 } | 162 } |
| 155 [captureSession_ release]; | 163 [captureSession_ release]; |
| 156 captureSession_ = nil; | 164 captureSession_ = nil; |
| 157 [captureDeviceInput_ release]; | 165 [captureDeviceInput_ release]; |
| 158 captureDeviceInput_ = nil; | 166 captureDeviceInput_ = nil; |
| 159 return YES; | 167 return YES; |
| 160 } | 168 } |
| 161 } | 169 } |
| 162 | 170 |
| 163 - (BOOL)setCaptureHeight:(int)height width:(int)width frameRate:(int)frameRate { | 171 - (BOOL)setCaptureHeight:(int)height width:(int)width frameRate:(int)frameRate { |
| 164 if (!captureDeviceInput_) { | 172 if (!captureDeviceInput_) { |
| 165 DLOG(ERROR) << "No video capture device set."; | 173 [self sendErrorString:[NSString |
| 174 stringWithUTF8String:"No video capture device set."]]; | |
| 166 return NO; | 175 return NO; |
| 167 } | 176 } |
| 168 if ([[captureSession_ outputs] count] != 1) { | 177 if ([[captureSession_ outputs] count] != 1) { |
| 169 DLOG(ERROR) << "Video capture capabilities already set."; | 178 [self sendErrorString:[NSString |
| 179 stringWithUTF8String:"Video capture capabilities already set."]]; | |
| 170 return NO; | 180 return NO; |
| 171 } | 181 } |
| 172 if (frameRate <= 0) { | 182 if (frameRate <= 0) { |
| 173 DLOG(ERROR) << "Wrong frame rate."; | 183 [self sendErrorString:[NSString stringWithUTF8String: "Wrong frame rate."]]; |
| 174 return NO; | 184 return NO; |
| 175 } | 185 } |
| 176 | 186 |
| 177 frameRate_ = frameRate; | 187 frameRate_ = frameRate; |
| 178 | 188 |
| 179 QTCaptureDecompressedVideoOutput *output = | 189 QTCaptureDecompressedVideoOutput *output = |
| 180 [[captureSession_ outputs] objectAtIndex:0]; | 190 [[captureSession_ outputs] objectAtIndex:0]; |
| 181 | 191 |
| 182 // Set up desired output properties. The old capture dictionary is used to | 192 // Set up desired output properties. The old capture dictionary is used to |
| 183 // retrieve the initial pixel format, which must be maintained. | 193 // retrieve the initial pixel format, which must be maintained. |
| 184 NSDictionary* videoSettingsDictionary = @{ | 194 NSDictionary* videoSettingsDictionary = @{ |
| 185 (id)kCVPixelBufferWidthKey : @(width), | 195 (id)kCVPixelBufferWidthKey : @(width), |
| 186 (id)kCVPixelBufferHeightKey : @(height), | 196 (id)kCVPixelBufferHeightKey : @(height), |
| 187 (id)kCVPixelBufferPixelFormatTypeKey : [[output pixelBufferAttributes] | 197 (id)kCVPixelBufferPixelFormatTypeKey : [[output pixelBufferAttributes] |
| 188 valueForKey:(id)kCVPixelBufferPixelFormatTypeKey] | 198 valueForKey:(id)kCVPixelBufferPixelFormatTypeKey] |
| 189 }; | 199 }; |
| 190 [output setPixelBufferAttributes:videoSettingsDictionary]; | 200 [output setPixelBufferAttributes:videoSettingsDictionary]; |
| 191 | 201 |
| 192 [output setMinimumVideoFrameInterval:(NSTimeInterval)1/(float)frameRate]; | 202 [output setMinimumVideoFrameInterval:(NSTimeInterval)1/(float)frameRate]; |
| 193 return YES; | 203 return YES; |
| 194 } | 204 } |
| 195 | 205 |
| 196 - (BOOL)startCapture { | 206 - (BOOL)startCapture { |
| 197 if ([[captureSession_ outputs] count] == 0) { | 207 if ([[captureSession_ outputs] count] == 0) { |
| 198 // Capture properties not set. | 208 // Capture properties not set. |
| 199 DLOG(ERROR) << "Video capture device not initialized."; | 209 [self sendErrorString:[NSString |
| 210 stringWithUTF8String:"Video capture device not initialized."]]; | |
| 200 return NO; | 211 return NO; |
| 201 } | 212 } |
| 202 if ([[captureSession_ inputs] count] == 0) { | 213 if ([[captureSession_ inputs] count] == 0) { |
| 203 NSError *error; | 214 NSError *error; |
| 204 if (![captureSession_ addInput:captureDeviceInput_ error:&error]) { | 215 if (![captureSession_ addInput:captureDeviceInput_ error:&error]) { |
| 205 DLOG(ERROR) << "Could not connect video capture device." | 216 [self sendErrorString:[NSString |
| 206 << [[error localizedDescription] UTF8String]; | 217 stringWithFormat:@"Could not connect video capture device: %@ %@", |
| 218 [error localizedDescription], | |
| 219 [error localizedFailureReason]]]; | |
| 220 | |
| 207 return NO; | 221 return NO; |
| 208 } | 222 } |
| 209 NSNotificationCenter * notificationCenter = | 223 NSNotificationCenter * notificationCenter = |
| 210 [NSNotificationCenter defaultCenter]; | 224 [NSNotificationCenter defaultCenter]; |
| 211 [notificationCenter addObserver:self | 225 [notificationCenter addObserver:self |
| 212 selector:@selector(handleNotification:) | 226 selector:@selector(handleNotification:) |
| 213 name:QTCaptureSessionRuntimeErrorNotification | 227 name:QTCaptureSessionRuntimeErrorNotification |
| 214 object:captureSession_]; | 228 object:captureSession_]; |
| 215 [captureSession_ startRunning]; | 229 [captureSession_ startRunning]; |
| 216 } | 230 } |
| (...skipping 85 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 302 aspectNumerator, aspectDenominator); | 316 aspectNumerator, aspectDenominator); |
| 303 | 317 |
| 304 CVPixelBufferUnlockBaseAddress(videoFrame, kLockFlags); | 318 CVPixelBufferUnlockBaseAddress(videoFrame, kLockFlags); |
| 305 } | 319 } |
| 306 [lock_ unlock]; | 320 [lock_ unlock]; |
| 307 } | 321 } |
| 308 | 322 |
| 309 - (void)handleNotification:(NSNotification*)errorNotification { | 323 - (void)handleNotification:(NSNotification*)errorNotification { |
| 310 NSError * error = (NSError*)[[errorNotification userInfo] | 324 NSError * error = (NSError*)[[errorNotification userInfo] |
| 311 objectForKey:QTCaptureSessionErrorKey]; | 325 objectForKey:QTCaptureSessionErrorKey]; |
| 312 NSString* str_error = | 326 [self sendErrorString:[NSString stringWithFormat:@"%@: %@", |
| 313 [NSString stringWithFormat:@"%@: %@", | 327 [error localizedDescription], |
|
Henrik Grunell
2014/05/30 11:48:32
Fix indentation.
mcasas
2014/05/30 12:15:27
Done.
Henrik Grunell
2014/05/30 12:30:48
Nit: Same here.
| |
| 314 [error localizedDescription], | 328 [error localizedFailureReason]]]; |
| 315 [error localizedFailureReason]]; | 329 } |
| 316 | 330 |
| 317 frameReceiver_->ReceiveError([str_error UTF8String]); | 331 - (void)sendErrorString:(NSString*)error { |
| 332 [lock_ lock]; | |
| 333 if (frameReceiver_) | |
| 334 frameReceiver_->ReceiveError([error UTF8String]); | |
| 335 [lock_ unlock]; | |
| 318 } | 336 } |
| 319 | 337 |
| 320 @end | 338 @end |
| OLD | NEW |