AVFoundation简单入门二

heavyfish 发布于1年前 阅读12332次
0 条评论

 

在上两篇文章的基础上,我们初步认识了AV Foundation框架,并且可以利用它完成一些特定的需求,今天,继续我们的小小的研究。日进一步,不求速成。

通过了解我们知道,AV Foundation可以用来播放和创建基于时间的音视频资源,并可以精确的处理基于时间的音视频媒体数据(查找、创建、编辑及二次编码),当然也可以在硬件设备上获取实时的视频输入流或视频回放等。AV Foundation可以满足您对媒体数据的大部分处理。

在处理之前,我们需要大致的了解下媒体工程软件里的相关类。一个工程文件中有很多轨道,如音频轨道1,音频轨道2 ...,视频轨道1,视频轨道2等,每个轨道里有许多素材,对于每个视频素材,它可以进行缩放、旋转等操作,素材库中的视频拖到轨道中会分为视频轨和音频轨两个轨道。大致分为以下几个:

AVAsset:素材库里的素材;

AVAssetTrack:素材的轨道;

AVMutableComposition :一个用来合成视频的工程文件;

AVMutableCompositionTrack :工程文件中的轨道,有音频轨、视频轨等,里面可以插入各种对应的素材;

AVMutableVideoCompositionLayerInstruction:视频轨道中的一个视频,可以缩放、旋转等;

AVMutableVideoCompositionInstruction:一个视频轨道,包含了这个轨道上的所有视频素材;

AVMutableVideoComposition:管理所有视频轨道,可以决定最终视频的尺寸,裁剪需要在这里进行;

AVAssetExportSession:配置渲染参数并渲染。

AVAsset

AVFoundation使用AVAsset类来表示一个媒体资源,一个AVAsset实例是一个或多个音视频媒体数据的集合,是一个抽象类。可以使用子类用URL来创建一个asset对象,也可以基于现有的媒体资源创造一个新的媒体资源。下面是AVAsset中的一些属性,分别对应着视频的基本信息,如时长,创建时间等。

@property (nonatomic, readonly) CMTime duration;

@property (nonatomic, readonly) float preferredRate;// 默认速度

@property (nonatomic, readonly) float preferredVolume;// 音量

@property (nonatomic, readonly, nullable) AVMetadataItem *creationDate NS_AVAILABLE(10_8, 5_0);// 视频的创建时间

代码是最好的老师,创建一个AVAsset:

AVAsset *asset = [AVAsset assetWithURL:...];

为了创建一个由URL标识的代表任何资源的assert对象,可以使用AVURLAssert,最简单的是从文件里创建一个assert对象:

NSURL *url = ...;
AVURLAsset *anAsset = [[AVURLAsset alloc] initWithURL:url options:nil];

AVURLAsset初始化方法的第二个参数使用一个dictionary,这个dictionary里的唯一一个key是 AVURLAssetPreferPreciseDurationAndTimingKey,它的value是一个boolean类型(用NSValue包装的对象),这个值表示asset是否提供一个精确的duration。

AVURLAssetPreferPreciseDurationAndTimingKey值为NO(不传默认为NO),duration会取一个估计值,计算量比较小。反之如果为YES,duration需要返回一个精确值,计算量会比较大,耗时比较长。使用一个预估的duration效率比较高并且对播放来说足够。如果你想要播放asset,初始化方法传nil就行了,而不是一个dictionry,或者传一个以AVURLAssetPreferPreciseDurationAndTimingKeydictionary为key,值为NO的一个dictionary;如果你想把asset加到一个composition中,你需要一个精确的访问权限,这时你可以传一个dictionary,这个dictionary的一组键值对为AVURLAssetPreferPreciseDurationAndTimingKey和YES。

NSURL *url = ...;
NSDictionary *options = @{ AVURLAssetPreferPreciseDurationAndTimingKey : @YES };
AVURLAsset *anAssetToUseInAComposition = [[AVURLAsset alloc] initWithURL:url options:options];

从asset中获取静态图片(比如说缩略图),你可以用AVAssetImageGenerator对象。可以用asset初始化一个AVAssetImageGenerator对象,即使asset在初始化的时候没有可见的track也能成功,所以需要使用tracksWithMediaCharacteristic检测asset是否有track。generateCGImagesAsynchronouslyForTimes:completionHandler: 方法可以生成一系列图片,第一个参数是一个包含NSValue类型的数组,数组里每一个对象都是CMTime结构体,表示你想要生成的图片在视频中的时间点,第二个参数是一个block,每生成一张图片都会回调这个block,这个block提供一个result的参数告诉你图片是否成功生成或者图片生成操作是否取消。

