六:视屏

视屏多媒体文件主要是存放视屏数据信息,视屏数据量要远远大于音频数据文件,而且视屏编码和解码算法非常复杂,因此早起的计算机由于CPU处理能力差,要采用视屏解压卡硬件支持,视频采集和压缩也要采用硬件卡。按照视频来源可以分为:

  • 本地视屏是将视屏文件放在本地播放,因此速度快、画质好;
  • 网络流媒体视屏,来源于网络不需要存储,广泛应用于视屏点播、网络演示、远程教育、网络视屏广告等互联网信息服务领域

随着移动设备硬件性能提升和网络带宽的提高,高质量视屏文件播放和网络播放都是问题了,能否开发出功能完善、高质量播放等就交给了软件程序本身。

6.1 视屏文件

视屏文件很多,下面我们先介绍一下这些文件及格式的区别:

1.AVI文件
AVI是音频视屏交错(Audio Video Interleaved)的英文缩写,它是微软公司开发的一种符合RIFF文件规范的数字音频与视频文件格式,是将音频与视频同步组合在一起的文件格式。它对视屏文件采用了一种有损压缩方式,但压缩比例高,画面质量不是太好。

2.WMV文件
WMV也是微软公司推出的一种流媒体格式。在同等视屏质量下,WMV格式的体积非常小,因此很适合在晚上播放和传输。可视忧郁微软公司本身的局限性使其WMV的应用发展并不顺利。首先,它是微软公司的产品,它必定要依赖着Windows以及PC,起码要有PC的主板。这就增加了机顶盒的造价,从而影响了视屏广播点播的普及。其次,WMV技术的视屏传输延迟要十几秒钟。

3.RMVB文件
RMVB是一种视屏文件格式,RMVB中的VB指Variable Bit Rate(可改变之比特率),它打破了压缩的平均比特率,使在静态画面下的比特率降低,来达到优化整个视屏中比特率,提高效率,节约资源的目的。RMVB最大特点是在保证文件清晰度的同时具有体积小巧的特点。

4.3GP文件
3GP是一种3G流媒体的视屏编码格式,主要是为了配合3G网络的高传输速度而开发的,也是手机中的一种视屏格式。3GP使用户能够发送大量的数据到移动电话网络,从而明确传输大型文件。是新的移动设备标准格式,应用在手机、PSP等移动设备上,优点是文件体积小,移动性强,适合移动设备使用,缺点是在PC上兼容性差,支持软件少,且播放质量差,帧数低,较AVI等格式相差很多。

5.MOV文件
MOV即QuickTime视频格式,它是苹果公司开发一种音频、视频文件格式,用于存储常用数字媒体类型。MOV格式文件是以轨道(track)的形式组织起来的,一个MOV格式文件结构中可以包含很多轨道。MOV格式文件,画面效果较AVI格式要稍微好一些。

6.MP4文件
MP4(全称MPEG-4 Part 14),是一种使用MPEG-4的多媒体文件格式,文件后缀名为MP4,起源于QuickTime,采用H.264解码。另外,MP4又可理解为MP4播放器,MP4播放器是一种集音频、视频、图片浏览、电子书、收音机等于一体的多功能播放器。

7.M4V文件
M4V是一个标准视屏文件格式,由苹果公司创造。此种格式为iOS设备所使用,同时此格式基于MPEG- 4编码第二版。

6.2 播放视屏

播放视屏在iOS平台由很多方法,主要使用MediaPlayer框架中的MPMoviePlayerController或MPMoviePlayerViewController类实现,或者是使用AVFoundation框架中的AVPlayer和AVQueuePlayer实现。

6.2.1 使用MediaPlayer框架

MediaPlayer框架是一种高层次的API。使用起来比较简单,但是对于视屏播放的控制界面是系统定义好的播放界面,我们只能对这些界面进行有效的控制,MediaPlayer适合于开发一些视屏播放应用。

