音效

  • 1.iOS中音乐可以暂停,音效不能操作;而且音效可以立即播放,加载到本地,而音乐需要缓冲,边下边播.在iOS中只有小于30s的音乐才可以使用音效的方式进行播放.
  • 2.播放音效的实现思路:初始化soundID -> 获取音频文件的本地路径(NSURL类型) ->创建soundID -> 进行播放.
音效播放的优化:以键值对的形式对音频文件和对应的soundID进行缓存,
  获取之前先判断对应的soundID是否已经在,存在的话直接在内存中取,不存在才创建:
  SystemSoundID systemSoundID;
  NSURL *url = [[NSBundle mainBundle] URLForResource:@"buyao.caf" withExtension:nil];
  AudioServicesCreateSystemSoundID((__bridge CFURLRef _Nonnull)(url), &systemSoundID);
  AudioServicesPlaySystemSound(systemSoundID);

  在接收到内存警告的情况下:释放soundID对应的内存地址,清空内存缓存.
  //释放soundID对应的内存地址
  for (NSNumber *soundIDNum in self.soundIDCache.allValues) {
    
     AudioServicesDisposeSystemSoundID([soundIDNum unsignedIntValue]);
  }
  • 3.框架:AVFoundation.
  • 4.关键类:SystemSoundID,每个音频文件使用音效方式处理时,会完整的添加到内存中,并被赋予一个声音ID.
  • 5.C的对象和OC对象的转化中内存管理:
__bridge 用于Foundation中的类和CoreFoundation中的类相互转换,
  并且内存管理所有权不会改变。
  
  __bridge_retained 只能用于Foundation对象转CoreFoundation引用,
  而且内存管理的所有权会移交给引CoreFoundation的引用(注意手动内存管理)。
  
  __bridge_transfer 只能用于CoreFoundation引用转Foundation对象,
  而且内存管理的所有权会移交给引Foundation(会自动内存管理,不需要手动释放)。

音乐

  • 1.框架:AVFoundation
  • 2.关键类:AVAudioPlayer (只能播放本地音乐).
对象方法创建:  _player = [[AVAudioPlayer alloc] initWithContentsOfURL:url error:nil];
  • 3.音乐的播放.暂停和停止的实现:根据音频文件路径初始化播放器 -> 进行缓冲
[self.player prepareToPlay];
  • 4.注意点:停止播放是音乐停止缓冲,但播放进度并不会清零.暂停音乐是暂时停止播放,但缓冲继续.音乐播放,隐式缓冲.
关键方法: 
  -(void)play;//播放
  -(void)prepareToPlay;//缓存播放
  -(void)pause;//暂停
  -(void)stop;//停止

录音

  • 1.录音功能的实现:
生成录音的存放路径(沙盒路径,拼接文件名:stringByAppendingPathComponent:@"音频名.caf") -> 设置采样率和位深 -> 对象方法创建录音机对象
  • 2.框架:AVFoundation.

关键类:AVFoundationRecorder.
关键方法:
-(void)record;//录制
-(void)pause;//暂停
-(void)stop;//停止

  • 3.设置采样率和位深:
NSDictionary *settings = @{AVSampleRateKey:@44100,AVLinearPCMBitDepthKey:@16};
  • 4.注意点:

真机进行录音时,需要请求用户授权.在info.plist进行麦克风的授权说明.

  • 5.检测录音音量变化(&静默2s停止录音)
    //开启检测音量变化
    self.recorder.meteringEnabled = YES;
    //开启定时器
    [self.displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
- (void)updateMeters{
	//更新音量变化
	[self.recorder updateMeters];
	//获取瞬时音量
	float power = [self.recorder peakPowerForChannel:0];
	NSLog(@"%f", power);
	//静默两秒后就停止录音
	if (power < -30) {
  	  //累计静默计数
    	self.muteCount++;
    	if (self.muteCount >= 120) { //静默两秒钟
      	  //停止录音
        	[self.recorder stop];
        	//停止定时器
        	[self.displayLink invalidate];
    	}  
	} else {
    //有声音,就重新计数
    self.muteCount = 0;
	}
  }

视频

  • 1.播放视频的三个框架
  • AVFoundation:提供了相对底层的API,不带界面效果.
  • MediaPlayer:对AVFoundation进行了封装,提供了较为简便的播放器使用.
  • AVKit:ios9推出,集AVFoundation和MediaPlayer之大成.
  • 2.播放器的创建
  • 1.MPMoviewPlayerViewController(带界面的视频播放器):

获取视频文件的url路径 ->对象方法创建视频播放控制器 ->modal展示.

  • 2.MPMoviewPlayerController(不带界面的视频播放器):

获取视频文件路径 ->创建控制器对象 ->将播放器的视图添加到要展示的界面上 ->[控制器对象 play];

  • 3.AVPlayer,偏底层,既可以播放音频又可以播放视频,播放视频需要添加layer到父视图上.
//创建资源对象  获取文件信息
  AVAsset *asset = [AVAsset assetWithURL:[NSURL URLWithString:@"http://baobab.wdjcdn.com/14676170652191(23).mp4"]];
  //创建播放项目  曲目
  AVPlayerItem *item = [AVPlayerItem playerItemWithAsset:asset];
  //AVPlayer既可以播音频也可以播视频,可以本地也可以播网络
  self.avPlayer = [[AVPlayer alloc] initWithPlayerItem:item];
  //设置视图layer
  AVPlayerLayer *layer = [AVPlayerLayer playerLayerWithPlayer:self.avPlayer];
  [self.view.layer addSublayer:layer];
  layer.frame = [UIScreen mainScreen].bounds;
  //播放
  [self.avPlayer play];
  • 4.AVPlayerViewController
//带界面的播放器
  AVPlayerViewController *playerVc = [[AVPlayerViewController alloc] init];
  //设置播放器
  //创建资源对象  获取文件信息
  AVAsset *asset = [AVAsset assetWithURL:[NSURL URLWithString:@"http://baobab.wdjcdn.com/14676170652191(23).mp4"]];
  //创建播放项目  曲目
  AVPlayerItem *item = [AVPlayerItem playerItemWithAsset:asset];
  AVPlayer *avPlayer = [AVPlayer playerWithPlayerItem:item];
  playerVc.player = avPlayer;
  //进行modal展示
  [self presentViewController:playerVc animated:YES completion:nil];
  [avPlayer play];
  • 3.特殊功能:
    1> 横屏全屏播放(视图旋转90度,frame设置为屏幕大小)
[UIView animateWithDuration:0.25 animations:^{
  	self.playerController.view.transform = CGAffineTransformRotate(self.playerController.view.transform, M_PI_2);
  	self.playerController.view.frame = [UIScreen mainScreen].bounds;
  }];

2> 获取全屏的Down事件(viewDidLoad中用通知监听退出全屏事件) 退出全屏&暂停时,说明触发了Down事件

//监听通知  退出全屏时
  [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didExitFullScreen) name:MPMoviePlayerDidExitFullscreenNotification object:nil];

3> 已经退出全屏后调用

- (void)didExitFullScreen{

    //判断播放状态
  	if (self.playerController.playbackState == MPMoviePlaybackStatePaused) {
  	NSLog(@"点击Down");
  	//移除视图
  	[self.playerController.view removeFromSuperview];
  	}
  }

视频截图(本质获取媒体文件中需求的关键帧图片)

  • 1.实现思路
-> 根据文件路径,创建视频文件资源(AVAsset) 
  -> 创建媒体文件图片生成器(AVAssetImageGenerator) 
  -> 获取当前播放时间 
  -> 生成图片:[图片生成器对象 generateCGImageAsynchronouslyForTimes:...];
  • 2.关键类:
AVAsset(资源)
  媒体文件图片生成器(AVAssetImageGenerator)
  • 3.关键方法:
创建媒体资源:AVAsset的类方法创建
  创建媒体文件图片生成器对象: AVAssetImageGenerator(类方法创建).
  获取当前媒体文件的播放时间:CMTime time  = self.avplayer.currentTime.
  生成图片:[媒体文件图片生成器对象 generateCGImagesAsynchronouslyForTimes...];
  //死锁: 在某个线程中嵌套该线程的同步任务才会死锁