AVURLAsset *asset = [AVURLAsset alloc] initWithURL:... options:nil];

if ([asset tracksWithMediaType:[AVMediaTypeVideo ] count] > 0 )
{
    AVAssetImageGenerator *generator = [[AVAssetImageGenerator alloc]initWithAsset:asset];
    generator.appliesPreferredTrackTransform = YES;

    CMTime time = CMTimeMakeWithSeconds(0, 25);
    NSValue *timeValue = [NSValue valueWithCMTime:time];

    [generator generateCGImagesAsynchronouslyForTimes:@[timeValue] completionHandler:^
     (CMTime requestedTime, CGImageRef image, CMTime actualTime, AVAssetImageGeneratorResult result, NSError *error)
     {
         if (result == AVAssetImageGeneratorSucceeded)
         {
             UIImage *captureImage = [UIImage imageWithCGImage:image];
             // 成功 do something
         }
         else
         {
             // 失敗
         }
     }];
}

AVAssetTrack

一般的视频至少有2个轨道,一个播放声音,一个播放画面。在AVAsset中,可以通过trackId,获得特定的track。

- (nullable AVAssetTrack *)trackWithTrackID:(CMPersistentTrackID)trackID;

除了通过trackID获得track之外,AVAsset中还提供了其他3中方式获得track:

@property (nonatomic, readonly) NSArray<AVAssetTrack *> *tracks;

tracks中包含了当前Asset中的所有track,通过遍历我们可以获得想要的track。

- (NSArray<AVAssetTrack *> *)tracksWithMediaType:(NSString *)mediaType;

-tracksWithMediaType:方法会根据指定的媒体类型返回一个track数组,数组中包含着Asset中所有指定媒体类型的track。如果Asset中没有这个媒体类型的track,返回一个空数组。AVMediaFormat中一共定义了8种媒体类型: AVMediaTypeVideo、AVMediaTypeAudio、AVMediaTypeText、AVMediaTypeClosedCaption、AVMediaTypeSubtitle、AVMediaTypeTimecode、AVMediaTypeMetadata、AVMediaTypeMuxed。

- (NSArray<AVAssetTrack *> *)tracksWithMediaCharacteristic:(NSString *)mediaCharacteristic;

-tracksWithMediaCharacteristic:方法会根据指定的媒体特征返回track数组,数组的特性与-tracksWithMediaType:类似,如果Asset中没有这个媒体特征的track,返回一个空数组。AVMediaFormat中一共定义了15种媒体特征: AVMediaTypeMetadataObject、AVMediaCharacteristicVisual、AVMediaCharacteristicAudible、AVMediaCharacteristicLegible、AVMediaCharacteristicFrameBased、AVMediaCharacteristicIsMainProgramContent、AVMediaCharacteristicIsAuxiliaryContent、AVMediaCharacteristicContainsOnlyForcedSubtitles、AVMediaCharacteristicTranscribesSpokenDialogForAccessibility、AVMediaCharacteristicDescribesMusicAndSoundForAccessibility、AVMediaCharacteristicEasyToRead、AVMediaCharacteristicDescribesVideoForAccessibility、AVMediaCharacteristicLanguageTranslation、AVMediaCharacteristicDubbedTranslation、AVMediaCharacteristicVoiceOverTranslation。

代码实现:

AVAsset *asset = [AVAsset assetWithURL:...];
NSArray* allVideoTracks = [asset tracksWithMediaType:AVMediaTypeVideo];
if ([allVideoTracks count] > 0)
{
   AVAssetTrack* track = [[asset tracksWithMediaType:AVMediaTypeVideo]objectAtIndex:0];
   CGSize size = [track naturalSize];// 本视频的分辨率
}

下面看一个小例:

AVAsset *asset = [AVAsset assetWithURL:...];
AVAssetTrack *videoAssetTrack = [[asset tracksWithMediaType:AVMediaTypeVideo]objectAtIndex:0];//素材的视频轨
AVAssetTrack *audioAssertTrack = [[asset tracksWithMediaType:AVMediaTypeAudio]objectAtIndex:0];//素材的音频轨

