iOS音视频录制并保存1

290 阅读2分钟

//

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