多媒体编程的实战

一、iOS的音频播放方法: 

1、System Sound Services

2、AVAudioPlayer 类

3、Audio Queue Services

  可以完全实现对声音的控制。例如,可以在声音数据从文件读到内存缓冲区后对声音进行一定处理再进行播放,从而实现对音频的快速/慢速 播放的功能,但是实现起来相对复杂许多。

4、OpenAL

  OpenAL 是一套跨平台的开源的音频处理接口,与图形处理的 OpenGL 类似,它为音频播放提供了一套更加优化的方案。它最适合开发游戏的音效,用法也与其他平台下相同。

 

  

二、集成音效

 

1、框架 AVFoundation

2、核心:System Sound Services 

(1)System Sound Services 是最底层也是最简单的声音播放服务,调用 AudioServicesPlaySystemSound 这个方法就可以播放一些简单的音频文件,使用此方法只适合播放一些很小的提示或者警告音,因为它有很多限制:

 

    <1>声音长度要小于 30

  <2> In linear PCM 或者 IMA4 (IMA/ADPCM) 格式的

  <3>打包成 .caf, .aif, 或者 .wav 的文件

  <4> 不能控制播放的进度

  <5>调用方法后立即播放声音

  <6>没有循环播放和立体声控制

 

(2)可以调用系统的震动功能,方法也很简单:AudioServicesPlayAlertSound

(3)从官方文档中发现可以通过 AudioServicesAddSystemSoundCompletion 方法为音频播放添加 CallBack 函数,有了 CallBack 函数我们可以解决不少问题,比如可以克服 System Sound Services 本身不支持循环播放的问题。

  

3、音效的播放示例

 (1)获取资源的url

   <1>  NSString *path = [[NSBundle mainBundle] pathForResource:fileName ofType:nil];

     <2> NSURL *url = [NSURL fileURLWithPath:path];

 

(2)定义soundID

       SystemSoundID soundID;

 

(3)创建一个soundID(注意桥接和地址传递)

     AudioServicesCreateSystemSoundID((__bridge CFURLRef _Nonnull)(url), &soundID);

 

(4)根据soundID 播放

   <1> 不但播放,而且震动

     AudioServicesPlayAlertSound(soundID);

   <2>只播放

        AudioServicesPlaySystemSound(soundID);

 

(5)清除内存

   AudioServicesDisposeSystemSoundID(soundID);

 

 

三、音乐的集成

1、框架:AVFoundation

2、核心:AVAudioPlayer 类

(1)AVAudioPlayer 是一个高级的播放器