//将素材的视频插入视频轨,音频插入音频轨
AVMutableComposition *composition = [AVMutableComposition composition];//这是工程文件
AVMutableCompositionTrack *videoCompositionTrack = [composition addMutableTrackWithMediaType:AVMediaTypeVideo preferredTrackID:kCMPersistentTrackID_Invalid];//视频轨道
[videoCompositionTrack insertTimeRange:CMTimeRangeMake(kCMTimeZero, videoAssetTrack.timeRange.duration) ofTrack:videoAssetTrack atTime:kCMTimeZero error:nil];//在视频轨道插入一个时间段的视频
AVMutableCompositionTrack *audioCompositionTrack = [composition addMutableTrackWithMediaType:AVMediaTypeAudio preferredTrackID:kCMPersistentTrackID_Invalid];//音频轨道
[audioCompositionTrack insertTimeRange: CMTimeRangeMake(kCMTimeZero, videoAssetTrack.timeRange.duration) ofTrack:audioAssertTrack atTime:kCMTimeZero error:nil];//插入音频数据,否则没有声音

// 裁剪视频
AVMutableVideoCompositionLayerInstruction *videoCompositionLayerIns = [AVMutableVideoCompositionLayerInstruction videoCompositionLayerInstructionWithAssetTrack:videoAssetTrack];
[videoCompositionLayerIns setTransform:videoAssetTrack.preferredTransform atTime:kCMTimeZero];//得到视频素材
AVMutableVideoCompositionInstruction *videoCompositionIns = [AVMutableVideoCompositionInstruction videoCompositionInstruction];
[videoCompositionIns setTimeRange:CMTimeRangeMake(kCMTimeZero, videoAssetTrack.timeRange.duration)];//得到视频轨道
AVMutableVideoComposition *videoComposition = [AVMutableVideoComposition videoComposition];
videoComposition.instructions = @[videoCompositionIns];videoComposition.renderSize = CGSizeMake(...);//裁剪出对应的大小
videoComposition.frameDuration = CMTimeMake(1, 30);

// 导出
AVAssetExportSession *exporter = [[AVAssetExportSession alloc] initWithAsset:composition presetName:AVAssetExportPresetMediumQuality];
exporter.videoComposition = videoComposition;
exporter.outputURL = [NSURL fileURLWithPath:... isDirectory:YES];
exporter.outputFileType = AVFileTypeMPEG4;
exporter.shouldOptimizeForNetworkUse = YES;
[exporter exportAsynchronouslyWithCompletionHandler:^{
    if (exporter.error) {
        // 失败处理
    }else{
        // 成功
    }
}];

补充上篇录制视频的示例:

#import "ViewController.h"
#import <AVFoundation/AVFoundation.h>
#import <AssetsLibrary/AssetsLibrary.h>

typedef void(^PropertyChangeBlock) (AVCaptureDevice *captureDevice);

@interface ViewController ()<AVCaptureFileOutputRecordingDelegate>
/** 负责输入和输出设备之间的数据传递 */
@property (nonatomic,strong) AVCaptureSession *captureSession;
/** 负责从AVCaptureDevice获得输入数据 */
@property (nonatomic,strong) AVCaptureDeviceInput *captureDeviceInput;
/** 视频输出流 */
@property (nonatomic,strong) AVCaptureMovieFileOutput *captureMovieFileOutput;
/** 相机拍摄预览图层 */
@property (nonatomic,strong) AVCaptureVideoPreviewLayer *captureVideoPreviewLayer;
/** 是否允许屏幕旋转(在录制屏幕中禁止旋转) */
@property (nonatomic,assign) BOOL enableRotation;
/** 旋转前大小 */
@property (nonatomic,assign) CGRect *lastBounds;
/** 后台任务标识 */
@property (nonatomic,assign) UIBackgroundTaskIdentifier backgroundTaskIdentifier;


@property (weak, nonatomic) IBOutlet UIView *viewContainer;
/** 自动闪光按钮 */
@property (weak, nonatomic) IBOutlet UIButton *flashAutoButton;
/** 打开闪光按钮 */
@property (weak, nonatomic) IBOutlet UIButton *flashOnButton;
/** 关闭闪光按钮 */
@property (weak, nonatomic) IBOutlet UIButton *flashOffButton;
/** 聚焦框 */
@property (weak, nonatomic) IBOutlet UIImageView *focusCursor;
/** 拍照按钮 */
@property (weak, nonatomic) IBOutlet UIButton *takeButton1;
@end

