//
// IDCaptureSessionCoordinator.h
// VideoCaptureDemo
//
// Created by lj on 2023/3/22.
// Copyright © 2023 lj. All rights reserved.
//
#import <Foundation/Foundation.h>
#import <AVFoundation/AVFoundation.h>
NS_ASSUME_NONNULL_BEGIN
@class IDCaptureSessionCoordinator;
@protocol IDCaptureSessionCoordinatorDelegate
@required
-
(void)coordinatorDidBeginRecording:(IDCaptureSessionCoordinator *)coordinator;
-
(void)coordinator:(IDCaptureSessionCoordinator *)coordinator didFinishRecordingToOutputFileURL:(NSURL *)outputFileURL error:(NSError *)error;
-
(void)IDCaptureOutput:(AVCaptureOutput *)captureOutput
didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer
fromConnection:(AVCaptureConnection *)connection;
-
(void)IDWriterCoordinator:(id)coordinator didFailWithError:(NSError *)error;
-
(void)IDNeedDetectAudioSampleBuffer:(CMSampleBufferRef)sampleBuffer;
@end
@interface IDCaptureSessionCoordinator : NSObject
@property (nonatomic, strong) AVCaptureSession *captureSession;
@property (nonatomic, strong) AVCaptureDeviceInput *videoInput;//视频输入
@property (nonatomic, strong) AVCaptureDeviceInput *audioMicInput;//麦克风输入
@property (nonatomic, assign) AVCaptureDevicePosition positionSetting;
@property (nonatomic, strong) AVCaptureVideoPreviewLayer *previewLayer;
@property (nonatomic, copy) NSString *path;
@property (nonatomic, strong) dispatch_queue_t delegateCallbackQueue;
@property (nonatomic, weak) id delegate;
-
(void)setDelegate:(id)delegate callbackQueue:(dispatch_queue_t)delegateCallbackQueue;
//切换前后置摄像头
-
(void)switchCamera;
-
(BOOL)isRunning;
-
(void)startRunning;
-
(void)stopRunning:(void(^)(NSError *error))complete;
-
(void)startRecording;
-
(void)stopRecording:(void(^)(NSError *error))complete;
@end
NS_ASSUME_NONNULL_END
//
// IDCaptureSessionCoordinator.m
// VideoCaptureDemo
//
// Created by lj on 2023/3/22.
// Copyright © 2023 lj. All rights reserved.
//
#import "IDCaptureSessionCoordinator.h"
@interface IDCaptureSessionCoordinator ()
@property (nonatomic, strong) dispatch_queue_t sessionQueue;
@end
@implementation IDCaptureSessionCoordinator
#pragma mark - Method
- (void)setDelegate:(id)delegate callbackQueue:(dispatch_queue_t)delegateCallbackQueue
{
if(delegate && ( delegateCallbackQueue == NULL)){
@throw [NSException exceptionWithName:NSInvalidArgumentException reason:@"Caller must provide a delegateCallbackQueue" userInfo:nil];
}
@synchronized(self)
{
_delegate = delegate;
if (delegateCallbackQueue != _delegateCallbackQueue){
_delegateCallbackQueue = delegateCallbackQueue;
}
}
}
- (void)switchCamera
{
NSUInteger cameraCount = [[AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo] count];
if (cameraCount > 1) {
NSError *error;
AVCaptureDeviceInput *newVideoInput;
AVCaptureDevicePosition position = [[self.videoInput device] position];
if (position == AVCaptureDevicePositionBack) {
newVideoInput = [[AVCaptureDeviceInput alloc] initWithDevice:[self cameraWithPosition:AVCaptureDevicePositionFront] error:&error];
self.positionSetting = AVCaptureDevicePositionFront;
} else if (position == AVCaptureDevicePositionFront) {
newVideoInput = [[AVCaptureDeviceInput alloc] initWithDevice:[self cameraWithPosition:AVCaptureDevicePositionBack] error:&error];
self.positionSetting = AVCaptureDevicePositionBack;
} else {
return;
}
if (newVideoInput != nil) {
[self.captureSession beginConfiguration];
[self.captureSession removeInput:self.videoInput];
if ([self.captureSession canAddInput:newVideoInput]) {
[self.captureSession addInput:newVideoInput];
self.videoInput = newVideoInput;
} else {
[self.captureSession addInput:self.videoInput];
}
[self.captureSession commitConfiguration];
} else if (error) {
NSLog(@"toggle carema failed, error = %@", error);
}
} else {
NSLog(@"摄像头能正常工作的个数小于2");
}
}
- (BOOL)isRunning
{
return self.captureSession.isRunning;
}
- (void)startRunning
{
if (!self.isRunning) {
dispatch_sync(self.sessionQueue, ^{
[self.captureSession startRunning];
});
}else {
NSLog(@"isRunning");
}
}
- (void)stopRunning:(void(^)(NSError *error))complete;
{
if (self.isRunning) {
dispatch_sync(self.sessionQueue, ^{
// the captureSessionDidStopRunning method will stop recording if necessary as well, but we do it here so that the last video and audio samples are better aligned
[self stopRecording:complete]; //does nothing if we aren't currently recording
[self.captureSession stopRunning];
});
}
}
- (void)startRecording {
//overwritten by subclass
}
- (void)stopRecording:(void(^)(NSError *error))complete {
//overwritten by subclass
}
#pragma mark - getter and setter
//用来返回是前置摄像头还是后置摄像头
- (AVCaptureDevice *)cameraWithPosition:(AVCaptureDevicePosition)position {
//返回视频录制相关的所有默认设备
NSArray *devices = [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo];
//遍历这些设备返回跟position相关的设备
for (AVCaptureDevice *device in devices) {
if ([device position] == position) {
return device;
}
}
return nil;
}
- (AVCaptureDeviceInput *)videoInput
{
NSError *error;
if (!_videoInput) {
_videoInput = [[AVCaptureDeviceInput alloc] initWithDevice:[self cameraWithPosition:AVCaptureDevicePositionFront] error:&error];
}
return _videoInput;
}
//麦克风输入
- (AVCaptureDeviceInput *)audioMicInput
{
if (_audioMicInput == nil) {
AVCaptureDevice *mic = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeAudio];
NSError *error;
_audioMicInput = [AVCaptureDeviceInput deviceInputWithDevice:mic error:&error];
}
return _audioMicInput;
}
- (AVCaptureVideoPreviewLayer *)previewLayer
{
if(!_previewLayer){
_previewLayer = [AVCaptureVideoPreviewLayer layerWithSession:self.captureSession];
_previewLayer.videoGravity = AVLayerVideoGravityResizeAspectFill;
}
return _previewLayer;
}
- (dispatch_queue_t)sessionQueue
{
if (!_sessionQueue) {
_sessionQueue = dispatch_queue_create( "com.example.capturepipeline.session", DISPATCH_QUEUE_SERIAL );
}
return _sessionQueue;
}
@end