音頻播放
在iOS中音頻播放從形式上可以分為音效播放和音樂播放。前者主要指的是一些短音頻播放,通常作為點綴音頻,對於這類音頻不需要進行進度、循環等控制。後者指的是一些較長的音頻,通常是主音頻,對於這些音頻的播放通常需要進行精確的控制。在iOS中播放兩類音頻分別使用AudioToolbox.framework和AVFoundation.framework兩個框架來完成音效和音樂播放。
一、音效
AudioToolbox.framework是一套基於C語言的框架,使用它來播放音效其本質是將短音頻注冊到系統聲音服務(System Sound Service)。System Sound Service是一種簡單、底層的聲音播放服務,使用此方法只適合播放一些很小的提示或者警告音,還可以調用系統的震動功能,但是它本身也存在著一些限制:
1.音頻播放時間不能超過30s
2.數據必須是PCM或者IMA4格式
3.音頻文件必須打包成.caf、.aif、wav中的一種(不過需要注意的是注意這是官方文檔的說法,實際測試發現部分.mp3也可以播放)
4.不能控制播放的進度
5.調用方法後立即播放聲音
6.沒有循環播放和立體聲控制
二、音樂
如果播放較大的音頻或者要對音頻有精確的控制則System Sound Service可能就很難滿足實際需求了,通常這種情況會選擇使用AVFoundation.framework中的AVAudioPlayer來實現,我們可以把 AVAudioPlayer 看作是一個高級的播放器,它支持廣泛的音頻格式。
AVAudioPlayer 可以播放任意長度的音頻文件、支持循環播放、可以同步播放多個音頻文件、控制播放進度以及從音頻文件的任意一點開始播放等,更高級的功能可以參考 AVAudioPlayer 的文檔 。要使用 AVAudioPlayer 的對象播放文件,你只需為其指定一個音頻文件並設定一個實現了 AVAudioPlayerDelegate 協議的 delegate 對象。
只要將 AVAudioPlayer 的 numberOfLoops 屬性設為負數,音頻文件就會一直循環播放直到調用 stop 方法。
AVAudioPlayer類封裝了播放單個聲音的能力。播放器可以用NSURL或者NSData來初始化,要注意的是NSURL不可以是網絡url而必須是本地文件url,因為AVAudioPlayer不具備播放網絡音頻的能力。因為AVAudioPlayer只能播放一個完整的文件,並不支持流式播放,所以必須是緩沖完才能播放。一個AVAudioPlayer只能播放一個音頻,如果你想混音你可以創建多個AVAudioPlayer實例,每個相當於混音板上的一個軌道,
額外進階
1.設置後台播放模式(兩個步驟缺一不可):
第一步:Required background modes
App plays audio or streams audio/video using AirPlay
第二步:設置AVAudioSession的類型為AVAudioSessionCategoryPlayback並且調用setActive::方法啟動會話
AVAudioSession *audioSession=[AVAudioSession sharedInstance];
[audioSession setCategory:AVAudioSessionCategoryPlayback error:nil];
[audioSession setActive:YES error:nil];
2.處理拔出耳機後暫停功能
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(routeChange:) name:AVAudioSessionRouteChangeNotification object:nil];
NSDictionary *dic=notification.userInfo;
int changeReason= [dic[AVAudioSessionRouteChangeReasonKey] intValue];
if (changeReason==AVAudioSessionRouteChangeReasonOldDeviceUnavailable) {
AVAudioSessionRouteDescription *routeDescription=dic[AVAudioSessionRouteChangePreviousRouteKey];
AVAudioSessionPortDescription *portDescription= [routeDescription.outputs firstObject];
if ([portDescription.portType isEqualToString:@”Headphones”]) {
if ([self.audioPlayer isPlaying])
{
[self.audioPlayer pause];
self.timer.fireDate=[NSDate distantFuture];
}
}
配置iOS9 HTTPS&HTTP
NSAppTransportSecurity
NSAllowsArbitraryLoads
搖一搖demo
#import "ViewController.h"
//短效音頻的框架
#import
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
}
-(void)motionBegan:(UIEventSubtype)motion withEvent:(UIEvent *)event{
NSLog(@"搖動開始");
//短效音頻播放
//創建系統聲音ID
SystemSoundID soundID;
//創建短效音頻 播放器 並且設置播放資源
NSURL * url = [NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"shake" ofType:@"wav"]];
AudioServicesCreateSystemSoundID((__bridge CFURLRef)(url), &soundID);
//播放短效音頻的同時加入震動效果
//kSystemSoundID_Vibrate 指的就是震動效果
AudioServicesPlaySystemSound(kSystemSoundID_Vibrate);
//播放聲音
AudioServicesPlaySystemSound(soundID);
}
-(void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event{
NSLog(@"搖動結束");
//一般寫頁面跳轉
}
-(void)motionCancelled:(UIEventSubtype)motion withEvent:(UIEvent *)event{
NSLog(@"搖動取消");
}
音樂播放方法
#pragma mark - 設置音樂播放器
-(void)createAudioPlayer{
//初始化播放器
//第一種(本地) 用NSURL初始化 這裡的URL指的不是網絡url 而是本地url
NSURL * url = [NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:self.localMusicArray[_currentIndex] ofType:@"mp3"]];
NSLog(@"%@",self.localMusicArray[_currentIndex]);
_audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:url error:nil];
//
// //預播放
// [_audioPlayer prepareToPlay];
// //播放
// [_audioPlayer play];
//第二種(網絡) 用NSData初始化 先緩沖到本地 然後播放本地文件
// _audioPlayer = [[AVAudioPlayer alloc ]initWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:self.urlArray[_currentIndex]]] error:nil];
/**************設 置****************/
//設置代理
_audioPlayer.delegate = self;
//設置音量
_audioPlayer.volume = 0.5; //0.1-1.0之間
//設置播放循環次數,負數表示無限循環,0表示播放一次,正數是幾就播放幾次,默認播放一次
_audioPlayer.numberOfLoops = 0;
//設置開始播放的位置 單位/s
_audioPlayer.currentTime = 0; //可以指定從任意位置播放
//聲道數 只讀屬性
/*
int channals = _audioPlayer.numberOfChannels;
//設置計數 開啟音頻計數
_audioPlayer.meteringEnabled = YES;
[_audioPlayer updateMeters];//更新音頻讀數
//獲取平均電平和峰值電平
for (int i = 0; i < channals; i++) {
//平均電平
float average = [_audioPlayer averagePowerForChannel:i];
//峰值電平
float peak = [_audioPlayer peakPowerForChannel:i];
}
//獲取播放持續時間
NSTimeInterval time = _audioPlayer.duration;
*/
//分配播放資源,並將其添加到內部實行隊列中
[_audioPlayer prepareToPlay];
//[_audioPlayer play];
}
#pragma mark - 代理方法,處理播放過程中的突發狀況
-(void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag{
NSLog(@"播放完成");
//循環播放,單曲循環...
if (flag) {
//音頻文件在正常情況下播放完成
}else{
//說明音頻雖然播放結束了 但是數據解碼錯誤
}
}
-(void)audioPlayerDecodeErrorDidOccur:(AVAudioPlayer *)player error:(NSError *)error{
//數據解碼錯誤
}
//處理音頻播放過程中被中斷的情況,ios8之前必須要實現這兩個代理方法 ios8之後系統自動處理
//播放被中斷 比如突然來電或者用戶home鍵返回
-(void)audioPlayerBeginInterruption:(AVAudioPlayer *)player{
//如果當前正在播放則暫停
if (_audioPlayer.isPlaying) {
[_audioPlayer pause];
}
}
//結束中斷 返回程序
-(void)audioPlayerEndInterruption:(AVAudioPlayer *)player{
[_audioPlayer play];
}
後台播放 開啟線控
#pragma mark - 細節處理 後台播放 和開啟線控
-(void)dealWithDetail{
//設置後台播放,結合修改 plist
//初始化音頻會話 多個app的音頻同時播放管理
AVAudioSession * session = [[AVAudioSession alloc] init];
//設置類型為後台播放類型
[session setCategory:AVAudioSessionCategoryPlayback error:nil];
//開啟後台播放
[session setActive:YES error:nil];
//監聽輸出設備的狀態
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(routeChange:) name:AVAudioSessionRouteChangeNotification object:nil];
}
-(void)routeChange:(NSNotification *)noti{
//獲取監聽內容
NSDictionary * dic = noti.userInfo;
//獲取狀態改變的原因
int changeReason = [dic[AVAudioSessionRouteChangeReasonKey] intValue];
//當狀態改變原因為檢測不到輸出設備
if (changeReason == AVAudioSessionRouteChangeReasonOldDeviceUnavailable) {
//獲取狀態描述
AVAudioSessionRouteDescription * description = dic[AVAudioSessionRouteChangePreviousRouteKey];
//獲取端口描述
AVAudioSessionPortDescription * portDescription = [description.outputs firstObject];
//如果端口是耳機 並且音頻正在播放的話 就暫停播放
if([portDescription.portType isEqualToString:@"HeadPhones"]){
if (_audioPlayer.isPlaying) {
//暫停播放器
[_audioPlayer pause];
//暫停定時器
[timer setFireDate:[NSDate distantFuture]];
}
}
}
}
錄音demo
#import "ViewController.h"
#import
#define SCREEN_HEIGHT [UIScreen mainScreen].bounds.size.height
#define SCREEN_WIDTH [UIScreen mainScreen].bounds.size.width
@interface ViewController ()
{
//記錄錄音時間
UILabel * _timeLabel ;
AVAudioRecorder * _audioRecoder;
AVPlayer * _player;
}
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
[self createUI];
[self createAudioRecoder];
//定時器檢測錄音的時長
[NSTimer scheduledTimerWithTimeInterval:0.01 target:self selector:@selector(culTime) userInfo:nil repeats:YES];
}
#pragma mark -定時器函數
-(void)culTime{
_timeLabel.text = [NSString stringWithFormat:@"%.2f s",_audioRecoder.currentTime];
}
#pragma mark - 創建音頻錄制
-(void)createAudioRecoder{
//設置存儲錄音文件的路徑
NSString * path = [NSHomeDirectory() stringByAppendingPathComponent:@"Documents/recoder.aac"];
NSLog(@"%@",path);
//設置錄制音頻的屬性
NSDictionary * dic = @{AVFormatIDKey:@(kAudioFormatMPEG4AAC), //音頻格式
AVSampleRateKey:@(4000.0), //比特率
AVNumberOfChannelsKey:@(2) //聲道
};
//初始化音頻錄制
_audioRecoder = [[AVAudioRecorder alloc] initWithURL:[NSURL fileURLWithPath:path] settings:dic error:nil];
//設置代理 代理的作用是檢測錄音文件什麼時候出錯
_audioRecoder.delegate = self;
//預錄制
[_audioRecoder prepareToRecord];
}
#pragma mark - 創建UI
- (void)createUI{
_timeLabel = [[UILabel alloc] initWithFrame:CGRectMake(150, 100, 100, 40)];
_timeLabel.backgroundColor = [UIColor redColor];
[self.view addSubview: _timeLabel];
//控制按鈕
NSArray * arr = @[@"錄音",@"暫停",@"停止",@"播放"];
for (int i = 0; i< arr.count; i++) {
UIButton * btn = [UIButton buttonWithType:UIButtonTypeCustom];
btn.frame= CGRectMake(SCREEN_WIDTH/4*i, 200, SCREEN_WIDTH/4-20, 40);
[btn setTitle:arr[i] forState:UIControlStateNormal];
[btn setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
btn.tag = 100+i;
[btn addTarget:self action:@selector(onClick:) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:btn];
}
}
#pragma mark - 按鈕響應方法
-(void)onClick:(UIButton *)btn{
switch (btn.tag) {
case 100:
{
[_audioRecoder record];
}
break;
case 101:
{
[_audioRecoder pause];
}
break;
case 102:
{
[_audioRecoder stop];
}
break;
case 103:
{
[self startPlay];
}
break;
default:
break;
}
}
#pragma mark - 播放錄音
-(void)startPlay{
//初始化音頻播放器
_player = [[AVPlayer alloc] init];
//取得播放路徑
AVPlayerItem * item = [AVPlayerItem playerItemWithURL:_audioRecoder.url];
NSLog(@"%@",_audioRecoder.url);
//切換播放源
[_player replaceCurrentItemWithPlayerItem:item];
//播放
[_player play];
}
@end
視頻播放
一、MPMoviePlayerController
MPMoviePlayerController支持本地視頻和網絡視頻播放。這個類實現了MPMediaPlayback協議,因此具備一般的播放器控制功能,例如播放、暫停、停止等。但是MPMediaPlayerController自身並不是一個完整的視圖控制器,如果要在UI中展示視頻需要將view屬性添加到界面中
二、MPMoviePlayerViewController
其實MPMoviePlayerController如果不作為嵌入視頻來播放(例如在新聞中嵌入一個視頻),通常在播放時都是占滿一個屏幕的,特別是在iPhone、iTouch上。因此從iOS3.2以後蘋果也在思考既然MPMoviePlayerController在使用時通常都是將其視圖view添加到另外一個視圖控制器中作為子視圖,那麼何不直接創建一個控制器視圖內部創建一個MPMoviePlayerController屬性並且默認全屏播放,開發者在開發的時候直接使用這個視圖控制器。這個內部有一個MPMoviePlayerController的視圖控制器就是MPMoviePlayerViewController,它繼承於UIViewController。MPMoviePlayerViewController內部多了一個moviePlayer屬性和一個帶有url的初始化方法,同時它內部實現了一些作為模態視圖展示所特有的功能,例如默認是全屏模式展示、彈出後自動播放、作為模態窗口展示時如果點擊“Done”按鈕會自動退出模態窗口
iOS9之前的實現:
//初始化播放器並且設置播放資源
//網絡視頻播放
MPMoviePlayerViewController * player = [[MPMoviePlayerViewController alloc]initWithContentURL:[NSURL URLWithString:@"http://video.szzhangchu.com/1442391674615_6895658983.mp4"]];
//本地視頻播放
MPMoviePlayerViewController * player = [[MPMoviePlayerViewController alloc]initWithContentURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"MovieTest" ofType:@"mp4"]]];
//設置播放來源
player.moviePlayer.movieSourceType = MPMovieSourceTypeFile;
//設置全屏播放
player.moviePlayer.controlStyle = MPMovieControlStyleFullscreen;
//播放前視頻預處理
[player.moviePlayer prepareToPlay];
//開始播放
[player.moviePlayer play];
//推出視頻播放的控制器
[self presentViewController:player animated:YES completion:nil];
iOS9之後的實現:
//初始化播放器
AVPlayerViewController * playerVC = [[AVPlayerViewController alloc]init];
//設置播放資源
AVPlayer * player = [AVPlayer playerWithURL:url];
playerVC.player = player;
[self presentViewController:playerVC animated:YES completion:nil];
三、AVPlayer
MPMoviePlayerController足夠強大,幾乎不用寫幾行代碼就能完成一個播放器,但是正是由於它的高度封裝使得要自定義這個播放器變得很復雜,甚至是不可能完成。例如有些時候需要自定義播放器的樣式,那麼如果要使用MPMoviePlayerController就不合適了,如果要對視頻有自由的控制則可以使用AVPlayer。AVPlayer存在於AVFoundation中,它更加接近於底層,所以靈活性也更強
AVPlayer本身並不能顯示視頻,而且它也不像MPMoviePlayerController有一個view屬性。如果AVPlayer要顯示必須創建一個播放器層AVPlayerLayer用於展示,播放器層繼承於CALayer,有了AVPlayerLayer之添加到控制器視圖的layer中即可。要使用AVPlayer首先了解一下幾個常用的類:
AVAsset:主要用於獲取多媒體信息,是一個抽象類,不能直接使用。
AVURLAsset:AVAsset的子類,可以根據一個URL路徑創建一個包含媒體信息的AVURLAsset對象。
AVPlayerItem:一個媒體資源管理對象,管理者視頻的一些基本信息和狀態,一個AVPlayerItem對應著一個視頻資源
功能簡單介紹:
視頻的播放、暫停功能,這也是最基本的功能,AVPlayer對應著兩個方法play、pause來實現。但是關鍵問題是如何判斷當前視頻是否在播放,在前面的內容中無論是音頻播放器還是視頻播放器都有對應的狀態來判斷,但是AVPlayer卻沒有這樣的狀態屬性,通常情況下可以通過判斷播放器的播放速度來獲得播放狀態。如果rate為0說明是停止狀態,1是則是正常播放狀態。
到目前為止無論是MPMoviePlayerController還是AVPlayer來播放視頻都相當強大,但是它也存在著一些不可回避的問題,那就是支持的視頻編碼格式很有限:H.264、MPEG-4,擴展名(壓縮格式):.mp4、.mov、.m4v、.m2v、.3gp、.3g2等。但是無論是MPMoviePlayerController還是AVPlayer它們都支持絕大多數音頻編碼
四、VLC視頻播放(第三方視頻集成播放)
集成步驟
1>加入libMobileVLCKit
2>加庫:ibstdc++ libiconv libbz2 Security.framework QuartzCore.framework CoreText.framework CFnetWork.framework OpenGLES.framework AudioToolbox.framework
3>修改C++編譯器為stdC++:
在[Build Setting]輸入[c++][Apple LLVM 5.1 - Languate - C++]
[C++ Standard..]選第一個[libstdc++(GNU C++ standard library)]
4>在[Build Setting][search][Search Paths][Header..]加一個路徑
打開[libMobileVLCKit][include][MobileVLCKit]顯示簡介復制地址
把地址的前半部(包括文件夾名稱)分改為$(SRCROOT)
5>添加頭文件//#import “VLCMediaPlayer.h”
6>使用的文件改為 .mm支持C++編譯
舉例視頻鏈接
http://video.szzhangchu.com/1442391674615_6895658983.mp4
配置iOS9
NSAppTransportSecurity
NSAllowsArbitraryLoads