@implementation ViewController

- (void)viewDidLoad {
[super viewDidLoad];
}

- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
// 初始化会话
_captureSession = [[AVCaptureSession alloc] init];
if ([_captureSession canSetSessionPreset:AVCaptureSessionPreset1280x720]) { // 设置分辨率
    _captureSession.sessionPreset = AVCaptureSessionPreset1280x720;
}

// 获得输入设备
AVCaptureDevice *captureDevice = [self getCameraDeviceWithPosition:AVCaptureDevicePositionBack]; // 得道后置摄像头
if (!captureDevice) {
    NSLog(@"取得后置摄像头出现错误");
    return;
}

// 添加一个音频输入设备
AVCaptureDevice *audioCaptureDevice = [[AVCaptureDevice devicesWithMediaType:AVMediaTypeAudio] firstObject];

NSError *error = nil;
// 根据输入设备初始化输入对象,用于获得输入数据
_captureDeviceInput = [[AVCaptureDeviceInput alloc] initWithDevice:captureDevice error:&error];
if (error) {
    NSLog(@"取得设备输入对象时出错,错误原因:%@",error.localizedDescription);
    return;
}
AVCaptureDeviceInput *audioCaptureDeviceInput = [[AVCaptureDeviceInput alloc] initWithDevice:audioCaptureDevice error:&error];
if (error) {
    NSLog(@"取得设备输入对象时出错,错误原因:%@",error.localizedDescription);
    return;
}

// 初始化输出设备对象,用于获得输出数据
_captureMovieFileOutput = [[AVCaptureMovieFileOutput alloc] init];

// 将设备输入添加到会话中
if ([_captureSession canAddInput:_captureDeviceInput]) {
    [_captureSession addInput:_captureDeviceInput];
    [_captureSession addInput:audioCaptureDeviceInput];
    AVCaptureConnection *captureConnection=[_captureMovieFileOutput connectionWithMediaType:AVMediaTypeVideo];
    if ([captureConnection isVideoStabilizationSupported ]) {
        captureConnection.preferredVideoStabilizationMode=AVCaptureVideoStabilizationModeAuto;//视频防抖
    }
}

// 将设备输出添加到会话中
if ([_captureSession canAddOutput:_captureMovieFileOutput]) {
    [_captureSession addOutput:_captureMovieFileOutput];
}

// 创建视频预览层,用于时实展示摄像头状态
_captureVideoPreviewLayer = [[AVCaptureVideoPreviewLayer alloc] initWithSession:self.captureSession];
CALayer *layer = self.viewContainer.layer;
layer.masksToBounds = YES;  // 设置图层的圆角属性

_captureVideoPreviewLayer.frame = layer.bounds;
_captureVideoPreviewLayer.videoGravity = AVLayerVideoGravityResize; // 填充模式显示在layer上
// 将视频预览层添加到界面中
[layer insertSublayer:_captureVideoPreviewLayer below:self.focusCursor.layer];

_enableRotation=YES;
[self addNotificationToCaptureDevice:captureDevice];  // 给输入设备添加通知
[self addGenstureRecognizer]; // 添加手势
[self setFlashModeButtonStatus]; // 设置闪光灯按钮状态
}

// 在控制器视图展示和视图离开界面时启动,停止会话
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
[self.captureSession startRunning];
}

- (void)viewDidDisappear:(BOOL)animated
{
[super viewDidDisappear:animated];
[self.captureSession stopRunning];
}

- (BOOL)shouldAutorotate
{
return self.enableRotation;
}
// 旋转屏幕时调整视频预览图层的方向
- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration
{
AVCaptureConnection *captureConnection = [self.captureVideoPreviewLayer connection];
captureConnection.videoOrientation = (AVCaptureVideoOrientation)toInterfaceOrientation;
}

// 旋转后重新设置大小
- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation
{
_captureVideoPreviewLayer.frame = self.viewContainer.bounds;
}
-(void)dealloc{
[self removeNotification];
}

#pragma -mark 通知
/**
 * 给输入设备添加通知
 *
 */