具体而言,MediaPlayer中的MPMoviePlayerController或MPMoviePlayerViewController可以实现视屏播放。如图所示是MPMoviePlayerController和MPMoviePlayerViewController关系类图。
这里写图片描述
MPMoviePlayerController是核心播放控制类,它虽然后缀命名是“Controller”,但是与视图控制器没有任何关系,它继承的是NSObject,MPMoviePlayerController的构造方法是-initWithContentURL:。如果要想在视图上显示视屏,需要将MPMoviePlayerController的view属性添加到要显示的视图层次结构中。如下图代码所示:

[self.viewForMovie addSubview:player.view];

其中,viewForMovie是要显示视屏的视图,player是MPMoviePlayerController类型的实例对象。

MPMoviePlayerViewController封装了MPMoviePlayerController和UIViewController,提供简单播放控制和视图功能。它继承的是UIViewController,可见它是一个视图控制器类,它的构造方法是-initWithContentURL:与MPMoviePlayerController完全一样。它还有一个重要的属性moviePlayer,这个属性的类型是MPMoviePlayerController,通过这个属性可以获得MPMoviePlayerController对象实例。

下面通过一个实例介绍MediaPlayer 框架的应用,
这里写图片描述

这里写图片描述


#import "ViewController.h"
#import <MediaPlayer/MediaPlayer.h>

@interface ViewController ()

@property (nonatomic, strong) MPMoviePlayerViewController * moviePlayerView;
@property (nonatomic, strong) MPMoviePlayerController * moviePlayer;

- (IBAction)useMPMoviePlayerController:(id)sender;
- (IBAction)useMPMoviePlayerViewController:(id)sender;

@end

@implementation ViewController

- (void)viewDidLoad
{
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
}

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

-(NSURL *)movieURL
{
    NSBundle *bundle = [NSBundle mainBundle];
    NSString *moviePath = [bundle pathForResource:@"YY"
                                           ofType:@"mp4"];
    if (moviePath) {
        return [NSURL fileURLWithPath:moviePath];
    } else {
        return nil;
    }
}

- (IBAction)useMPMoviePlayerController:(id)sender
{

    if (_moviePlayer == nil) {
        _moviePlayer = [[MPMoviePlayerController alloc] initWithContentURL:[self movieURL]];

        _moviePlayer.scalingMode = MPMovieScalingModeAspectFit;
        _moviePlayer.controlStyle = MPMovieControlStyleDefault;
        [self.view addSubview:_moviePlayer.view];

        // 在播放完成时发出
        [[NSNotificationCenter defaultCenter] addObserver:self
                                                 selector: @selector(playbackFinished4MoviePlayerController:)
                                                     name:MPMoviePlayerPlaybackDidFinishNotification
                                                   object:nil];
        // 退出全屏时发出
        [[NSNotificationCenter defaultCenter] addObserver:self
                                                 selector:@selector(doneButtonClick:)
                                                     name:MPMoviePlayerWillExitFullscreenNotification
                                                   object:nil];
    }

    [_moviePlayer play];
    [_moviePlayer setFullscreen:YES animated:YES];
}

- (IBAction)useMPMoviePlayerViewController:(id)sender
{
    if (_moviePlayerView == nil) {
        _moviePlayerView = [[MPMoviePlayerViewController alloc] initWithContentURL:[self movieURL]];
        _moviePlayerView.moviePlayer.scalingMode = MPMovieScalingModeAspectFill;
        _moviePlayerView.moviePlayer.controlStyle = MPMovieControlStyleEmbedded;

        [[NSNotificationCenter defaultCenter] addObserver:self
                                                 selector: @selector(playbackFinished4MoviePlayerViewController:)
                                                     name:MPMoviePlayerPlaybackDidFinishNotification
                                                   object:nil];
    }


    [self presentMoviePlayerViewControllerAnimated:_moviePlayerView];
}