视频录制

  • 1.框架:AVFoundation.
  • 2.关键类:
AVCaptureDeviceInput(摄入设备)
  AVCaptureMoviewFileOutput(输出设备)
  AVCaptureSession(采集会话)
  AVCaptureVideoPreviewLayer(预览视图)
  • 3.视频录制的实现思路:
-> 输入设备(音频输入麦克风,视频输入摄像头)
  -> 输出设备(视频文件) 
  ->管理输入设备和输出设备的会话 
  ->预览视图 
  ->开始录制 
  ->录制视频的保存路径,输出设备开始录制 
  ->停止录制.

1>输入音频设备:

AVCaptureDevice *video = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];

2>输入视频设备:

self.videoInput = [AVCaptureDeviceInput deviceInputWithDevice:video error:nil];

3>输出设备:

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

4>会话:

self.session = [[AVCaptureSession alloc] init];
  //加入输入设备
  if ([self.session canAddInput:self.videoInput]) {
	[self.session addInput:self.videoInput];
  }
  if ([self.session canAddInput:self.audioInput]) {
	[self.session addInput:self.audioInput];
  }
  //加入输出设备
  if ([self.session canAddOutput:self.output]) {
	[self.session addOutput:self.output];
  }

5>预览视图:

self.previewLayer = [AVCaptureVideoPreviewLayer layerWithSession:self.session];

6>开始采集:

[self.session startRunning];

7>开始录制:

NSString *path = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).lastObject stringByAppendingPathComponent:@"录制.mp4"];
  NSURL *url = [NSURL fileURLWithPath:path];
  [self.output startRecordingToOutputFileURL:url recordingDelegate:self];

8>停止录制(三步:停止录制 停止采集和移除预览视图)

[self.output stopRecording];
  [self.session stopRunning];
  [self.previewLayer removeFromSuperlayer];
  • 4.在访问麦克风和摄像头时应请求用户授权,在info,plist文件中对麦克风和摄像头进行描述.

视频转制(输出相册中的视频并压缩)

  • 1.实现思路:
-> 创建UIImagePickerController对象 
  ->设置代理,设置资源类型(获取图片)和媒体类型(获取视频) 
  ->modal展示 
  ->在代理方法中获取路径获取媒体文件 
  ->创建媒体转制会话 ->设置输出的路径 
  ->设置可支持的输出格式 
  ->输出视频 
  ->销毁UIImagePickerController对象
  • 2.步骤:
    1>创建控制器:
UIImagePickerController *imgVc = [[UIImagePickerController alloc] init];

2>设置代理:

imgVc.delegate = self;

3>设置类型:

imgVc.sourceType = UIImagePickerControllerSourceTypePhotoLibrary;

4>获取相册中视频 需要设置媒体类型:

imgVc.mediaTypes = [UIImagePickerController availableMediaTypesForSourceType:UIImagePickerControllerSourceTypePhotoLibrary];

5>modal展示:

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

7>转制视频:

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

	NSLog(@"%@", info);
	//获取媒体文件
	NSURL *url = info[UIImagePickerControllerMediaURL];
	AVAsset *asset = [AVAsset assetWithURL:url];
	//创建媒体转制会话
	AVAssetExportSession *session = [AVAssetExportSession exportSessionWithAsset:asset presetName:AVAssetExportPresetLowQuality];
	//设置转出的路径
	NSString *filePath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).lastObject stringByAppendingPathComponent:@"转出的视频.mp4"];
	session.outputURL = [NSURL fileURLWithPath:filePath];
	//获取支持的转出格式
	[session determineCompatibleFileTypesWithCompletionHandler:^(NSArray<NSString *> * _Nonnull compatibleFileTypes) {

	NSLog(@"%@", compatibleFileTypes);
	}];
	//设置转出的格式 转出格式必须支持
	session.outputFileType = @"public.mpeg-4";
	//转出视频
	[session exportAsynchronouslyWithCompletionHandler:^{

	NSLog(@"完成转出");
	//销毁控制器
	[picker dismissViewControllerAnimated:YES completion:nil];
	}];
  }