- (void)addNotificationToCaptureDevice:(AVCaptureDevice *)captureDevice
{
//注意添加区域改变捕获通知必须首先设置设备允许捕获
[self changeDeviceProperty:^(AVCaptureDevice *captureDevice) {
    captureDevice.subjectAreaChangeMonitoringEnabled = YES;
}];
NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
// 捕获区域发生改变
[notificationCenter addObserver:self selector:@selector(areaChange:) name:AVCaptureDeviceSubjectAreaDidChangeNotification object:captureDevice];
}

- (void)removeNotificationFromCaptureDevice:(AVCaptureDevice *)captureDevice
{
NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
[notificationCenter removeObserver:self name:AVCaptureDeviceSubjectAreaDidChangeNotification object:captureDevice];
}

/**
 *  移除所有通知
 */
-(void)removeNotification{
NSNotificationCenter *notificationCenter= [NSNotificationCenter defaultCenter];
[notificationCenter removeObserver:self];
}
-(void)addNotificationToCaptureSession:(AVCaptureSession *)captureSession{
NSNotificationCenter *notificationCenter= [NSNotificationCenter defaultCenter];
//会话出错
[notificationCenter addObserver:self selector:@selector(sessionRuntimeError:) name:AVCaptureSessionRuntimeErrorNotification object:captureSession];
}

/**
 *  设备连接成功
 *
 *  @param notification 通知对象
 */
-(void)deviceConnected:(NSNotification *)notification{
NSLog(@"设备已连接...");
}
/**
 *  设备连接断开
 *
 *  @param notification 通知对象
 */
-(void)deviceDisconnected:(NSNotification *)notification{
NSLog(@"设备已断开.");
}
/**
 *  捕获区域改变
 *
 *  @param notification 通知对象
 */
-(void)areaChange:(NSNotification *)notification{
NSLog(@"捕获区域改变...");
}

/**
 *  会话出错
 *
 *  @param notification 通知对象
 */
-(void)sessionRuntimeError:(NSNotification *)notification{
NSLog(@"会话发生错误.");
}

#pragma -mark 私有方法
/**
 *  取得指定位置的摄像头
 *
 *  @param position 摄像头位置
 *
 *  @return 摄像头设备
 */
-(AVCaptureDevice *)getCameraDeviceWithPosition:(AVCaptureDevicePosition )position{
NSArray *cameras= [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo];
for (AVCaptureDevice *camera in cameras) {
    if ([camera position] == position) {
        return camera;

    }
}
return nil;
}

/**
 * 改变设备属性的统一操作方法
 * @param propertyChange 属性改变操作
 */
- (void)changeDeviceProperty:(PropertyChangeBlock)propertyChange
{
AVCaptureDevice *captureDevice = [self.captureDeviceInput device];
NSError *error;
//注意改变设备属性前一定要首先调用lockForConfiguration:调用完之后使用unlockForConfiguration方法解锁
if ([captureDevice lockForConfiguration:&error]) {
    propertyChange(captureDevice);
    [captureDevice unlockForConfiguration];
}else{
    NSLog(@"设置设备属性过程发生错误,错误信息:%@",error.localizedDescription);
}
}

/**
 *  设置闪光灯模式
 *
 *  @param flashMode 闪光灯模式
 */
-(void)setFlashMode:(AVCaptureFlashMode )flashMode{
[self changeDeviceProperty:^(AVCaptureDevice *captureDevice) {
    if ([captureDevice isFlashModeSupported:flashMode]) {
        [captureDevice setFlashMode:flashMode];
    }
}];
}

/**
 *  设置聚焦模式
 *
 *  @param focusMode 聚焦模式
 */
-(void)setFocusMode:(AVCaptureFocusMode )focusMode{
[self changeDeviceProperty:^(AVCaptureDevice *captureDevice) {
    if ([captureDevice isFocusModeSupported:focusMode]) {
        [captureDevice setFocusMode:focusMode];
    }
}];
}
/**
 *  设置曝光模式
 *
 *  @param exposureMode 曝光模式
 */
-(void)setExposureMode:(AVCaptureExposureMode)exposureMode{
[self changeDeviceProperty:^(AVCaptureDevice *captureDevice) {
    if ([captureDevice isExposureModeSupported:exposureMode]) {
        [captureDevice setExposureMode:exposureMode];
    }
}];
}

/**
 *  设置聚焦点
 *
 *  @param point 聚焦点
 */