- (void) playbackFinished4MoviePlayerController: (NSNotification*) notification
{
    NSLog(@"使用MPMoviePlayerController播放完成.");
    [[NSNotificationCenter defaultCenter] removeObserver:self];
    [_moviePlayer stop];
    [_moviePlayer.view removeFromSuperview];
    _moviePlayer = nil;
}

-(void)doneButtonClick:(NSNotification*)aNotification
{
    NSLog(@"退出全屏.");
    if (_moviePlayer.playbackState == MPMoviePlaybackStateStopped)
    {
        [_moviePlayer.view removeFromSuperview];
        _moviePlayer = nil;
    }
}

- (void) playbackFinished4MoviePlayerViewController: (NSNotification*) notification
{
    NSLog(@"使用MPMoviePlayerViewController播放完成.");
    [[NSNotificationCenter defaultCenter] removeObserver:self];
    [_moviePlayerView dismissMoviePlayerViewControllerAnimated];
    _moviePlayerView = nil;
}

@end

说明:
_moviePlayer.scalingMode = MPMovieScalingModeAspectFit;
设置视屏的缩放属性:当视屏的尺寸与屏幕的尺寸不一致的情况下,
通过设置缩放模式可以控制视屏在屏幕上显示方式,它的取值有:

  • MPMovieScalingModeNone 原始寸尺显示
  • MPMovieScalingModeAspectFit 保持原始高宽比比例缩放视屏,使其填充一个方向,另一个方向会有黑边
  • MPMovieScalingModeAspectFill 保持原始高宽比例缩放视屏,使其填充一个方向,另一个方向可能超出屏幕,则会切除
  • MPMovieScalingModeFill 两个方向刚好填充两边,不考虑保持原始高宽比缩放视屏,结果有可能会高宽比例失真
    这里写图片描述

_moviePlayerView.moviePlayer.controlStyle = MPMovieControlStyleEmbedded;是设置视屏的controlStyle属性,controlStyle属性可以控制播放风格,它的取值有:

  • MPMovieControlStyleNone。没有播放控制控件,适合于游戏等应用过渡界面或片尾视屏等
  • MPMovieControlStyleStyleFullScreen。全屏播放,有播放进度、Done按钮、快进等控件
  • MPMovieControlStyleDefault。默认风格,没有Done按钮
  • MPMovieControlStyleEmbedded。嵌入风格的播放控制控件,没有Done按钮
    这里写图片描述

dd_moviePlayer.playbackState == MPMoviePlaybackStateStopped是判断视屏播放状态是否停止,playbackState是视屏播放状态属性,它的取值有:

  • MPMoviePlaybackStateStopped 停止状态
  • MPMoviePlaybackStateplaying 播放状态
  • MPMoviePlaybackStatePaused 暂停状态
  • MPMoviePlaybackStateInterrupted 临时中断状态
  • MPMoviePlaybackStateSeekingForward 向前跳过状态
  • MPMoviePlaybackStateSeekingBackward 向后跳过状态

6.2.2 使用AVFoundation框架

在AVFoundation框架中也有播放视屏的相关类,这些类能够使我们对播放视屏具有更加细粒度的控制。我们可以在视图上采用自己设计的播放控制控件,可以同时播放多个视屏,AVFoundation框架还可以实现在动画中夹杂一些视屏片段的效果,这经常被用于游戏场景的过渡。
与MediaPlayer框架不同,使用AVFoundation框架播放视屏设计的类会比较多,而且开发起来比较负责,如图所示是AVFoundation框架中视屏播放相关类图
这里写图片描述