(2)它支持广泛的音频格式,主要是以下这些格式:

    <1>  AAC

  <2> AMR(AdaptiveMulti-Rate, aformatforspeech)

  <3> ALAC(AppleLossless)

  <4> iLBC(internetLowBitrateCodec, anotherformatforspeech)

  <5> IMA4(IMA/ADPCM)

  <6> linearPCM(uncompressed)

  <7> µ-lawanda-law

  <8> MP3(MPEG-1audiolayer3

 

(3)AVAudioPlayer 可以播放任意长度的音频文件、支持循环播放、可以同步播放多个音频文件、控制播放进度以及从音频文件的任意一点开始播放等。  

(4)AVAudioPlayer的numberOfLoops属性设为负数,音频文件就会一直循环播放直到调用 stop 方法。

(5)play、pause、stop方法控制音频的播放

(6)虽然 AVAudioPlayer 可以播放很多格式,但是我们在实际开发过程中还是最好使用一些没有压缩的格式,比如 WAVE 文件,这样可以减少系统处理单元的资源占用,以便更好的完成程序的其他功能。

(7)在使用 AVAudioPlayer 连续播放 mp3 这类经过压缩的音频文件时,在连接处可能出现一定的间隔时间。 

(8)一些其他属性

   <1>音量 0.0-1.0 之间 

    player.volume =0.8;

  <2>循环次数,默认只播放一次

    player.numberOfLoops =3; 

  <3>播放位置,可以指定从任意位置开始播放

    player.currentTime =15.0;

  <4>声道数

    NSUInteger channels = player.numberOfChannels;//只读属性

  <5>持续时间

    NSTimeInterval duration = player.duration;//获取持续时间

  <6>仪表计数

    player.meteringEnabled =YES;//开启仪表计数功能  

   [playerupdateMeters];//更新仪表计数

 

 

3、音乐的播放示例

 (1)获取资源地址

    NSURL *url = [[NSBundle mainBundle] URLForResource:@"童话.mp3" withExtension:nil];

    NSError *error = nil;

(2)创建音乐播放的对象

    AVAudioPlayer *player = [[AVAudioPlayer alloc] initWithContentsOfURL:url error:&error];

(3)无限循环播放

     self.player.numberOfLoops = -1;

(4)准备播放,分配播放所需的资源,并将其加入内部播放队列(可以省略,在播放的方法中集成了)

   [player prepareToPlay];

 

(5)播放的操控

 

  <1> 播放

      [self.player play];

  <2> 暂停

      [self.player pause];

  <3> 停止(和暂停效果相同)

      [self.player stop]; 

 

 

四、录音的实现

  

1、框架:AVFoundation 

2、核心:AVAudioRecorder 类 

(1)AVAudioRecorder创建录音机时除了指定路径外还必须指定录音设置信息,因为录音机必须知道录音文件的格式、采样率、通道数、每个采样点的位数等信息,通常只需要几个常用设置。

(2)常用属性

   <1> 当前录音的通道

   channelAssignments

  <2>是否启用录音测量,如果启用录音测量可以获得录音分贝等数据信息

  meteringEnabled

  <3>录音时长,只读,注意仅仅在录音状态可用

  currentTime

  <4> 录音文件地址

  url

  <5>录音文件设置,只读

  settings

 

 

3、录音实现的步骤

 

(1)获取沙盒路径,用于保存

    NSString *path = [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject] stringByAppendingPathComponent:@"luzhi.wav"];

(2)创建录音对象

      self.recorder = [[AVAudioRecorder alloc] initWithURL:[NSURL fileURLWithPath:path] settings:nil error:&error];

(3)准备录音

    [self.recorder prepareToRecord];

(4)开启表盘,监听分贝数变化

    self.recorder.meteringEnabled = YES;

(5)录音

    [self.recorder record];

(6)暂停

    [self.recorder pause];

(7)停止

    [self.recorder stop];

(8)时时监听

    [self.recorder updateMeters];

(9)获取分贝数(-100 ~ 0)

   float power = [self.recorder averagePowerForChannel:0];

 

4、关于settings(录音格式、编码等设置)的补充

 

  NSMutableDictionary *dicM=[NSMutableDictionary dictionary];

    //设置录音格式

    [dicM setObject:@(kAudioFormatLinearPCM) forKey:AVFormatIDKey];

    //设置录音采样率,8000是电话采样率,对于一般录音已经够了

    [dicM setObject:@(8000) forKey:AVSampleRateKey];

    //设置通道,这里采用单声道

    [dicM setObject:@(1) forKey:AVNumberOfChannelsKey];

    //每个采样点位数,分为8、16、24、32

    [dicM setObject:@(8) forKey:AVLinearPCMBitDepthKey];

    //是否使用浮点数采样

    [dicM setObject:@(YES) forKey:AVLinearPCMIsFloatKey];

    //....其他设置等

 

 

 

五、视频播放 iOS8.0

1、框架  MediaPlayer

2、核心:MPMoviePlayerController

3、配置示例