-(void)focusWithMode:(AVCaptureFocusMode)focusMode exposureMode:(AVCaptureExposureMode)exposureMode atPoint:(CGPoint)point{
[self changeDeviceProperty:^(AVCaptureDevice *captureDevice) {
    if ([captureDevice isFocusModeSupported:focusMode]) {
        [captureDevice setFocusMode:AVCaptureFocusModeAutoFocus];
    }
    if ([captureDevice isFocusPointOfInterestSupported]) {
        [captureDevice setFocusPointOfInterest:point];
    }
    if ([captureDevice isExposureModeSupported:exposureMode]) {
        [captureDevice setExposureMode:AVCaptureExposureModeAutoExpose];
    }
    if ([captureDevice isExposurePointOfInterestSupported]) {
        [captureDevice setExposurePointOfInterest:point];
    }
}];
}

/**
 * 添加手势:点按时聚焦
 *
 */
- (void)addGenstureRecognizer
{
UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tapScreen:)];
[self.viewContainer addGestureRecognizer:tapGesture];
}
- (void)tapScreen:(UITapGestureRecognizer *)tapGesture
{
CGPoint point= [tapGesture locationInView:self.viewContainer];
NSLog(@"(%f,%f)",point.x,point.y);
//将UI坐标转化为摄像头坐标
CGPoint cameraPoint= [self.captureVideoPreviewLayer captureDevicePointOfInterestForPoint:point];
[self setFocusCursorWithPoint:point];
[self focusWithMode:AVCaptureFocusModeAutoFocus exposureMode:AVCaptureExposureModeAutoExpose atPoint:cameraPoint];
}


/**
 *  设置聚焦光标位置
 *
 *  @param point 光标位置
 */
-(void)setFocusCursorWithPoint:(CGPoint)point{
self.focusCursor.center=point;
self.focusCursor.transform=CGAffineTransformMakeScale(1.5, 1.5);
self.focusCursor.alpha=1.0;
[UIView animateWithDuration:1.0 animations:^{
    self.focusCursor.transform=CGAffineTransformIdentity;
} completion:^(BOOL finished) {
    self.focusCursor.alpha=0;
}];
}

/**
 *  设置闪光灯按钮状态
 */
-(void)setFlashModeButtonStatus{
AVCaptureDevice *captureDevice=[self.captureDeviceInput device];
AVCaptureFlashMode flashMode=captureDevice.flashMode;
if([captureDevice isFlashAvailable]){  // 如果当前闪光可用(后置摄像头时闪光灯可用,前置摄像头时闪光灯不可用)
    self.flashAutoButton.hidden=NO;
    self.flashOnButton.hidden=NO;
    self.flashOffButton.hidden=NO;
    self.flashAutoButton.enabled=YES;
    self.flashOnButton.enabled=YES;
    self.flashOffButton.enabled=YES;
    switch (flashMode) {
        case AVCaptureFlashModeAuto:
            self.flashAutoButton.enabled=NO;
            break;
        case AVCaptureFlashModeOn:
            self.flashOnButton.enabled=NO;
            break;
        case AVCaptureFlashModeOff:
            self.flashOffButton.enabled=NO;
            break;
        default:
            break;
    }
}else{
    self.flashAutoButton.hidden=YES;
    self.flashOnButton.hidden=YES;
    self.flashOffButton.hidden=YES;
}
}