从图中可以看出涉及6个类

  • AVPlayer,核心的播放视屏需要的核心类,它有4个构造方法,可以通过NSURL或AVPlayerItem构造它。在呈现视屏时需要把AVPlayer放入到AVPlayerLayer中,然后再把AVPlayerLayer对象添加到当前视图的图层上。
  • AVQueuePlayer,如果需要播放多个视屏时,可以使用AVQueuePlayer对象,它可以有多个AVPlayerItem对象组成。
  • AVPlayerLayer,视屏播放图层对象,它时需要添加到当前视图的图层上。
  • AVAsset,代表一个抽象的媒体,包含标题,文件大小等等,不关联任何格式。每个AVAsset由多个track组成,每个track可以时一个音频通道或者视屏通道。
  • AVURLAsset,是AVAsset具体实现类,可以使用NSURL来初始化AVURLAsset
  • AVPlayerItem,是代表一个AVAsset状态,可以使用它观察到视屏播放状态
#import "ViewController.h"
#import <AVFoundation/AVFoundation.h>

@interface ViewController ()
{
    id timeObserver;
    BOOL isPlaying;

}

- (IBAction)play:(id)sender;
- (IBAction)seek:(id)sender;

@property (weak, nonatomic) IBOutlet UISlider *slider;

@property (weak, nonatomic) IBOutlet UIToolbar *toolBar;

@property (nonatomic,weak) AVPlayer *avPlayer;
@property (nonatomic,weak) AVPlayerLayer *layer;
@property (nonatomic,strong) AVPlayerItem *playerItem;

@end

@implementation ViewController
- (void)viewDidLoad
{
    [super viewDidLoad];

    NSString *filepath = [[NSBundle mainBundle] pathForResource:@"YY" ofType:@"mp4"];
    NSURL *fileURL = [NSURL fileURLWithPath:filepath];


    AVURLAsset *asset = [AVURLAsset URLAssetWithURL:fileURL options:nil];
    self.playerItem = [AVPlayerItem playerItemWithAsset:asset];

    self.avPlayer = [AVPlayer playerWithPlayerItem: self.playerItem ];

    self.layer = [AVPlayerLayer playerLayerWithPlayer:self.avPlayer];

    float scale = 1.776;

    self.layer.frame = CGRectMake(0, -350,
                                  self.view.frame.size.width * scale,
                                  self.view.frame.size.height * scale);

    [self.view.layer insertSublayer:self.layer atIndex:0];

    double duration = CMTimeGetSeconds(asset.duration);

    self.slider.maximumValue =  duration;
    self.slider.minimumValue  = 0.0;

    isPlaying = NO;


}


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


- (IBAction)play:(id)sender {

    UIBarButtonItem *item1;

    if (!isPlaying) {

        [self addObserver];
        [self.avPlayer seekToTime:kCMTimeZero];
        [self.avPlayer play];

        isPlaying = YES;
        item1 = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemPause
                                                              target:self
                                                              action:@selector(play:)];


    } else  {
        isPlaying = NO;
        item1 = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemPlay
                                                              target:self
                                                              action:@selector(play:)];

        [self.avPlayer pause];
    }

    NSMutableArray *items = [[NSMutableArray alloc] initWithArray:[self.toolBar items]];
    [items replaceObjectAtIndex:0 withObject:item1];

    [self.toolBar setItems:items];


}

/*
在该方法中需要将滑块与AVPlayer播放时间同步
其中AVPlayer的seekToTime:方法是跳到指定的播放时间,但是这里的时间CMTime类型,我们需要把秒转换为CMTime时间,
CMTimeMakeWithSeconds函数实现这个转换,它的第一个参数,是一个时间值,第二个参数是时标,
CMTimeMakeWithSeconds(value, 10)则说明它的时间单位是十分之一秒
*/
- (IBAction)seek:(id)sender {
    float value = [self.slider value];
    [self.avPlayer seekToTime:CMTimeMakeWithSeconds(value, 10)];
}


