视屏多媒体文件主要是存放视屏数据信息,视屏数据量要远远大于音频数据文件,而且视屏编码和解码算法非常复杂,因此早起的计算机由于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