#pragma mark -按钮处理
#pragma mark 切换前后摄像头
- (IBAction)toggleButtonClick1:(UIButton *)sender
{
// 得到当前的输入设备并删除其上面的通知
AVCaptureDevice *currentDevice=[self.captureDeviceInput device];
AVCaptureDevicePosition currentPosition=[currentDevice position];
[self removeNotificationFromCaptureDevice:currentDevice];

AVCaptureDevice *toChangeDevice;
AVCaptureDevicePosition toChangePosition=AVCaptureDevicePositionFront;
if (currentPosition==AVCaptureDevicePositionUnspecified||currentPosition==AVCaptureDevicePositionFront) { // 如果当前输入设备不是前置摄像头
    toChangePosition=AVCaptureDevicePositionBack;
}
toChangeDevice=[self getCameraDeviceWithPosition:toChangePosition]; // 取得摄像头设备
[self addNotificationToCaptureDevice:toChangeDevice];  // 给设备加入通知
//获得要调整的设备输入对象
AVCaptureDeviceInput *toChangeDeviceInput=[[AVCaptureDeviceInput alloc]initWithDevice:toChangeDevice error:nil];

//改变会话的配置前一定要先开启配置,配置完成后提交配置改变
[self.captureSession beginConfiguration];
//移除原有输入对象
[self.captureSession removeInput:self.captureDeviceInput];
//添加新的输入对象
if ([self.captureSession canAddInput:toChangeDeviceInput]) {
    [self.captureSession addInput:toChangeDeviceInput];
    self.captureDeviceInput=toChangeDeviceInput;
}
//提交会话配置
[self.captureSession commitConfiguration];


[self setFlashModeButtonStatus];
}
#pragma mark 录制视频
- (IBAction)takeButtonClick11:(UIButton *)sender {
//根据设备输出获得连接
AVCaptureConnection *captureConnection=[self.captureMovieFileOutput connectionWithMediaType:AVMediaTypeVideo];
//根据连接取得设备输出的数据
if (![self.captureMovieFileOutput isRecording]) {  // 如果此时没有在录屏
    self.enableRotation=NO;
    //如果支持多任务则则开始多任务
    if ([[UIDevice currentDevice] isMultitaskingSupported]) {
        self.backgroundTaskIdentifier=[[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:nil];
    }
    //预览图层和视频方向保持一致
    captureConnection.videoOrientation=[self.captureVideoPreviewLayer connection].videoOrientation;
    NSString *outputFielPath=[NSTemporaryDirectory() stringByAppendingString:@"myMovie.mov"];
    NSLog(@"save path is :%@",outputFielPath);
    NSURL *fileUrl=[NSURL fileURLWithPath:outputFielPath];
    NSLog(@"fileUrl:%@",fileUrl);
    [self.captureMovieFileOutput startRecordingToOutputFileURL:fileUrl recordingDelegate:self];
}
else{
    [self.captureMovieFileOutput stopRecording];//停止录制
}
}
#pragma mark 自动闪光灯开启
- (IBAction)flashAutoClick1:(UIButton *)sender {
[self setFlashMode:AVCaptureFlashModeAuto];
[self setFlashModeButtonStatus];
}
#pragma mark 打开闪光灯
- (IBAction)flashOnClick1:(UIButton *)sender {
[self setFlashMode:AVCaptureFlashModeOn];
[self setFlashModeButtonStatus];
}
#pragma mark 关闭闪光灯
- (IBAction)flashOffClick1:(UIButton *)sender {
[self setFlashMode:AVCaptureFlashModeOff];
[self setFlashModeButtonStatus];
}


#pragma mark -AVCaptureFileOutputRecordingDelegate  视频输出代理中的方法
// optional
// 当数据开始写入文件的时候调用,如果数据写入错误,则该方法不会被调用
- (void)captureOutput:(AVCaptureFileOutput *)captureOutput didStartRecordingToOutputFileAtURL:(NSURL *)fileURL fromConnections:(NSArray *)connections
{
NSLog(@"开始录制");
}
// required
// 当数据写入完成时调用该方法
- (void)captureOutput:(AVCaptureFileOutput *)captureOutput didFinishRecordingToOutputFileAtURL:(NSURL *)outputFileURL fromConnections:(NSArray *)connections error:(NSError *)error
{
NSLog(@"视频录制完成");
//视频录制完成之后在后台将视频存储到相簿
self.enableRotation = YES;
UIBackgroundTaskIdentifier lastBackgroundTaskIdentifier = self.backgroundTaskIdentifier;
self.backgroundTaskIdentifier = UIBackgroundTaskInvalid;
ALAssetsLibrary *assetsLibrary = [[ALAssetsLibrary alloc] init];
[assetsLibrary writeVideoAtPathToSavedPhotosAlbum:outputFileURL completionBlock:^(NSURL *assetURL, NSError *error) {
    if (error) {
        NSLog(@"保存视频到相簿的过程发生错误,错误信息:%@",error.localizedDescription);
    }
    NSLog(@"outputURL:%@",outputFileURL);
    [[NSFileManager defaultManager] removeItemAtURL:outputFileURL error:nil];
    if (lastBackgroundTaskIdentifier != UIBackgroundTaskInvalid) {
        [[UIApplication sharedApplication] endBackgroundTask:lastBackgroundTaskIdentifier];
    }
    NSLog(@"成功保存视频到相簿");
}];
}

@end
需要 登录 后回复方可回复, 如果你还没有账号你可以 注册 一个帐号。