-(void)addObserver{

    if (timeObserver == nil) {

        [[NSNotificationCenter defaultCenter] addObserver:self
                                                 selector:@selector(playerItemDidReachEnd:)
                                                     name:AVPlayerItemDidPlayToEndTimeNotification
                                                   object:self.playerItem];

        timeObserver = [self.avPlayer addPeriodicTimeObserverForInterval:CMTimeMake(1, 10)
                                                                   queue:dispatch_get_main_queue()
                                                              usingBlock:^(CMTime time) {
                                                                  float duration = CMTimeGetSeconds(self.avPlayer.currentTime);
                                                                  NSLog(@"duration = %f",duration);
                                                                  self.slider.value = duration;
                                                              }
                        ];

    }

}


- (void) playerItemDidReachEnd:(NSNotification*) aNotification
{
    NSLog(@"播放完成");
    if (timeObserver) {
        [self.avPlayer removeTimeObserver:timeObserver];

        timeObserver = nil;
        self.slider.value = 0.0;
        isPlaying = NO;

        [[NSNotificationCenter defaultCenter] removeObserver:self
                                                        name:AVPlayerItemDidPlayToEndTimeNotification
                                                      object:nil];


        UIBarButtonItem* item1 = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemPlay
                                                                               target:self
                                                                               action:@selector(play:)];






        NSMutableArray *items = [[NSMutableArray alloc] initWithArray:[self.toolBar items]];
        [items replaceObjectAtIndex:0 withObject:item1];

        [self.toolBar setItems:items];
    }

}


@end

6.3 录制视屏

视屏录制也有两种主要的技术:一个是UIImagePickerController,另一个是使用AVFoundation框架

6.3.1 使用UIImagePickerController

#import "ViewController.h"
#import <MobileCoreServices/MobileCoreServices.h>
#import <QuartzCore/QuartzCore.h>

@interface ViewController ()
<UIImagePickerControllerDelegate,UINavigationControllerDelegate>

- (IBAction)videoRecod:(id)sender;


@end

@implementation ViewController

- (void)viewDidLoad
{
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
}

