Chromium Code Reviews| Index: media/capture/video/mac/video_capture_device_avfoundation_mac.mm |
| diff --git a/media/capture/video/mac/video_capture_device_avfoundation_mac.mm b/media/capture/video/mac/video_capture_device_avfoundation_mac.mm |
| index 0f89a03119e5b3b75c9de35ddbcc961923fd4bc3..3c7bfaf38693e712733133ac793a715ec63a21e1 100644 |
| --- a/media/capture/video/mac/video_capture_device_avfoundation_mac.mm |
| +++ b/media/capture/video/mac/video_capture_device_avfoundation_mac.mm |
| @@ -95,8 +95,6 @@ void MaybeWriteUma(int number_of_devices, int number_of_suspended_devices) { |
| } |
| } |
| -} // anonymous namespace |
| - |
| // This function translates Mac Core Video pixel formats to Chromium pixel |
| // formats. |
| media::VideoPixelFormat FourCCToChromiumPixelFormat(FourCharCode code) { |
| @@ -112,6 +110,25 @@ media::VideoPixelFormat FourCCToChromiumPixelFormat(FourCharCode code) { |
| } |
| } |
| +// Extracts |baseAddress| and |length| out of a SampleBuffer. |
| +void ExtractBaseAddressAndLength( |
| + char** baseAddress, |
|
Robert Sesek
2016/07/08 19:31:04
naming: use under_scores in non-ObjC methods
mcasas
2016/07/08 21:39:28
Oops, yes, done.
|
| + size_t* length, |
| + CoreMediaGlue::CMSampleBufferRef sampleBuffer) { |
| + CoreMediaGlue::CMBlockBufferRef blockBuffer = |
| + CoreMediaGlue::CMSampleBufferGetDataBuffer(sampleBuffer); |
| + DCHECK(blockBuffer); |
| + |
| + size_t lengthAtOffset; |
| + CoreMediaGlue::CMBlockBufferGetDataPointer(blockBuffer, 0, &lengthAtOffset, |
| + length, baseAddress); |
| + // Expect the (M)JPEG data to be available as a contiguous reference, i.e. |
| + // not covered by multiple memory blocks. |
| + DCHECK_EQ(lengthAtOffset, *length); |
| +} |
| + |
| +} // anonymous namespace |
| + |
| @implementation VideoCaptureDeviceAVFoundation |
| #pragma mark Class methods |
| @@ -217,6 +234,8 @@ media::VideoPixelFormat FourCCToChromiumPixelFormat(FourCharCode code) { |
| // No need to release |captureDeviceInput_|, is owned by the session. |
| captureDeviceInput_ = nil; |
| } |
| + if (stillImageOutput_) |
| + [captureSession_ removeOutput:stillImageOutput_]; |
| return YES; |
| } |
| @@ -261,6 +280,13 @@ media::VideoPixelFormat FourCCToChromiumPixelFormat(FourCharCode code) { |
| queue:dispatch_get_global_queue( |
| DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)]; |
| [captureSession_ addOutput:captureVideoDataOutput_]; |
| + |
| + // Create and plug the still image capture output. This should happen in |
| + // advance of the actual picture to allow for the 3A to stabilize. |
| + stillImageOutput_.reset( |
| + [[AVFoundationGlue::AVCaptureStillImageOutputClass() alloc] init]); |
| + [captureSession_ addOutput:stillImageOutput_]; |
| + |
| return YES; |
| } |
| @@ -359,6 +385,48 @@ media::VideoPixelFormat FourCCToChromiumPixelFormat(FourCharCode code) { |
| [[NSNotificationCenter defaultCenter] removeObserver:self]; |
| } |
| +- (void)takePhoto { |
| + DCHECK(main_thread_checker_.CalledOnValidThread()); |
| + DCHECK([captureSession_ isRunning]); |
| + |
| + DCHECK_EQ(1u, [[stillImageOutput_ connections] count]); |
| + CrAVCaptureConnection* const connection = |
| + [[stillImageOutput_ connections] firstObject]; |
| + if (!connection) { |
| + base::AutoLock lock(lock_); |
| + frameReceiver_->OnPhotoError(); |
| + return; |
| + } |
| + |
| + const auto handler = ^(CoreMediaGlue::CMSampleBufferRef sampleBuffer, |
| + NSError* error) { |
| + base::AutoLock lock(lock_); |
| + if (!frameReceiver_) |
| + return; |
| + if (error != nil) { |
| + frameReceiver_->OnPhotoError(); |
| + return; |
| + } |
| + |
| + // Recommended compressed pixel format is JPEG, we don't expect surprises. |
| + // TODO(mcasas): Consider using [1] for merging EXIF output information: |
| + // [1] +(NSData*)jpegStillImageNSDataRepresentation:jpegSampleBuffer; |
| + DCHECK_EQ( |
| + CoreMediaGlue::kCMVideoCodecType_JPEG, |
| + CoreMediaGlue::CMFormatDescriptionGetMediaSubType( |
| + CoreMediaGlue::CMSampleBufferGetFormatDescription(sampleBuffer))); |
| + |
| + char* baseAddress = 0; |
| + size_t length = 0; |
| + ExtractBaseAddressAndLength(&baseAddress, &length, sampleBuffer); |
| + frameReceiver_->OnPhotoTaken(reinterpret_cast<uint8_t*>(baseAddress), |
| + length, "image/jpeg"); |
| + }; |
| + |
| + [stillImageOutput_ captureStillImageAsynchronouslyFromConnection:connection |
| + completionHandler:handler]; |
| +} |
| + |
| #pragma mark Private methods |
| // |captureOutput| is called by the capture device to deliver a new frame. |
| @@ -381,17 +449,7 @@ didOutputSampleBuffer:(CoreMediaGlue::CMSampleBufferRef)sampleBuffer |
| size_t frameSize = 0; |
| CVImageBufferRef videoFrame = nil; |
| if (fourcc == CoreMediaGlue::kCMVideoCodecType_JPEG_OpenDML) { |
| - // If MJPEG, use block buffer instead of pixel buffer. |
| - CoreMediaGlue::CMBlockBufferRef blockBuffer = |
| - CoreMediaGlue::CMSampleBufferGetDataBuffer(sampleBuffer); |
| - if (blockBuffer) { |
| - size_t lengthAtOffset; |
| - CoreMediaGlue::CMBlockBufferGetDataPointer( |
| - blockBuffer, 0, &lengthAtOffset, &frameSize, &baseAddress); |
| - // Expect the MJPEG data to be available as a contiguous reference, i.e. |
| - // not covered by multiple memory blocks. |
| - CHECK_EQ(lengthAtOffset, frameSize); |
| - } |
| + ExtractBaseAddressAndLength(&baseAddress, &frameSize, sampleBuffer); |
| } else { |
| videoFrame = CoreMediaGlue::CMSampleBufferGetImageBuffer(sampleBuffer); |
| // Lock the frame and calculate frame size. |