(1)有视图的播放

    <1> 获取视频路径

    NSString *path = [[NSBundle mainBundle] pathForResource:@"Alizee_La_Isla_Bonita副本.mp4" ofType:nil];

   <2>创建播放视频的对象,并且实例化

    MPMoviePlayerViewController *playerVC = [[MPMoviePlayerViewController alloc] initWithContentURL:[NSURL fileURLWithPath:path]];

   <3>推出视频控制器

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

 

(2)无视图的播放

 <1>获取视频路径

    NSString *path = [[NSBundle mainBundle] pathForResource:@"Alizee_La_Isla_Bonita副本.mp4" ofType:nil] 

   <2>创建对象,并且实例化

    MPMoviePlayerController *playerC = [[MPMoviePlayerController alloc] initWithContentURL:[NSURL fileURLWithPath:path]];

   <3>准备播放

    [playerC prepareToPlay];

    <4> 设置控制样式

    /*

     MPMovieControlStyleNone,       // No controls

     MPMovieControlStyleEmbedded,   // 镶嵌

     MPMovieControlStyleFullscreen, // 全屏

     */

    playerC.controlStyle = MPMovieControlStyleFullscreen;

  <5> 设置控制器视图大小,以及添加视图

    playerC.view.frame = CGRectMake(0, 0, 300, 300);

    // self.view.bounds;

    [self.view addSubview:playerC.view];

    <6> 播放

    [playerC play];

 

 

 

六、视频播放 iOS9.0

 

1、框架: AVFoundation、AVKit

2、核心:AVPlayerViewController

3、配置示例

  <1> 创建播放对象

    AVPlayerViewController *playerViewController = [[AVPlayerViewController alloc] init];

     <2> 获取视频路径

    NSString *path = [[NSBundle mainBundle] pathForResource:@"Alizee_La_Isla_Bonita副本.mp4" ofType:nil];

   <3> 设置属性

    playerViewController.player = [[AVPlayer alloc] initWithURL:[NSURL fileURLWithPath:path]];

     <4> 播放

    [playerViewController.player play];

  <5> 显示播放页面,两种方式

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

    // 或者这样:

    playerViewController.view.frame = CGRectMake(0, 0, 400, 400);

    [self.view addSubview:playerViewController.view];

 

 

 七、视频截图(缩略图)

 

1、框架:AVFoundation

2、核心:AVAssetImageGenerator 

3、配置示例,由于帧与帧之间添加了过渡帧,可能造成截取不准确

(1)获取资源的路径

    NSString *path = [[NSBundle mainBundle] pathForResource:@"Alizee_La_Isla_Bonita.mp4" ofType:nil];

    NSURL *url = [NSURL fileURLWithPath:path];

(2)包装

    AVAsset *asset = [AVAsset assetWithURL:url];

(3)创建截取器

  AVAssetImageGenerator *generator = [AVAssetImageGenerator assetImageGeneratorWithAsset:asset];

(4)创建CMTime对象(结构体)

   <1> CMTime time = CMTimeMake(23, 1);    

    // <#int64_t value#>:    截取的时间

      // <#int32_t timescale#>:每秒播放多少帧

     <2> 包装

    NSValue *value = [NSValue valueWithCMTime:time];

(5)截取 

      [generator generateCGImagesAsynchronouslyForTimes:@[value] completionHandler:^(CMTime requestedTime, CGImageRef  _Nullable image, CMTime actualTime, AVAssetImageGeneratorResult result, NSError * _Nullable error) {

        // 如果更新UI,一定记住,要在主线程更新

        dispatch_sync(dispatch_get_main_queue(), ^{

            self.picImageView.image = [UIImage imageWithCGImage:image];

        });

    }];

 

 

 

八、视频录制(AVFoundation)

 

1、框架:AVFoundation

2、配置示例