- (void)didReceiveMemoryWarning
{
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

- (IBAction)videoRecod:(id)sender {

    if ([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera]) {

        UIImagePickerController *imagePickerController = [[UIImagePickerController alloc] init];
        imagePickerController.delegate = self;
        imagePickerController.sourceType = UIImagePickerControllerSourceTypeCamera;

        imagePickerController.mediaTypes = [[NSArray alloc]
                                            initWithObjects:(NSString *)kUTTypeMovie, nil];


        //录制质量设定
        imagePickerController.videoQuality = UIImagePickerControllerQualityTypeHigh;

        //只允许最多录制30秒时间
        imagePickerController.videoMaximumDuration = 30.0f;

        [self presentViewController:imagePickerController animated:YES completion:nil];

    } else {
        NSLog(@"摄像头不可用。");
    }
}


- (void) imagePickerControllerDidCancel: (UIImagePickerController *) picker {
    [self dismissViewControllerAnimated:YES completion:nil];
}

- (void) imagePickerController: (UIImagePickerController *) picker
            didFinishPickingMediaWithInfo: (NSDictionary *) info {

    NSURL *url = [info objectForKey:UIImagePickerControllerMediaURL];
    NSString *tempFilePath = [url path];

    if ( UIVideoAtPathIsCompatibleWithSavedPhotosAlbum(tempFilePath) ) {
        UISaveVideoAtPathToSavedPhotosAlbum( tempFilePath,
                                            self,
                                            @selector(video:didFinishSavingWithError:contextInfo:),
                                            (__bridge void *)(tempFilePath));
    }

    [self dismissViewControllerAnimated:YES completion:nil];

}


- (void)video:(NSString *)videoPath didFinishSavingWithError:(NSError *)error contextInfo:(NSString *)contextInfo {
    NSString *title; NSString *message;
    if (!error) {
        title = @"视频保存";
        message = @"视频已经保存到设备的相机胶卷中";
    } else {
        title =  @"视频失败";
        message = [error description];
    }
    UIAlertView *alert = [[UIAlertView alloc] initWithTitle:title
                                                    message:message
                                                   delegate:nil
                                          cancelButtonTitle:@"OK"
                                          otherButtonTitles:nil];
    [alert show];
}


// 可以在该方法中做一些初始化的处理
- (void)navigationController:(UINavigationController *)navigationController
      willShowViewController:(UIViewController *)viewController
                    animated:(BOOL)animated
{
    NSLog(@"选择器将要显示。");
}

// 可以在该方法中国年做些释放资源的处理
- (void)navigationController:(UINavigationController *)navigationController
       didShowViewController:(UIViewController *)viewController
                    animated:(BOOL)animated
{
    NSLog(@"选择器显示结束。");
}

@end

说明:
imagePickerController.videoQuality = UIImagePickerControllerQualityTypeHigh;是设置录制的视屏质量,videoQuality属性的取值有:

  • UIImagePickerControllerQualityTypeHigh 录制高质量视屏
  • UIImagePickerControllerQualityTypeMedium 录制中质量视屏
  • UIImagePickerControllerQualityTypeLow 录制低质量视屏

NSURL *url = [info objectForKey:UIImagePickerControllerMediaURL];
NSString *tempFilePath = [url path];
获得媒体文件保存路径,这个路径是临时性保存的

UIVideoAtPathIsCompatibleWithSavedPhotosAlbum函数测试是否可以将制定的媒体文件保存到相机胶卷库中。UISaveVideoAtPathToSavedPhotosAlbum函数保存视屏到相机胶卷库中,定义如下:

void UISaveVideoAtPathToSavedPhotosAlbum(
    NSString *videoPath,//要保存的视屏路径
    id completionTarget, //保存完成后回调方法所在的对象
    SEL completionSelector, //保存完成后回调的方法
    void *  contextInfo//上下文信息

completionSelector中的回调方法签名是可以由我们自己命名的,但是它必须包含三个参数,而且参数类型是固定的,例如下面的形式:

- (void):(NSString *)videoPath didFinishSavingWithError:(NSError *)error contextInfo:(NSString *)contextInfo

6.3.2 使用AVFoundation框架

为了能够对视屏录制过程进行更加细粒度的控制,也可以使用AVFoundation框架实现视屏录制和保存。使用AVFoundation
框架进行视屏录制,这是一种AVFoundation捕获技术,它可以获得来自设备的输入流,实时采集、捕获和播放视屏技术。

它们涉及的类比较多,这些类主要有:

  • AVCaptureSession,捕获会话,是为了实现从摄像头和麦克风捕获数据,需要使用AVCaptureSession对象协调输入输出数据。
  • AVCaptureDevice,捕获设备,代表输入一个设备,例如摄像头和麦克风。
  • AVCaptureDeviceInput,捕获会话的一个输入数据源
  • AVCaptureOutput,捕获会话的一个输出目标,例如输出的视屏文件和静态图片
  • AVCaptureMovieFileOutput,是AVCaptureOutPut的子类,通过它可以将捕获的数据输出到QuickTime视屏文件(.mov)。
  • AVCaptureVideoPreviewLayer,是CALayer的子类,可以使用它来显示录制的视屏
  • AVCaptureConnection,捕获连接,在一个捕获会话中输入和输出之间的连接。
#import "ViewController.h"

#import <AVFoundation/AVFoundation.h>
// 为了保存媒体文件所需要的
#import <AssetsLibrary/AssetsLibrary.h>

//AVCaptureFileOutputRecordingDelegate 为了录制单个文件时候发生的事件做出反应
@interface ViewController ()
    <AVCaptureFileOutputRecordingDelegate>
{
    BOOL isRecording;
}

@property (weak, nonatomic) IBOutlet UILabel *label;
@property (weak, nonatomic) IBOutlet UIButton *button;

@property (strong, nonatomic) AVCaptureSession *session;
@property (strong, nonatomic) AVCaptureMovieFileOutput *output;

- (IBAction)recordPressed:(id)sender;

@end

@implementation ViewController

- (void)viewDidLoad
{
    [super viewDidLoad];
    self.session =  [[AVCaptureSession alloc] init];
    // 设置捕获的视屏的质量
    self.session.sessionPreset = AVCaptureSessionPresetMedium;
    // 获得默认捕获设备AVCaptureDevice对象
    // AVMediaTypeVideo 设置媒体类型为视屏
    AVCaptureDevice *cameraDevice = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];

    NSError *error = nil;
    // 视屏捕获输入对象
    AVCaptureDeviceInput *camera = [AVCaptureDeviceInput deviceInputWithDevice:cameraDevice error:&error];

    // 获得默认捕获设备AVCaptureDevice对象
    // AVMediaTypeAudio 设置媒体类型为音频
    AVCaptureDevice *micDevice = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeAudio];
    // 音频捕获输入对象
    AVCaptureDeviceInput *mic = [AVCaptureDeviceInput deviceInputWithDevice:micDevice error:&error];

    // 判断捕获会话中是否能够添加AVCaptureMovieFileOutput类型的视频捕获输出对象
    if (error || !camera || !mic) {
        NSLog(@"Input Error");
    } else {
        [self.session addInput:camera];
        [self.session addInput:mic];
    }

    // 视频捕获输出对象 默认是.mov文件
    self.output = [[AVCaptureMovieFileOutput alloc] init];

    if ([self.session canAddOutput:self.output]) {
        [self.session addOutput:self.output];
    }

    AVCaptureVideoPreviewLayer *previewLayer = [AVCaptureVideoPreviewLayer layerWithSession:self.session];
    previewLayer.frame = CGRectMake(0,0, self.view.frame.size.width, self.view.frame.size.height);
    // 将图层插入到最底层,使得预览视图层布遮挡界面上方的绿色标签
    [self.view.layer insertSublayer:previewLayer atIndex:0];


    [self.session startRunning];
    isRecording = NO;
    self.label.text = @"";

}

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


