blob: c7418bff10af1b638cc9abc0286f8be39731e3ae [file] [log] [blame]
// Copyright 2013 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.
#ifndef MEDIA_CAPTURE_VIDEO_MAC_VIDEO_CAPTURE_DEVICE_AVFOUNDATION_MAC_H_
#define MEDIA_CAPTURE_VIDEO_MAC_VIDEO_CAPTURE_DEVICE_AVFOUNDATION_MAC_H_
#import <AVFoundation/AVFoundation.h>
#import <Foundation/Foundation.h>
#include "base/callback_forward.h"
#include "base/mac/scoped_dispatch_object.h"
#include "base/mac/scoped_nsobject.h"
#include "base/synchronization/lock.h"
#include "base/threading/thread_checker.h"
#include "media/capture/video/mac/sample_buffer_transformer_mac.h"
#include "media/capture/video/video_capture_device.h"
#include "media/capture/video_capture_types.h"
namespace media {
class CAPTURE_EXPORT VideoCaptureDeviceAVFoundationFrameReceiver {
public:
virtual ~VideoCaptureDeviceAVFoundationFrameReceiver() = default;
// Called to deliver captured video frames. It's safe to call this method
// from any thread, including those controlled by AVFoundation.
virtual void ReceiveFrame(const uint8_t* video_frame,
int video_frame_length,
const VideoCaptureFormat& frame_format,
const gfx::ColorSpace color_space,
int aspect_numerator,
int aspect_denominator,
base::TimeDelta timestamp) = 0;
// Called to deliver GpuMemoryBuffer-wrapped captured video frames. This
// function may be called from any thread, including those controlled by
// AVFoundation.
virtual void ReceiveExternalGpuMemoryBufferFrame(
CapturedExternalVideoBuffer frame,
std::vector<CapturedExternalVideoBuffer> scaled_frames,
base::TimeDelta timestamp) = 0;
// Callbacks with the result of a still image capture, or in case of error,
// respectively. It's safe to call these methods from any thread.
virtual void OnPhotoTaken(const uint8_t* image_data,
size_t image_length,
const std::string& mime_type) = 0;
// Callback when a call to takePhoto fails.
virtual void OnPhotoError() = 0;
// Forwarder to VideoCaptureDevice::Client::OnError().
virtual void ReceiveError(VideoCaptureError error,
const base::Location& from_here,
const std::string& reason) = 0;
};
// When this feature is enabled, the capturer can be configured using
// setScaledResolutions to output scaled versions of the captured frame (in
// addition to the original frame), whenever NV12 IOSurfaces are available to
// the capturer. These are available either when the camera supports it and
// kAVFoundationCaptureV2ZeroCopy is enabled or when kInCaptureConvertToNv12 is
// used to convert frames to NV12.
CAPTURE_EXPORT extern const base::Feature kInCapturerScaling;
// Find the best capture format from |formats| for the specified dimensions and
// frame rate. Returns an element of |formats|, or nil.
AVCaptureDeviceFormat* CAPTURE_EXPORT
FindBestCaptureFormat(NSArray<AVCaptureDeviceFormat*>* formats,
int width,
int height,
float frame_rate);
} // namespace media
// TODO(crbug.com/1126690): rename this file to be suffixed by the
// "next generation" moniker.
CAPTURE_EXPORT
@interface VideoCaptureDeviceAVFoundation
: NSObject <AVCaptureVideoDataOutputSampleBufferDelegate> {
@private
// The following attributes are set via -setCaptureHeight:width:frameRate:.
int _frameWidth;
int _frameHeight;
float _frameRate;
// The capture format that best matches the above attributes.
base::scoped_nsobject<AVCaptureDeviceFormat> _bestCaptureFormat;
// A serial queue to deliver frames on, ensuring frames are delivered in
// order.
base::ScopedDispatchObject<dispatch_queue_t> _sampleQueue;
// Protects concurrent setting and using |frameReceiver_|. Note that the
// GUARDED_BY decoration below does not have any effect.
base::Lock _lock;
// Used to avoid UAF in -captureOutput.
base::Lock _destructionLock;
media::VideoCaptureDeviceAVFoundationFrameReceiver* _frameReceiver
GUARDED_BY(_lock); // weak.
bool _capturedFirstFrame GUARDED_BY(_lock);
bool _capturedFrameSinceLastStallCheck GUARDED_BY(_lock);
std::unique_ptr<base::WeakPtrFactory<VideoCaptureDeviceAVFoundation>>
_weakPtrFactoryForStallCheck;
// Used to rate-limit crash reports for https://crbug.com/1168112.
bool _hasDumpedForFrameSizeMismatch;
base::scoped_nsobject<AVCaptureSession> _captureSession;
// |captureDevice_| is an object coming from AVFoundation, used only to be
// plugged in |captureDeviceInput_| and to query for session preset support.
base::scoped_nsobject<AVCaptureDevice> _captureDevice;
base::scoped_nsobject<AVCaptureDeviceInput> _captureDeviceInput;
base::scoped_nsobject<AVCaptureVideoDataOutput> _captureVideoDataOutput;
// When enabled, converts captured frames to NV12.
std::unique_ptr<media::SampleBufferTransformer> _sampleBufferTransformer;
// Transformers used to create downscaled versions of the captured image.
// Enabled when setScaledResolutions is called (i.e.
// media::VideoCaptureFeedback asks for scaled frames on behalf of a consumer
// in the Renderer process), NV12 output is enabled and the kInCapturerScaling
// feature is on.
std::vector<std::unique_ptr<media::SampleBufferTransformer>>
_scaledFrameTransformers;
// An AVDataOutput specialized for taking pictures out of |captureSession_|.
base::scoped_nsobject<AVCaptureStillImageOutput> _stillImageOutput;
size_t _takePhotoStartedCount;
size_t _takePhotoPendingCount;
size_t _takePhotoCompletedCount;
bool _stillImageOutputWarmupCompleted;
std::unique_ptr<base::WeakPtrFactory<VideoCaptureDeviceAVFoundation>>
_weakPtrFactoryForTakePhoto;
// For testing.
base::RepeatingCallback<void()> _onStillImageOutputStopped;
scoped_refptr<base::SingleThreadTaskRunner> _mainThreadTaskRunner;
}
// Previous to any use, clients must call -initWithFrameReceiver: to
// initialise an object of this class and register a |frameReceiver_|. This
// initializes the instance and the underlying capture session and registers the
// frame receiver.
- (instancetype)initWithFrameReceiver:
(media::VideoCaptureDeviceAVFoundationFrameReceiver*)frameReceiver;
// Frame receiver registration or removal can also happen via explicit call
// to -setFrameReceiver:. Re-registrations are safe and allowed, even during
// capture using this method.
- (void)setFrameReceiver:
(media::VideoCaptureDeviceAVFoundationFrameReceiver*)frameReceiver;
// Sets which capture device to use by name, retrieved via |deviceNames|.
// Method -setCaptureDevice: must be called at least once with a device
// identifier from GetVideoCaptureDeviceNames(). It creates all the necessary
// AVFoundation objects on the first call; it connects them ready for capture
// every time. Once the deviceId is known, the library objects are created if
// needed and connected for the capture, and a by default resolution is set. If
// |deviceId| is nil, then the eventual capture is stopped and library objects
// are disconnected. Returns YES on success, NO otherwise. If the return value
// is NO, an error message is assigned to |outMessage|. This method should not
// be called during capture (i.e. between -startCapture and -stopCapture).
- (BOOL)setCaptureDevice:(NSString*)deviceId
errorMessage:(NSString**)outMessage;
// Configures the capture properties for the capture session and the video data
// output; this means it MUST be called after setCaptureDevice:. Return YES on
// success, else NO.
- (BOOL)setCaptureHeight:(int)height
width:(int)width
frameRate:(float)frameRate;
// If an efficient path is available, the capturer will perform scaling and
// deliver scaled frames to the |frameReceiver| as specified by |resolutions|.
// The scaled frames are delivered in addition to the original captured frame.
// Resolutions that match the captured frame or that would result in upscaling
// are ignored.
- (void)setScaledResolutions:(std::vector<gfx::Size>)resolutions;
// Starts video capturing and registers notification listeners. Must be
// called after setCaptureDevice:, and, eventually, also after
// setCaptureHeight:width:frameRate:.
// The capture can be stopped and restarted multiple times, potentially
// reconfiguring the device in between.
// Returns YES on success, NO otherwise.
- (BOOL)startCapture;
// Stops video capturing and stops listening to notifications. Same as
// setCaptureDevice:nil but doesn't disconnect the library objects. The capture
// can be
- (void)stopCapture;
// Takes a photo. This method should only be called between -startCapture and
// -stopCapture.
- (void)takePhoto;
// This function translates Mac Core Video pixel formats to Chromium pixel
// formats. This implementation recognizes NV12.
+ (media::VideoPixelFormat)FourCCToChromiumPixelFormat:(FourCharCode)code;
- (void)setOnStillImageOutputStoppedForTesting:
(base::RepeatingCallback<void()>)onStillImageOutputStopped;
// Use the below only for test.
- (void)callLocked:(base::OnceClosure)lambda;
- (void)processPixelBufferNV12IOSurface:(CVPixelBufferRef)pixelBuffer
captureFormat:
(const media::VideoCaptureFormat&)captureFormat
colorSpace:(const gfx::ColorSpace&)colorSpace
timestamp:(const base::TimeDelta)timestamp;
- (BOOL)processPixelBufferPlanes:(CVImageBufferRef)pixelBuffer
captureFormat:(const media::VideoCaptureFormat&)captureFormat
colorSpace:(const gfx::ColorSpace&)colorSpace
timestamp:(const base::TimeDelta)timestamp;
@end
#endif // MEDIA_CAPTURE_VIDEO_MAC_VIDEO_CAPTURE_DEVICE_AVFOUNDATION_MAC_H_