(1)创建视频录制的对象,并且初始化

    <1>生成画面

    AVCaptureDevice *videoDevice = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];

    self.inputVideo = [[AVCaptureDeviceInput alloc] initWithDevice:videoDevice error:nil];  

    <2>生成声音

    AVCaptureDevice *AudioDevice = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeAudio];

    self.inputAudio = [[AVCaptureDeviceInput alloc] initWithDevice:AudioDevice error:nil];   

    <3>生成加工厂

    self.session = [[AVCaptureSession alloc] init];

    <4>生成导出(输出对象)

    self.output = [[AVCaptureMovieFileOutput alloc] init];

    

(2)将画面、声音、输出对象添加到加工厂(会话)

    if ([self.session canAddInput:self.inputAudio]) {

        [self.session addInput:self.inputAudio];

    }

    if ([self.session canAddInput:self.inputVideo]) {

        [self.session addInput:self.inputVideo];

    }

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

        [self.session addOutput:self.output];

    }

    

(3) 添加预览layer层(用插入图层的方式)

     <1> AVCaptureVideoPreviewLayer *preLayer = [AVCaptureVideoPreviewLayer layerWithSession:self.session];          <2>preLayer.frame = self.view.bounds;  

   <3>[self.view.layer insertSublayer:preLayer atIndex:0];

 

(4)开启加工厂(开启捕获)

    [self.session startRunning];

 

(5)配置存储路径

    NSString *path = [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject] stringByAppendingPathComponent:@"录制的视频.MP4"];

 

(6)开启录制

     [self.output startRecordingToOutputFileURL:[NSURL fileURLWithPath:path] recordingDelegate:self];

 

(7)暂停录制

    if ([self.output isRecording]) {

    [self.output stopRecording];

    } 

 

九、视频录制(UIImagePickerController)

 

1、框架:UIImagePickerController

2、配置示例(包括拍照功能)

 

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

@interface ViewController ()<UIImagePickerControllerDelegate,UINavigationControllerDelegate>
@property (assign,nonatomic) int isVideo;//是否录制视频,如果为1表示录制视频,0代表拍照
@property (strong,nonatomic) UIImagePickerController *imagePicker;
@property (weak, nonatomic) IBOutlet UIImageView *photo;//照片展示视图
@property (strong ,nonatomic) AVPlayer *player;//播放器,用于录制完视频后播放视频

@end

@implementation ViewController

#pragma mark - 控制器视图事件
- (void)viewDidLoad {
    [super viewDidLoad];
    //通过这里设置当前程序是拍照还是录制视频
    _isVideo=YES;
}

#pragma mark - UI事件
//点击拍照按钮
- (IBAction)takeClick:(UIButton *)sender {
    [self presentViewController:self.imagePicker animated:YES completion:nil];
}