- (void)viewWillAppear:(BOOL)animated
{
    [super viewWillAppear:animated];
    if (![self.session isRunning])
    {
        [self.session startRunning];
    }
}

- (void)viewWillDisappear:(BOOL)animated
{
    [super viewWillDisappear:animated];
    if ([self.session isRunning])
    {
        [self.session stopRunning];
    }
}


- (IBAction)recordPressed:(id)sender {
    if (!isRecording)
    {
        [self.button setTitle:@"停止" forState:UIControlStateNormal];
        self.label.text = @"录制中...";
        isRecording = YES;
        NSURL *fileURL = [self fileURL];
        [self.output startRecordingToOutputFileURL:fileURL recordingDelegate:self];
    }
    else
    {
        [self.button setTitle:@"录制" forState:UIControlStateNormal];
        self.label.text = @"停止";
        [self.output stopRecording];
        isRecording = NO;
    }
}

// 为了管理视屏录制过程中生成的文件,我们自定义了fileURL方法,在该方法中获得应用程序沙箱目录中tmp目录,这个目录是临时目录。fileURL方法的代码如下:
- (NSURL *) fileURL
{
    NSString *outputPath = [[NSString alloc] initWithFormat:@"%@%@", NSTemporaryDirectory(), @"movie.mov"];
    NSURL *outputURL = [[NSURL alloc] initFileURLWithPath:outputPath];

    NSFileManager *manager = [[NSFileManager alloc] init];
    if ([manager fileExistsAtPath:outputPath])
    {
        [manager removeItemAtPath:outputPath error:nil];
    }

    return outputURL;
}

#pragma  mark-- AVCaptureFileOutputRecordingDelegate委托协议实现方法
// 在所有缓存中的数据写入到文件中时调用了如下方法
- (void)captureOutput:(AVCaptureFileOutput *)captureOutput
    didFinishRecordingToOutputFileAtURL:(NSURL *)outputFileURL
    fromConnections:(NSArray *)connections error:(NSError *)error
{

    if (error == nil) {
        ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init];

        [library writeVideoAtPathToSavedPhotosAlbum:outputFileURL
                                    completionBlock:^(NSURL *assetURL, NSError *error)
         {
             if (error)
             {
                 NSLog(@"写入错误。") ;
             }

         }];
    }

}

@end

6.4 编辑视屏

有的时候,我们需要为录制的视屏进行编辑,这里所谓编辑就只是对视屏的裁剪,这也是可以理解的,毕竟iOS设备是无法与专业的视屏处理软件和高配置的PC相比。编辑视屏可以使用UIVideoEditorController类和它的委托协议UIVideoEditorControllerDelegate,其中UIVideoEditorController类中的主要方法和属性如下:

  • +(BOOL)canEditVideoAtPath:(NSString *)videoPath,判断是否指定的视屏文件可以被编辑。
  • videoQuality,视屏的质量
  • videoPath,视屏文件的路径
  • videoMaximumDuration,视屏的最长时间

UIVideoEditorControllerDelegate 协议中定义的方法,说明如下:

  • videoEditorController:didSaveEditedVideoToPath:在视屏编辑界面中单击了Save按钮
  • videoEditorControllerDidCancel:在视屏编辑界面中单击了Cancel按钮
  • videoEditorController:didFailWithController:视屏编辑失败。
#import "ViewController.h"

@interface ViewController ()
<UIVideoEditorControllerDelegate,UINavigationControllerDelegate>

- (IBAction)editButtonPress:(id)sender;

@end

@implementation ViewController

- (void)viewDidLoad
{
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
}

- (void)didReceiveMemoryWarning
{
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

- (IBAction)editButtonPress:(id)sender {

    NSBundle *bundle = [NSBundle mainBundle];
    NSString *moviePath = [bundle pathForResource:@"YY"
                                           ofType:@"mp4"];

    //判断设备是否支持编辑视频
    if ([UIVideoEditorController canEditVideoAtPath:moviePath]){


        UIVideoEditorController *videoEditor =
        [[UIVideoEditorController alloc] init];

        videoEditor.delegate = self;
        videoEditor.videoPath = moviePath;

        [self presentViewController:videoEditor animated:YES completion:NULL];


    } else {
        NSLog(@"不能编辑这个视频");
    }

}


- (void)videoEditorController:(UIVideoEditorController *)editor
     didSaveEditedVideoToPath:(NSString *)editedVideoPath{

    [editor dismissViewControllerAnimated:YES completion:NULL];


    if ( UIVideoAtPathIsCompatibleWithSavedPhotosAlbum(editedVideoPath) ) {
        UISaveVideoAtPathToSavedPhotosAlbum(editedVideoPath, self,
                                            @selector(video:didFinishSavingWithError:contextInfo:),
                                            (__bridge void *)(editedVideoPath));
    }
}

- (void)videoEditorController:(UIVideoEditorController *)editor
             didFailWithError:(NSError *)error{
    NSLog(@"编辑视频出错");
    NSLog(@"Video editor error occurred = %@", error);
    [editor dismissViewControllerAnimated:YES completion:NULL];
}

- (void)videoEditorControllerDidCancel:(UIVideoEditorController *)editor{
    NSLog(@"视频编辑取消");
    [editor dismissViewControllerAnimated:YES completion:NULL];
}


- (void)video:(NSString *)videoPath
didFinishSavingWithError:(NSError *)error
  contextInfo:(NSString *)contextInfo {

    NSString *title; NSString *message;
    if (!error) {
        title = @"视频保存";
        message = @"视频已经保存到设备的相机胶卷中";
    } else {
        title =  @"视频失败";
        message = [error description];
    }
    UIAlertView *alert = [[UIAlertView alloc] initWithTitle:title
                                                    message:message
                                                   delegate:nil
                                          cancelButtonTitle:@"OK"
                                          otherButtonTitles:nil];
    [alert show];
}

@end
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值