#pragma mark - UIImagePickerController代理方法
//完成
-(void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info{
    NSString *mediaType=[info objectForKey:UIImagePickerControllerMediaType];
    if ([mediaType isEqualToString:(NSString *)kUTTypeImage]) {//如果是拍照
        UIImage *image;
        //如果允许编辑则获得编辑后的照片,否则获取原始照片
        if (self.imagePicker.allowsEditing) {
            image=[info objectForKey:UIImagePickerControllerEditedImage];//获取编辑后的照片
        }else{
            image=[info objectForKey:UIImagePickerControllerOriginalImage];//获取原始照片
        }
        [self.photo setImage:image];//显示照片
        UIImageWriteToSavedPhotosAlbum(image, nil, nil, nil);//保存到相簿
    }else if([mediaType isEqualToString:(NSString *)kUTTypeMovie]){//如果是录制视频
        NSLog(@"video...");
        NSURL *url=[info objectForKey:UIImagePickerControllerMediaURL];//视频路径
        NSString *urlStr=[url path];
        if (UIVideoAtPathIsCompatibleWithSavedPhotosAlbum(urlStr)) {
            //保存视频到相簿,注意也可以使用ALAssetsLibrary来保存
            UISaveVideoAtPathToSavedPhotosAlbum(urlStr, self, @selector(video:didFinishSavingWithError:contextInfo:), nil);//保存视频到相簿
        }
        
    }

    [self dismissViewControllerAnimated:YES completion:nil];
}
-(void)imagePickerControllerDidCancel:(UIImagePickerController *)picker{
    NSLog(@"取消");
}

#pragma mark - 私有方法
-(UIImagePickerController *)imagePicker{
    if (!_imagePicker) {
        _imagePicker=[[UIImagePickerController alloc]init];
        _imagePicker.sourceType=UIImagePickerControllerSourceTypeCamera;//设置image picker的来源,这里设置为摄像头
        _imagePicker.cameraDevice=UIImagePickerControllerCameraDeviceRear;//设置使用哪个摄像头,这里设置为后置摄像头
        if (self.isVideo) {
            _imagePicker.mediaTypes=@[(NSString *)kUTTypeMovie];
            _imagePicker.videoQuality=UIImagePickerControllerQualityTypeIFrame1280x720;
            _imagePicker.cameraCaptureMode=UIImagePickerControllerCameraCaptureModeVideo;//设置摄像头模式(拍照,录制视频)
            
        }else{
            _imagePicker.cameraCaptureMode=UIImagePickerControllerCameraCaptureModePhoto;
        }
        _imagePicker.allowsEditing=YES;//允许编辑
        _imagePicker.delegate=self;//设置代理,检测操作
    }
    return _imagePicker;
}

//视频保存后的回调
- (void)video:(NSString *)videoPath didFinishSavingWithError:(NSError *)error contextInfo:(void *)contextInfo{
    if (error) {
        NSLog(@"保存视频过程中发生错误,错误信息:%@",error.localizedDescription);
    }else{
        NSLog(@"视频保存成功.");
        //录制完之后自动播放
        NSURL *url=[NSURL fileURLWithPath:videoPath];
        _player=[AVPlayer playerWithURL:url];
        AVPlayerLayer *playerLayer=[AVPlayerLayer playerLayerWithPlayer:_player];
        playerLayer.frame=self.photo.frame;
        [self.photo.layer addSublayer:playerLayer];
        [_player play];
        
    }
}
@end

 

 

 

十、视频压缩

 

1、框架:AVFoundation

2、核心:UIImagePickerController,AVAssetExportSession

3、配置示例

(1)判断相册是否可用

   [UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeSavedPhotosAlbum]

(2)获取视频

   <1> UIImagePickerController *picker = [[UIImagePickerController alloc] init];

   <2> 指定资源类型    

      picker.sourceType = UIImagePickerControllerSourceTypeSavedPhotosAlbum;

   <3>  指定媒体类型,这样才可以在相册中看到视频类型的文件

      picker.mediaTypes = [UIImagePickerController availableMediaTypesForSourceType:UIImagePickerControllerSourceTypeSavedPhotosAlbum];

     <4> 设置代理

      picker.delegate = self;

     <5> 跳转控制器

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

(3)重写picker的代理

   <1> 方法名

    - (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary<NSString *,id> *)info;

   <2> 获得url

      NSURL *url = info[UIImagePickerControllerMediaURL];

       <3> 包装

      AVAsset *asset = [AVAsset assetWithURL:url];

       <4> 创建压缩加工厂(压缩的质量)

      AVAssetExportSession *session = [AVAssetExportSession exportSessionWithAsset:asset presetName:AVAssetExportPresetLowQuality];

    <5> 设置工厂压缩后的输出路径

    NSString *path = [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject] stringByAppendingPathComponent:@“压缩过的文件.mp4"];

       session.outputURL = [NSURL fileURLWithPath:path];

    <6> 设置工厂压缩后的文件类型     

      session.outputFileType = @"public.mpeg-4";

    <7> 开始压缩  

      [session exportAsynchronouslyWithCompletionHandler:^{

          NSLog(@"finish");

      }];

    

 

posted @ 2016-04-30 16:14  执着的怪味豆  阅读(275)  评论(0编辑  收藏  举报