久久精品国产精品国产精品污,男人扒开添女人下部免费视频,一级国产69式性姿势免费视频,夜鲁夜鲁很鲁在线视频 视频,欧美丰满少妇一区二区三区,国产偷国产偷亚洲高清人乐享,中文 在线 日韩 亚洲 欧美,熟妇人妻无乱码中文字幕真矢织江,一区二区三区人妻制服国产

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

iOS开发系列--音频播放、录音、视频播放、拍照、视频录制(转)

發(fā)布時間:2023/12/19 编程问答 23 豆豆
生活随笔 收集整理的這篇文章主要介紹了 iOS开发系列--音频播放、录音、视频播放、拍照、视频录制(转) 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

?

概覽

隨著移動互聯(lián)網(wǎng)的發(fā)展,如今的手機早已不是打電話、發(fā)短信那么簡單了,播放音樂、視頻、錄音、拍照等都是很常用的功能。在iOS中對于多媒體的支持是非常強大的,無論是音視頻播放、錄制,還是對麥克風、攝像頭的操作都提供了多套API。在今天的文章中將會對這些內容進行一一介紹:

  • 音頻
  • 音效
  • 音樂
  • 音頻會話
  • 錄音
  • 音頻隊列服務
  • 視頻
  • MPMoviePlayerController
  • MPMoviePlayerViewController
  • AVPlayer
  • 攝像頭
  • UIImagePickerController拍照和視頻錄制
  • AVFoundation拍照和錄制視頻
  • 總結
  • 目?錄
  • 音頻

    在iOS中音頻播放從形式上可以分為音效播放和音樂播放。前者主要指的是一些短音頻播放,通常作為點綴音頻,對于這類音頻不需要進行進度、循環(huán)等控制。后者指的是一些較長的音頻,通常是主音頻,對于這些音頻的播放通常需要進行精確的控制。在iOS中播放兩類音頻分別使用AudioToolbox.framework和AVFoundation.framework來完成音效和音樂播放。

    音效

    AudioToolbox.framework是一套基于C語言的框架,使用它來播放音效其本質是將短音頻注冊到系統(tǒng)聲音服務(System Sound Service)。System Sound Service是一種簡單、底層的聲音播放服務,但是它本身也存在著一些限制:

    • 音頻播放時間不能超過30s
    • 數(shù)據(jù)必須是PCM或者IMA4格式
    • 音頻文件必須打包成.caf、.aif、.wav中的一種(注意這是官方文檔的說法,實際測試發(fā)現(xiàn)一些.mp3也可以播放)

    使用System Sound Service 播放音效的步驟如下:

  • 調用AudioServicesCreateSystemSoundID(?? CFURLRef? inFileURL, SystemSoundID*?? outSystemSoundID)函數(shù)獲得系統(tǒng)聲音ID。
  • 如果需要監(jiān)聽播放完成操作,則使用AudioServicesAddSystemSoundCompletion(? SystemSoundID inSystemSoundID,
    CFRunLoopRef? inRunLoop, CFStringRef? inRunLoopMode, AudioServicesSystemSoundCompletionProc? inCompletionRoutine, void*? inClientData)
    方法注冊回調函數(shù)。
  • 調用AudioServicesPlaySystemSound(SystemSoundID inSystemSoundID)?或者AudioServicesPlayAlertSound(SystemSoundID inSystemSoundID)?方法播放音效(后者帶有震動效果)。
  • 下面是一個簡單的示例程序:

    // // KCMainViewController.m // Audio // // Created by Kenshin Cui on 14/03/30. // Copyright (c) 2014年 cmjstudio. All rights reserved. // 音效播放#import "KCMainViewController.h" #import <AudioToolbox/AudioToolbox.h>@interface KCMainViewController ()@end@implementation KCMainViewController- (void)viewDidLoad {[super viewDidLoad];[self playSoundEffect:@"videoRing.caf"]; }/*** 播放完成回調函數(shù)** @param soundID 系統(tǒng)聲音ID* @param clientData 回調時傳遞的數(shù)據(jù)*/ void soundCompleteCallback(SystemSoundID soundID,void * clientData){NSLog(@"播放完成..."); }/*** 播放音效文件** @param name 音頻文件名稱*/ -(void)playSoundEffect:(NSString *)name{NSString *audioFile=[[NSBundle mainBundle] pathForResource:name ofType:nil];NSURL *fileUrl=[NSURL fileURLWithPath:audioFile];//1.獲得系統(tǒng)聲音IDSystemSoundID soundID=0;/*** inFileUrl:音頻文件url* outSystemSoundID:聲音id(此函數(shù)會將音效文件加入到系統(tǒng)音頻服務中并返回一個長整形ID)*/AudioServicesCreateSystemSoundID((__bridge CFURLRef)(fileUrl), &soundID);//如果需要在播放完之后執(zhí)行某些操作,可以調用如下方法注冊一個播放完成回調函數(shù)AudioServicesAddSystemSoundCompletion(soundID, NULL, NULL, soundCompleteCallback, NULL);//2.播放音頻AudioServicesPlaySystemSound(soundID);//播放音效 // AudioServicesPlayAlertSound(soundID);//播放音效并震動 }@end

    音樂

    如果播放較大的音頻或者要對音頻有精確的控制則System Sound Service可能就很難滿足實際需求了,通常這種情況會選擇使用AVFoundation.framework中的AVAudioPlayer來實現(xiàn)。AVAudioPlayer可以看成一個播放器,它支持多種音頻格式,而且能夠進行進度、音量、播放速度等控制。首先簡單看一下AVAudioPlayer常用的屬性和方法:

    屬性說明
    @property(readonly, getter=isPlaying) BOOL playing是否正在播放,只讀
    @property(readonly) NSUInteger numberOfChannels音頻聲道數(shù),只讀
    @property(readonly) NSTimeInterval duration音頻時長
    @property(readonly) NSURL *url音頻文件路徑,只讀
    @property(readonly) NSData *data音頻數(shù)據(jù),只讀
    @property float pan立體聲平衡,如果為-1.0則完全左聲道,如果0.0則左右聲道平衡,如果為1.0則完全為右聲道
    @property float volume音量大小,范圍0-1.0
    @property BOOL enableRate是否允許改變播放速率
    @property float rate播放速率,范圍0.5-2.0,如果為1.0則正常播放,如果要修改播放速率則必須設置enableRate為YES
    @property NSTimeInterval currentTime當前播放時長
    @property(readonly) NSTimeInterval deviceCurrentTime輸出設備播放音頻的時間,注意如果播放中被暫停此時間也會繼續(xù)累加
    @property NSInteger numberOfLoops循環(huán)播放次數(shù),如果為0則不循環(huán),如果小于0則無限循環(huán),大于0則表示循環(huán)次數(shù)
    @property(readonly) NSDictionary *settings音頻播放設置信息,只讀
    @property(getter=isMeteringEnabled) BOOL meteringEnabled是否啟用音頻測量,默認為NO,一旦啟用音頻測量可以通過updateMeters方法更新測量值
    對象方法說明
    - (instancetype)initWithContentsOfURL:(NSURL *)url error:(NSError **)outError使用文件URL初始化播放器,注意這個URL不能是HTTP URL,AVAudioPlayer不支持加載網(wǎng)絡媒體流,只能播放本地文件
    - (instancetype)initWithData:(NSData *)data error:(NSError **)outError使用NSData初始化播放器,注意使用此方法時必須文件格式和文件后綴一致,否則出錯,所以相比此方法更推薦使用上述方法或- (instancetype)initWithData:(NSData *)data fileTypeHint:(NSString *)utiString error:(NSError **)outError方法進行初始化
    - (BOOL)prepareToPlay;加載音頻文件到緩沖區(qū),注意即使在播放之前音頻文件沒有加載到緩沖區(qū)程序也會隱式調用此方法。
    - (BOOL)play;播放音頻文件
    - (BOOL)playAtTime:(NSTimeInterval)time在指定的時間開始播放音頻
    - (void)pause;暫停播放
    - (void)stop;停止播放
    - (void)updateMeters更新音頻測量值,注意如果要更新音頻測量值必須設置meteringEnabled為YES,通過音頻測量值可以即時獲得音頻分貝等信息
    - (float)peakPowerForChannel:(NSUInteger)channelNumber;獲得指定聲道的分貝峰值,注意如果要獲得分貝峰值必須在此之前調用updateMeters方法
    - (float)averagePowerForChannel:(NSUInteger)channelNumber獲得指定聲道的分貝平均值,注意如果要獲得分貝平均值必須在此之前調用updateMeters方法
    @property(nonatomic, copy) NSArray *channelAssignments獲得或設置播放聲道
    代理方法說明
    - (void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag音頻播放完成
    - (void)audioPlayerDecodeErrorDidOccur:(AVAudioPlayer *)player error:(NSError *)error音頻解碼發(fā)生錯誤

    AVAudioPlayer的使用比較簡單:

  • 初始化AVAudioPlayer對象,此時通常指定本地文件路徑。
  • 設置播放器屬性,例如重復次數(shù)、音量大小等。
  • 調用play方法播放。
  • 下面就使用AVAudioPlayer實現(xiàn)一個簡單播放器,在這個播放器中實現(xiàn)了播放、暫停、顯示播放進度功能,當然例如調節(jié)音量、設置循環(huán)模式、甚至是聲波圖像(通過分析音頻分貝值)等功能都可以實現(xiàn),這里就不再一一演示。界面效果如下:

    當然由于AVAudioPlayer一次只能播放一個音頻文件,所有上一曲、下一曲其實可以通過創(chuàng)建多個播放器對象來完成,這里暫不實現(xiàn)。播放進度的實現(xiàn)主要依靠一個定時器實時計算當前播放時長和音頻總時長的比例,另外為了演示委托方法,下面的代碼中也實現(xiàn)了播放完成委托方法,通常如果有下一曲功能的話播放完可以觸發(fā)下一曲音樂播放。下面是主要代碼:

    // // ViewController.m // KCAVAudioPlayer // // Created by Kenshin Cui on 14/03/30. // Copyright (c) 2014年 cmjstudio. All rights reserved. //#import "ViewController.h" #import <AVFoundation/AVFoundation.h> #define kMusicFile @"劉若英 - 原來你也在這里.mp3" #define kMusicSinger @"劉若英" #define kMusicTitle @"原來你也在這里"@interface ViewController ()<AVAudioPlayerDelegate>@property (nonatomic,strong) AVAudioPlayer *audioPlayer;//播放器 @property (weak, nonatomic) IBOutlet UILabel *controlPanel; //控制面板 @property (weak, nonatomic) IBOutlet UIProgressView *playProgress;//播放進度 @property (weak, nonatomic) IBOutlet UILabel *musicSinger; //演唱者 @property (weak, nonatomic) IBOutlet UIButton *playOrPause; //播放/暫停按鈕(如果tag為0認為是暫停狀態(tài),1是播放狀態(tài))@property (weak ,nonatomic) NSTimer *timer;//進度更新定時器@end@implementation ViewController- (void)viewDidLoad {[super viewDidLoad];[self setupUI];}/*** 初始化UI*/ -(void)setupUI{self.title=kMusicTitle;self.musicSinger.text=kMusicSinger; }-(NSTimer *)timer{if (!_timer) {_timer=[NSTimer scheduledTimerWithTimeInterval:0.5 target:self selector:@selector(updateProgress) userInfo:nil repeats:true];}return _timer; }/*** 創(chuàng)建播放器** @return 音頻播放器*/ -(AVAudioPlayer *)audioPlayer{if (!_audioPlayer) {NSString *urlStr=[[NSBundle mainBundle]pathForResource:kMusicFile ofType:nil];NSURL *url=[NSURL fileURLWithPath:urlStr];NSError *error=nil;//初始化播放器,注意這里的Url參數(shù)只能時文件路徑,不支持HTTP Url_audioPlayer=[[AVAudioPlayer alloc]initWithContentsOfURL:url error:&error];//設置播放器屬性_audioPlayer.numberOfLoops=0;//設置為0不循環(huán)_audioPlayer.delegate=self;[_audioPlayer prepareToPlay];//加載音頻文件到緩存if(error){NSLog(@"初始化播放器過程發(fā)生錯誤,錯誤信息:%@",error.localizedDescription);return nil;}}return _audioPlayer; }/*** 播放音頻*/ -(void)play{if (![self.audioPlayer isPlaying]) {[self.audioPlayer play];self.timer.fireDate=[NSDate distantPast];//恢復定時器} }/*** 暫停播放*/ -(void)pause{if ([self.audioPlayer isPlaying]) {[self.audioPlayer pause];self.timer.fireDate=[NSDate distantFuture];//暫停定時器,注意不能調用invalidate方法,此方法會取消,之后無法恢復} }/*** 點擊播放/暫停按鈕** @param sender 播放/暫停按鈕*/ - (IBAction)playClick:(UIButton *)sender {if(sender.tag){sender.tag=0;[sender setImage:[UIImage imageNamed:@"playing_btn_play_n"] forState:UIControlStateNormal];[sender setImage:[UIImage imageNamed:@"playing_btn_play_h"] forState:UIControlStateHighlighted];[self pause];}else{sender.tag=1;[sender setImage:[UIImage imageNamed:@"playing_btn_pause_n"] forState:UIControlStateNormal];[sender setImage:[UIImage imageNamed:@"playing_btn_pause_h"] forState:UIControlStateHighlighted];[self play];} }/*** 更新播放進度*/ -(void)updateProgress{float progress= self.audioPlayer.currentTime /self.audioPlayer.duration;[self.playProgress setProgress:progress animated:true]; }#pragma mark - 播放器代理方法 -(void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag{NSLog(@"音樂播放完成..."); }@end 運行效果:

    音頻會話

    事實上上面的播放器還存在一些問題,例如通常我們看到的播放器即使退出到后臺也是可以播放的,而這個播放器如果退出到后臺它會自動暫停。如果要支持后臺播放需要做下面幾件事情:

    1.設置后臺運行模式:在plist文件中添加Required background modes,并且設置item 0=App plays audio or streams audio/video using AirPlay(其實可以直接通過Xcode在Project Targets-Capabilities-Background Modes中設置)

    2.設置AVAudioSession的類型為AVAudioSessionCategoryPlayback并且調用setActive::方法啟動會話。

    AVAudioSession *audioSession=[AVAudioSession sharedInstance];[audioSession setCategory:AVAudioSessionCategoryPlayback error:nil];[audioSession setActive:YES error:nil];

    3.為了能夠讓應用退到后臺之后支持耳機控制,建議添加遠程控制事件(這一步不是后臺播放必須的)

    前兩步是后臺播放所必須設置的,第三步主要用于接收遠程事件,這部分內容之前的文章中有詳細介紹,如果這一步不設置雖讓也能夠在后臺播放,但是無法獲得音頻控制權(如果在使用當前應用之前使用其他播放器播放音樂的話,此時如果按耳機播放鍵或者控制中心的播放按鈕則會播放前一個應用的音頻),并且不能使用耳機進行音頻控制。第一步操作相信大家都很容易理解,如果應用程序要允許運行到后臺必須設置,正常情況下應用如果進入后臺會被掛起,通過該設置可以上應用程序繼續(xù)在后臺運行。但是第二步使用的AVAudioSession有必要進行一下詳細的說明。

    在iOS中每個應用都有一個音頻會話,這個會話就通過AVAudioSession來表示。AVAudioSession同樣存在于AVFoundation框架中,它是單例模式設計,通過sharedInstance進行訪問。在使用Apple設備時大家會發(fā)現(xiàn)有些應用只要打開其他音頻播放就會終止,而有些應用卻可以和其他應用同時播放,在多種音頻環(huán)境中如何去控制播放的方式就是通過音頻會話來完成的。下面是音頻會話的幾種會話模式:

    會話類型說明是否要求輸入是否要求輸出是否遵從靜音鍵
    AVAudioSessionCategoryAmbient混音播放,可以與其他音頻應用同時播放
    AVAudioSessionCategorySoloAmbient獨占播放
    AVAudioSessionCategoryPlayback后臺播放,也是獨占的
    AVAudioSessionCategoryRecord錄音模式,用于錄音時使用
    AVAudioSessionCategoryPlayAndRecord播放和錄音,此時可以錄音也可以播放
    AVAudioSessionCategoryAudioProcessing硬件解碼音頻,此時不能播放和錄制
    AVAudioSessionCategoryMultiRoute多種輸入輸出,例如可以耳機、USB設備同時播放

    注意:是否遵循靜音鍵表示在播放過程中如果用戶通過硬件設置為靜音是否能關閉聲音。

    根據(jù)前面對音頻會話的理解,相信大家開發(fā)出能夠在后臺播放的音頻播放器并不難,但是注意一下,在前面的代碼中也提到設置完音頻會話類型之后需要調用setActive::方法將會話激活才能起作用。類似的,如果一個應用已經(jīng)在播放音頻,打開我們的應用之后設置了在后臺播放的會話類型,此時其他應用的音頻會停止而播放我們的音頻,如果希望我們的程序音頻播放完之后(關閉或退出到后臺之后)能夠繼續(xù)播放其他應用的音頻的話則可以調用setActive::方法關閉會話。代碼如下:

    // // ViewController.m // KCAVAudioPlayer // // Created by Kenshin Cui on 14/03/30. // Copyright (c) 2014年 cmjstudio. All rights reserved. // AVAudioSession 音頻會話#import "ViewController.h" #import <AVFoundation/AVFoundation.h> #define kMusicFile @"劉若英 - 原來你也在這里.mp3" #define kMusicSinger @"劉若英" #define kMusicTitle @"原來你也在這里"@interface ViewController ()<AVAudioPlayerDelegate>@property (nonatomic,strong) AVAudioPlayer *audioPlayer;//播放器 @property (weak, nonatomic) IBOutlet UILabel *controlPanel; //控制面板 @property (weak, nonatomic) IBOutlet UIProgressView *playProgress;//播放進度 @property (weak, nonatomic) IBOutlet UILabel *musicSinger; //演唱者 @property (weak, nonatomic) IBOutlet UIButton *playOrPause; //播放/暫停按鈕(如果tag為0認為是暫停狀態(tài),1是播放狀態(tài))@property (weak ,nonatomic) NSTimer *timer;//進度更新定時器@end@implementation ViewController- (void)viewDidLoad {[super viewDidLoad];[self setupUI];}/*** 顯示當面視圖控制器時注冊遠程事件** @param animated 是否以動畫的形式顯示*/ -(void)viewWillAppear:(BOOL)animated{[super viewWillAppear:animated];//開啟遠程控制[[UIApplication sharedApplication] beginReceivingRemoteControlEvents];//作為第一響應者//[self becomeFirstResponder]; } /*** 當前控制器視圖不顯示時取消遠程控制** @param animated 是否以動畫的形式消失*/ -(void)viewWillDisappear:(BOOL)animated{[super viewWillDisappear:animated];[[UIApplication sharedApplication] endReceivingRemoteControlEvents];//[self resignFirstResponder]; }/*** 初始化UI*/ -(void)setupUI{self.title=kMusicTitle;self.musicSinger.text=kMusicSinger; }-(NSTimer *)timer{if (!_timer) {_timer=[NSTimer scheduledTimerWithTimeInterval:0.5 target:self selector:@selector(updateProgress) userInfo:nil repeats:true];}return _timer; }/*** 創(chuàng)建播放器** @return 音頻播放器*/ -(AVAudioPlayer *)audioPlayer{if (!_audioPlayer) {NSString *urlStr=[[NSBundle mainBundle]pathForResource:kMusicFile ofType:nil];NSURL *url=[NSURL fileURLWithPath:urlStr];NSError *error=nil;//初始化播放器,注意這里的Url參數(shù)只能時文件路徑,不支持HTTP Url_audioPlayer=[[AVAudioPlayer alloc]initWithContentsOfURL:url error:&error];//設置播放器屬性_audioPlayer.numberOfLoops=0;//設置為0不循環(huán)_audioPlayer.delegate=self;[_audioPlayer prepareToPlay];//加載音頻文件到緩存if(error){NSLog(@"初始化播放器過程發(fā)生錯誤,錯誤信息:%@",error.localizedDescription);return nil;}//設置后臺播放模式AVAudioSession *audioSession=[AVAudioSession sharedInstance];[audioSession setCategory:AVAudioSessionCategoryPlayback error:nil]; // [audioSession setCategory:AVAudioSessionCategoryPlayback withOptions:AVAudioSessionCategoryOptionAllowBluetooth error:nil];[audioSession setActive:YES error:nil];//添加通知,拔出耳機后暫停播放[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(routeChange:) name:AVAudioSessionRouteChangeNotification object:nil];}return _audioPlayer; }/*** 播放音頻*/ -(void)play{if (![self.audioPlayer isPlaying]) {[self.audioPlayer play];self.timer.fireDate=[NSDate distantPast];//恢復定時器} }/*** 暫停播放*/ -(void)pause{if ([self.audioPlayer isPlaying]) {[self.audioPlayer pause];self.timer.fireDate=[NSDate distantFuture];//暫停定時器,注意不能調用invalidate方法,此方法會取消,之后無法恢復} }/*** 點擊播放/暫停按鈕** @param sender 播放/暫停按鈕*/ - (IBAction)playClick:(UIButton *)sender {if(sender.tag){sender.tag=0;[sender setImage:[UIImage imageNamed:@"playing_btn_play_n"] forState:UIControlStateNormal];[sender setImage:[UIImage imageNamed:@"playing_btn_play_h"] forState:UIControlStateHighlighted];[self pause];}else{sender.tag=1;[sender setImage:[UIImage imageNamed:@"playing_btn_pause_n"] forState:UIControlStateNormal];[sender setImage:[UIImage imageNamed:@"playing_btn_pause_h"] forState:UIControlStateHighlighted];[self play];} }/*** 更新播放進度*/ -(void)updateProgress{float progress= self.audioPlayer.currentTime /self.audioPlayer.duration;[self.playProgress setProgress:progress animated:true]; }/*** 一旦輸出改變則執(zhí)行此方法** @param notification 輸出改變通知對象*/ -(void)routeChange:(NSNotification *)notification{NSDictionary *dic=notification.userInfo;int changeReason= [dic[AVAudioSessionRouteChangeReasonKey] intValue];//等于AVAudioSessionRouteChangeReasonOldDeviceUnavailable表示舊輸出不可用if (changeReason==AVAudioSessionRouteChangeReasonOldDeviceUnavailable) {AVAudioSessionRouteDescription *routeDescription=dic[AVAudioSessionRouteChangePreviousRouteKey];AVAudioSessionPortDescription *portDescription= [routeDescription.outputs firstObject];//原設備為耳機則暫停if ([portDescription.portType isEqualToString:@"Headphones"]) {[self pause];}}// [dic enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) { // NSLog(@"%@:%@",key,obj); // }]; }-(void)dealloc{[[NSNotificationCenter defaultCenter] removeObserver:self name:AVAudioSessionRouteChangeNotification object:nil]; }#pragma mark - 播放器代理方法 -(void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag{NSLog(@"音樂播放完成...");//根據(jù)實際情況播放完成可以將會話關閉,其他音頻應用繼續(xù)播放[[AVAudioSession sharedInstance]setActive:NO error:nil]; }@end

    在上面的代碼中還實現(xiàn)了拔出耳機暫停音樂播放的功能,這也是一個比較常見的功能。在iOS7及以后的版本中可以通過通知獲得輸出改變的通知,然后拿到通知對象后根據(jù)userInfo獲得是何種改變類型,進而根據(jù)情況對音樂進行暫停操作。

    擴展--播放音樂庫中的音樂

    眾所周知音樂是iOS的重要組成播放,無論是iPod、iTouch、iPhone還是iPad都可以在iTunes購買音樂或添加本地音樂到音樂庫中同步到你的iOS設備。在MediaPlayer.frameowork中有一個MPMusicPlayerController用于播放音樂庫中的音樂。

    下面先來看一下MPMusicPlayerController的常用屬性和方法:

    屬性說明
    @property (nonatomic, readonly) MPMusicPlaybackState playbackState播放器狀態(tài),枚舉類型:
    MPMusicPlaybackStateStopped:停止播放 MPMusicPlaybackStatePlaying:正在播放
    MPMusicPlaybackStatePaused:暫停播放
    MPMusicPlaybackStateInterrupted:播放中斷
    MPMusicPlaybackStateSeekingForward:向前查找
    MPMusicPlaybackStateSeekingBackward:向后查找
    @property (nonatomic) MPMusicRepeatMode repeatMode重復模式,枚舉類型:
    MPMusicRepeatModeDefault:默認模式,使用用戶的首選項(系統(tǒng)音樂程序設置)
    MPMusicRepeatModeNone:不重復
    MPMusicRepeatModeOne:單曲循環(huán)
    MPMusicRepeatModeAll:在當前列表內循環(huán)
    @property (nonatomic) MPMusicShuffleMode shuffleMode隨機播放模式,枚舉類型:
    MPMusicShuffleModeDefault:默認模式,使用用戶首選項(系統(tǒng)音樂程序設置)
    MPMusicShuffleModeOff:不隨機播放
    MPMusicShuffleModeSongs:按歌曲隨機播放
    MPMusicShuffleModeAlbums:按專輯隨機播放
    @property (nonatomic, copy) MPMediaItem *nowPlayingItem正在播放的音樂項
    @property (nonatomic, readonly) NSUInteger indexOfNowPlayingItem當前正在播放的音樂在播放隊列中的索引
    @property(nonatomic, readonly) BOOL isPreparedToPlay是否準好播放準備
    @property(nonatomic) NSTimeInterval currentPlaybackTime當前已播放時間,單位:秒
    @property(nonatomic) float currentPlaybackRate當前播放速度,是一個播放速度倍率,0表示暫停播放,1代表正常速度
    類方法說明
    + (MPMusicPlayerController *)applicationMusicPlayer;獲取應用播放器,注意此類播放器無法在后臺播放
    + (MPMusicPlayerController *)systemMusicPlayer獲取系統(tǒng)播放器,支持后臺播放
    對象方法說明
    - (void)setQueueWithQuery:(MPMediaQuery *)query使用媒體隊列設置播放源媒體隊列
    - (void)setQueueWithItemCollection:(MPMediaItemCollection *)itemCollection使用媒體項集合設置播放源媒體隊列
    - (void)skipToNextItem下一曲
    - (void)skipToBeginning從起始位置播放
    - (void)skipToPreviousItem上一曲
    - (void)beginGeneratingPlaybackNotifications開啟播放通知,注意不同于其他播放器,MPMusicPlayerController要想獲得通知必須首先開啟,默認情況無法獲得通知
    - (void)endGeneratingPlaybackNotifications關閉播放通知
    - (void)prepareToPlay做好播放準備(加載音頻到緩沖區(qū)),在使用play方法播放時如果沒有做好準備回自動調用該方法
    - (void)play開始播放
    - (void)pause暫停播放
    - (void)stop停止播放
    - (void)beginSeekingForward開始向前查找(快進)
    - (void)beginSeekingBackward開始向后查找(快退)
    - (void)endSeeking結束查找
    通知說明
    (注意:要想獲得MPMusicPlayerController通知必須首先調用beginGeneratingPlaybackNotifications開啟通知)
    MPMusicPlayerControllerPlaybackStateDidChangeNotification播放狀態(tài)改變
    MPMusicPlayerControllerNowPlayingItemDidChangeNotification當前播放音頻改變
    MPMusicPlayerControllerVolumeDidChangeNotification聲音大小改變
    MPMediaPlaybackIsPreparedToPlayDidChangeNotification準備好播放
    • MPMusicPlayerController有兩種播放器:applicationMusicPlayer和systemMusicPlayer,前者在應用退出后音樂播放會自動停止,后者在應用停止后不會退出播放狀態(tài)。
    • MPMusicPlayerController加載音樂不同于前面的AVAudioPlayer是通過一個文件路徑來加載,而是需要一個播放隊列。在MPMusicPlayerController中提供了兩個方法來加載播放隊列:- (void)setQueueWithQuery:(MPMediaQuery *)query- (void)setQueueWithItemCollection:(MPMediaItemCollection *)itemCollection,正是由于它的播放音頻來源是一個隊列,因此MPMusicPlayerController支持上一曲、下一曲等操作。

    那么接下來的問題就是如何獲取MPMediaQueue或者MPMediaItemCollection?MPMediaQueue對象有一系列的類方法來獲得媒體隊列:

    + (MPMediaQuery *)albumsQuery;
    + (MPMediaQuery *)artistsQuery;
    + (MPMediaQuery *)songsQuery;
    + (MPMediaQuery *)playlistsQuery;
    + (MPMediaQuery *)podcastsQuery;
    + (MPMediaQuery *)audiobooksQuery;
    + (MPMediaQuery *)compilationsQuery;
    + (MPMediaQuery *)composersQuery;
    + (MPMediaQuery *)genresQuery;

    有了這些方法,就可以很容易獲到歌曲、播放列表、專輯媒體等媒體隊列了,這樣就可以通過:- (void)setQueueWithQuery:(MPMediaQuery *)query方法設置音樂來源了。又或者得到MPMediaQueue之后創(chuàng)建MPMediaItemCollection,使用- (void)setQueueWithItemCollection:(MPMediaItemCollection *)itemCollection設置音樂來源。

    有時候可能希望用戶自己來選擇要播放的音樂,這時可以使用MPMediaPickerController,它是一個視圖控制器,類似于UIImagePickerController,選擇完播放來源后可以在其代理方法中獲得MPMediaItemCollection對象。

    無論是通過哪種方式獲得MPMusicPlayerController的媒體源,可能都希望將每個媒體的信息顯示出來,這時候可以通過MPMediaItem對象獲得。一個MPMediaItem代表一個媒體文件,通過它可以訪問媒體標題、專輯名稱、專輯封面、音樂時長等等。無論是MPMediaQueue還是MPMediaItemCollection都有一個items屬性,它是MPMediaItem數(shù)組,通過這個屬性可以獲得MPMediaItem對象。

    下面就簡單看一下MPMusicPlayerController的使用,在下面的例子中簡單演示了音樂的選擇、播放、暫停、通知、下一曲、上一曲功能,相信有了上面的概念,代碼讀起來并不復雜(示例中是直接通過MPMeidaPicker進行音樂選擇的,但是仍然提供了兩個方法getLocalMediaQuery和getLocalMediaItemCollection來演示如何直接通過MPMediaQueue獲得媒體隊列或媒體集合):

    // // ViewController.m // MPMusicPlayerController // // Created by Kenshin Cui 14/03/30 // Copyright (c) 2014年 cmjstudio. All rights reserved. //#import "ViewController.h" #import <MediaPlayer/MediaPlayer.h>@interface ViewController ()<MPMediaPickerControllerDelegate>@property (nonatomic,strong) MPMediaPickerController *mediaPicker;//媒體選擇控制器 @property (nonatomic,strong) MPMusicPlayerController *musicPlayer; //音樂播放器@end@implementation ViewController- (void)viewDidLoad {[super viewDidLoad]; }-(void)dealloc{[self.musicPlayer endGeneratingPlaybackNotifications]; }/*** 獲得音樂播放器** @return 音樂播放器*/ -(MPMusicPlayerController *)musicPlayer{if (!_musicPlayer) {_musicPlayer=[MPMusicPlayerController systemMusicPlayer];[_musicPlayer beginGeneratingPlaybackNotifications];//開啟通知,否則監(jiān)控不到MPMusicPlayerController的通知[self addNotification];//添加通知//如果不使用MPMediaPickerController可以使用如下方法獲得音樂庫媒體隊列//[_musicPlayer setQueueWithItemCollection:[self getLocalMediaItemCollection]];}return _musicPlayer; }/*** 創(chuàng)建媒體選擇器** @return 媒體選擇器*/ -(MPMediaPickerController *)mediaPicker{if (!_mediaPicker) {//初始化媒體選擇器,這里設置媒體類型為音樂,其實這里也可以選擇視頻、廣播等 // _mediaPicker=[[MPMediaPickerController alloc]initWithMediaTypes:MPMediaTypeMusic];_mediaPicker=[[MPMediaPickerController alloc]initWithMediaTypes:MPMediaTypeAny];_mediaPicker.allowsPickingMultipleItems=YES;//允許多選 // _mediaPicker.showsCloudItems=YES;//顯示icloud選項_mediaPicker.prompt=@"請選擇要播放的音樂";_mediaPicker.delegate=self;//設置選擇器代理}return _mediaPicker; }/*** 取得媒體隊列** @return 媒體隊列*/ -(MPMediaQuery *)getLocalMediaQuery{MPMediaQuery *mediaQueue=[MPMediaQuery songsQuery];for (MPMediaItem *item in mediaQueue.items) {NSLog(@"標題:%@,%@",item.title,item.albumTitle);}return mediaQueue; }/*** 取得媒體集合** @return 媒體集合*/ -(MPMediaItemCollection *)getLocalMediaItemCollection{MPMediaQuery *mediaQueue=[MPMediaQuery songsQuery];NSMutableArray *array=[NSMutableArray array];for (MPMediaItem *item in mediaQueue.items) {[array addObject:item];NSLog(@"標題:%@,%@",item.title,item.albumTitle);}MPMediaItemCollection *mediaItemCollection=[[MPMediaItemCollection alloc]initWithItems:[array copy]];return mediaItemCollection; }#pragma mark - MPMediaPickerController代理方法 //選擇完成 -(void)mediaPicker:(MPMediaPickerController *)mediaPicker didPickMediaItems:(MPMediaItemCollection *)mediaItemCollection{MPMediaItem *mediaItem=[mediaItemCollection.items firstObject];//第一個播放音樂//注意很多音樂信息如標題、專輯、表演者、封面、時長等信息都可以通過MPMediaItem的valueForKey:方法得到,但是從iOS7開始都有對應的屬性可以直接訪問 // NSString *title= [mediaItem valueForKey:MPMediaItemPropertyAlbumTitle]; // NSString *artist= [mediaItem valueForKey:MPMediaItemPropertyAlbumArtist]; // MPMediaItemArtwork *artwork= [mediaItem valueForKey:MPMediaItemPropertyArtwork];//UIImage *image=[artwork imageWithSize:CGSizeMake(100, 100)];//專輯圖片NSLog(@"標題:%@,表演者:%@,專輯:%@",mediaItem.title ,mediaItem.artist,mediaItem.albumTitle);[self.musicPlayer setQueueWithItemCollection:mediaItemCollection];[self dismissViewControllerAnimated:YES completion:nil]; } //取消選擇 -(void)mediaPickerDidCancel:(MPMediaPickerController *)mediaPicker{[self dismissViewControllerAnimated:YES completion:nil]; }#pragma mark - 通知 /*** 添加通知*/ -(void)addNotification{NSNotificationCenter *notificationCenter=[NSNotificationCenter defaultCenter];[notificationCenter addObserver:self selector:@selector(playbackStateChange:) name:MPMusicPlayerControllerPlaybackStateDidChangeNotification object:self.musicPlayer]; }/*** 播放狀態(tài)改變通知** @param notification 通知對象*/ -(void)playbackStateChange:(NSNotification *)notification{switch (self.musicPlayer.playbackState) {case MPMusicPlaybackStatePlaying:NSLog(@"正在播放...");break;case MPMusicPlaybackStatePaused:NSLog(@"播放暫停.");break;case MPMusicPlaybackStateStopped:NSLog(@"播放停止.");break;default:break;} }#pragma mark - UI事件 - (IBAction)selectClick:(UIButton *)sender {[self presentViewController:self.mediaPicker animated:YES completion:nil]; }- (IBAction)playClick:(UIButton *)sender {[self.musicPlayer play]; }- (IBAction)puaseClick:(UIButton *)sender {[self.musicPlayer pause]; }- (IBAction)stopClick:(UIButton *)sender {[self.musicPlayer stop]; }- (IBAction)nextClick:(UIButton *)sender {[self.musicPlayer skipToNextItem]; }- (IBAction)prevClick:(UIButton *)sender {[self.musicPlayer skipToPreviousItem]; }@end

    錄音

    除了上面說的,在AVFoundation框架中還要一個AVAudioRecorder類專門處理錄音操作,它同樣支持多種音頻格式。與AVAudioPlayer類似,你完全可以將它看成是一個錄音機控制類,下面是常用的屬性和方法:

    屬性說明
    @property(readonly, getter=isRecording) BOOL recording;是否正在錄音,只讀
    @property(readonly) NSURL *url錄音文件地址,只讀
    @property(readonly) NSDictionary *settings錄音文件設置,只讀
    @property(readonly) NSTimeInterval currentTime錄音時長,只讀,注意僅僅在錄音狀態(tài)可用
    @property(readonly) NSTimeInterval deviceCurrentTime輸入設置的時間長度,只讀,注意此屬性一直可訪問
    @property(getter=isMeteringEnabled) BOOL meteringEnabled;是否啟用錄音測量,如果啟用錄音測量可以獲得錄音分貝等數(shù)據(jù)信息
    @property(nonatomic, copy) NSArray *channelAssignments當前錄音的通道
    對象方法說明
    - (instancetype)initWithURL:(NSURL *)url settings:(NSDictionary *)settings error:(NSError **)outError錄音機對象初始化方法,注意其中的url必須是本地文件url,settings是錄音格式、編碼等設置
    - (BOOL)prepareToRecord準備錄音,主要用于創(chuàng)建緩沖區(qū),如果不手動調用,在調用record錄音時也會自動調用
    - (BOOL)record開始錄音
    - (BOOL)recordAtTime:(NSTimeInterval)time在指定的時間開始錄音,一般用于錄音暫停再恢復錄音
    - (BOOL)recordForDuration:(NSTimeInterval) duration按指定的時長開始錄音
    - (BOOL)recordAtTime:(NSTimeInterval)time forDuration:(NSTimeInterval) duration在指定的時間開始錄音,并指定錄音時長
    - (void)pause;暫停錄音
    - (void)stop;停止錄音
    - (BOOL)deleteRecording;刪除錄音,注意要刪除錄音此時錄音機必須處于停止狀態(tài)
    - (void)updateMeters;更新測量數(shù)據(jù),注意只有meteringEnabled為YES此方法才可用
    - (float)peakPowerForChannel:(NSUInteger)channelNumber;指定通道的測量峰值,注意只有調用完updateMeters才有值
    - (float)averagePowerForChannel:(NSUInteger)channelNumber指定通道的測量平均值,注意只有調用完updateMeters才有值
    代理方法說明
    - (void)audioRecorderDidFinishRecording:(AVAudioRecorder *)recorder successfully:(BOOL)flag完成錄音
    - (void)audioRecorderEncodeErrorDidOccur:(AVAudioRecorder *)recorder error:(NSError *)error錄音編碼發(fā)生錯誤

    AVAudioRecorder很多屬性和方法跟AVAudioPlayer都是類似的,但是它的創(chuàng)建有所不同,在創(chuàng)建錄音機時除了指定路徑外還必須指定錄音設置信息,因為錄音機必須知道錄音文件的格式、采樣率、通道數(shù)、每個采樣點的位數(shù)等信息,但是也并不是所有的信息都必須設置,通常只需要幾個常用設置。關于錄音設置詳見幫助文檔中的“AV Foundation Audio Settings Constants”。

    下面就使用AVAudioRecorder創(chuàng)建一個錄音機,實現(xiàn)了錄音、暫停、停止、播放等功能,實現(xiàn)效果大致如下:

    在這個示例中將實行一個完整的錄音控制,包括錄音、暫停、恢復、停止,同時還會實時展示用戶錄音的聲音波動,當用戶點擊完停止按鈕還會自動播放錄音文件。程序的構建主要分為以下幾步:

  • 設置音頻會話類型為AVAudioSessionCategoryPlayAndRecord,因為程序中牽扯到錄音和播放操作。
  • 創(chuàng)建錄音機AVAudioRecorder,指定錄音保存的路徑并且設置錄音屬性,注意對于一般的錄音文件要求的采樣率、位數(shù)并不高,需要適當設置以保證錄音文件的大小和效果。
  • 設置錄音機代理以便在錄音完成后播放錄音,打開錄音測量保證能夠實時獲得錄音時的聲音強度。(注意聲音強度范圍-160到0,0代表最大輸入)
  • 創(chuàng)建音頻播放器AVAudioPlayer,用于在錄音完成之后播放錄音。
  • 創(chuàng)建一個定時器以便實時刷新錄音測量值并更新錄音強度到UIProgressView中顯示。
  • 添加錄音、暫停、恢復、停止操作,需要注意錄音的恢復操作其實是有音頻會話管理的,恢復時只要再次調用record方法即可,無需手動管理恢復時間等。
  • 下面是主要代碼:

    // // ViewController.m // AVAudioRecorder // // Created by Kenshin Cui on 14/03/30. // Copyright (c) 2014年 cmjstudio. All rights reserved. //#import "ViewController.h" #import <AVFoundation/AVFoundation.h> #define kRecordAudioFile @"myRecord.caf"@interface ViewController ()<AVAudioRecorderDelegate>@property (nonatomic,strong) AVAudioRecorder *audioRecorder;//音頻錄音機 @property (nonatomic,strong) AVAudioPlayer *audioPlayer;//音頻播放器,用于播放錄音文件 @property (nonatomic,strong) NSTimer *timer;//錄音聲波監(jiān)控(注意這里暫時不對播放進行監(jiān)控)@property (weak, nonatomic) IBOutlet UIButton *record;//開始錄音 @property (weak, nonatomic) IBOutlet UIButton *pause;//暫停錄音 @property (weak, nonatomic) IBOutlet UIButton *resume;//恢復錄音 @property (weak, nonatomic) IBOutlet UIButton *stop;//停止錄音 @property (weak, nonatomic) IBOutlet UIProgressView *audioPower;//音頻波動@end@implementation ViewController#pragma mark - 控制器視圖方法 - (void)viewDidLoad {[super viewDidLoad];[self setAudioSession]; }#pragma mark - 私有方法 /*** 設置音頻會話*/ -(void)setAudioSession{AVAudioSession *audioSession=[AVAudioSession sharedInstance];//設置為播放和錄音狀態(tài),以便可以在錄制完之后播放錄音[audioSession setCategory:AVAudioSessionCategoryPlayAndRecord error:nil];[audioSession setActive:YES error:nil]; }/*** 取得錄音文件保存路徑** @return 錄音文件路徑*/ -(NSURL *)getSavePath{NSString *urlStr=[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];urlStr=[urlStr stringByAppendingPathComponent:kRecordAudioFile];NSLog(@"file path:%@",urlStr);NSURL *url=[NSURL fileURLWithPath:urlStr];return url; }/*** 取得錄音文件設置** @return 錄音設置*/ -(NSDictionary *)getAudioSetting{NSMutableDictionary *dicM=[NSMutableDictionary dictionary];//設置錄音格式[dicM setObject:@(kAudioFormatLinearPCM) forKey:AVFormatIDKey];//設置錄音采樣率,8000是電話采樣率,對于一般錄音已經(jīng)夠了[dicM setObject:@(8000) forKey:AVSampleRateKey];//設置通道,這里采用單聲道[dicM setObject:@(1) forKey:AVNumberOfChannelsKey];//每個采樣點位數(shù),分為8、16、24、32[dicM setObject:@(8) forKey:AVLinearPCMBitDepthKey];//是否使用浮點數(shù)采樣[dicM setObject:@(YES) forKey:AVLinearPCMIsFloatKey];//....其他設置等return dicM; }/*** 獲得錄音機對象** @return 錄音機對象*/ -(AVAudioRecorder *)audioRecorder{if (!_audioRecorder) {//創(chuàng)建錄音文件保存路徑NSURL *url=[self getSavePath];//創(chuàng)建錄音格式設置NSDictionary *setting=[self getAudioSetting];//創(chuàng)建錄音機NSError *error=nil;_audioRecorder=[[AVAudioRecorder alloc]initWithURL:url settings:setting error:&error];_audioRecorder.delegate=self;_audioRecorder.meteringEnabled=YES;//如果要監(jiān)控聲波則必須設置為YESif (error) {NSLog(@"創(chuàng)建錄音機對象時發(fā)生錯誤,錯誤信息:%@",error.localizedDescription);return nil;}}return _audioRecorder; }/*** 創(chuàng)建播放器** @return 播放器*/ -(AVAudioPlayer *)audioPlayer{if (!_audioPlayer) {NSURL *url=[self getSavePath];NSError *error=nil;_audioPlayer=[[AVAudioPlayer alloc]initWithContentsOfURL:url error:&error];_audioPlayer.numberOfLoops=0;[_audioPlayer prepareToPlay];if (error) {NSLog(@"創(chuàng)建播放器過程中發(fā)生錯誤,錯誤信息:%@",error.localizedDescription);return nil;}}return _audioPlayer; }/*** 錄音聲波監(jiān)控定制器** @return 定時器*/ -(NSTimer *)timer{if (!_timer) {_timer=[NSTimer scheduledTimerWithTimeInterval:0.1f target:self selector:@selector(audioPowerChange) userInfo:nil repeats:YES];}return _timer; }/*** 錄音聲波狀態(tài)設置*/ -(void)audioPowerChange{[self.audioRecorder updateMeters];//更新測量值float power= [self.audioRecorder averagePowerForChannel:0];//取得第一個通道的音頻,注意音頻強度范圍時-160到0CGFloat progress=(1.0/160.0)*(power+160.0);[self.audioPower setProgress:progress]; } #pragma mark - UI事件 /*** 點擊錄音按鈕** @param sender 錄音按鈕*/ - (IBAction)recordClick:(UIButton *)sender {if (![self.audioRecorder isRecording]) {[self.audioRecorder record];//首次使用應用時如果調用record方法會詢問用戶是否允許使用麥克風self.timer.fireDate=[NSDate distantPast];} }/*** 點擊暫定按鈕** @param sender 暫停按鈕*/ - (IBAction)pauseClick:(UIButton *)sender {if ([self.audioRecorder isRecording]) {[self.audioRecorder pause];self.timer.fireDate=[NSDate distantFuture];} }/*** 點擊恢復按鈕* 恢復錄音只需要再次調用record,AVAudioSession會幫助你記錄上次錄音位置并追加錄音** @param sender 恢復按鈕*/ - (IBAction)resumeClick:(UIButton *)sender {[self recordClick:sender]; }/*** 點擊停止按鈕** @param sender 停止按鈕*/ - (IBAction)stopClick:(UIButton *)sender {[self.audioRecorder stop];self.timer.fireDate=[NSDate distantFuture];self.audioPower.progress=0.0; }#pragma mark - 錄音機代理方法 /*** 錄音完成,錄音完成后播放錄音** @param recorder 錄音機對象* @param flag 是否成功*/ -(void)audioRecorderDidFinishRecording:(AVAudioRecorder *)recorder successfully:(BOOL)flag{if (![self.audioPlayer isPlaying]) {[self.audioPlayer play];}NSLog(@"錄音完成!"); }@end

    運行效果:

    音頻隊列服務

    大家應該已經(jīng)注意到了,無論是前面的錄音還是音頻播放均不支持網(wǎng)絡流媒體播放,當然對于錄音來說這種需求可能不大,但是對于音頻播放來說有時候就很有必要了。AVAudioPlayer只能播放本地文件,并且是一次性加載所以音頻數(shù)據(jù),初始化AVAudioPlayer時指定的URL也只能是File URL而不能是HTTP URL。當然,將音頻文件下載到本地然后再調用AVAudioPlayer來播放也是一種播放網(wǎng)絡音頻的辦法,但是這種方式最大的弊端就是必須等到整個音頻播放完成才能播放,而不能使用流式播放,這往往在實際開發(fā)中是不切實際的。那么在iOS中如何播放網(wǎng)絡流媒體呢?就是使用AudioToolbox框架中的音頻隊列服務Audio Queue Services。

    使用音頻隊列服務完全可以做到音頻播放和錄制,首先看一下錄音音頻服務隊列:

    一個音頻服務隊列Audio Queue有三部分組成:

    三個緩沖器Buffers:每個緩沖器都是一個存儲音頻數(shù)據(jù)的臨時倉庫。

    一個緩沖隊列Buffer Queue:一個包含音頻緩沖器的有序隊列。

    一個回調Callback:一個自定義的隊列回調函數(shù)。

    聲音通過輸入設備進入緩沖隊列中,首先填充第一個緩沖器;當?shù)谝粋€緩沖器填充滿之后自動填充下一個緩沖器,同時會調用回調函數(shù);在回調函數(shù)中需要將緩沖器中的音頻數(shù)據(jù)寫入磁盤,同時將緩沖器放回到緩沖隊列中以便重用。下面是Apple官方關于音頻隊列服務的流程示意圖:

    類似的,看一下音頻播放緩沖隊列,其組成部分和錄音緩沖隊列類似。

    但是在音頻播放緩沖隊列中,回調函數(shù)調用的時機不同于音頻錄制緩沖隊列,流程剛好相反。將音頻讀取到緩沖器中,一旦一個緩沖器填充滿之后就放到緩沖隊列中,然后繼續(xù)填充其他緩沖器;當開始播放時,則從第一個緩沖器中讀取音頻進行播放;一旦播放完之后就會觸發(fā)回調函數(shù),開始播放下一個緩沖器中的音頻,同時填充第一個緩沖器放;填充滿之后再次放回到緩沖隊列。下面是詳細的流程:

    當然,要明白音頻隊列服務的原理并不難,問題是如何實現(xiàn)這個自定義的回調函數(shù),這其中我們有大量的工作要做,控制播放狀態(tài)、處理異常中斷、進行音頻編碼等等。由于牽扯內容過多,而且不是本文目的,如果以后有時間將另開一篇文章重點介紹,目前有很多第三方優(yōu)秀框架可以直接使用,例如AudioStreamer、FreeStreamer。由于前者當前只有非ARC版本,所以下面不妨使用FreeStreamer來簡單演示在線音頻播放的過程,當然在使用之前要做如下準備工作:

    1.拷貝FreeStreamer中的Reachability.h、Reachability.m和Common、astreamer兩個文件夾中的內容到項目中。

    2.添加FreeStreamer使用的類庫:CFNetwork.framework、AudioToolbox.framework、AVFoundation.framework
    、libxml2.dylib、MediaPlayer.framework。

    3.如果引用libxml2.dylib編譯不通過,需要在Xcode的Targets-Build Settings-Header Build Path中添加$(SDKROOT)/usr/include/libxml2。

    4.將FreeStreamer中的FreeStreamerMobile-Prefix.pch文件添加到項目中并將Targets-Build Settings-Precompile Prefix Header設置為YES,在Targets-Build Settings-Prefix Header設置為$(SRCROOT)/項目名稱/FreeStreamerMobile-Prefix.pch(因為Xcode6默認沒有pch文件)

    然后就可以編寫代碼播放網(wǎng)絡音頻了:

    // // ViewController.m // AudioQueueServices // // Created by Kenshin Cui on 14/03/30. // Copyright (c) 2014年 cmjstudio. All rights reserved. // 使用FreeStreamer實現(xiàn)網(wǎng)絡音頻播放#import "ViewController.h" #import "FSAudioStream.h"@interface ViewController ()@property (nonatomic,strong) FSAudioStream *audioStream;@end@implementation ViewController- (void)viewDidLoad {[super viewDidLoad];[self.audioStream play]; }/*** 取得本地文件路徑** @return 文件路徑*/ -(NSURL *)getFileUrl{NSString *urlStr=[[NSBundle mainBundle]pathForResource:@"劉若英 - 原來你也在這里.mp3" ofType:nil];NSURL *url=[NSURL fileURLWithPath:urlStr];return url; } -(NSURL *)getNetworkUrl{NSString *urlStr=@"http://192.168.1.102/liu.mp3";NSURL *url=[NSURL URLWithString:urlStr];return url; }/*** 創(chuàng)建FSAudioStream對象** @return FSAudioStream對象*/ -(FSAudioStream *)audioStream{if (!_audioStream) {NSURL *url=[self getNetworkUrl];//創(chuàng)建FSAudioStream對象_audioStream=[[FSAudioStream alloc]initWithUrl:url];_audioStream.onFailure=^(FSAudioStreamError error,NSString *description){NSLog(@"播放過程中發(fā)生錯誤,錯誤信息:%@",description);};_audioStream.onCompletion=^(){NSLog(@"播放完成!");};[_audioStream setVolume:0.5];//設置聲音}return _audioStream; }@end 其實FreeStreamer的功能很強大,不僅僅是播放本地、網(wǎng)絡音頻那么簡單,它還支持播放列表、檢查包內容、RSS訂閱、播放中斷等很多強大的功能,甚至還包含了一個音頻分析器,有興趣的朋友可以訪問官網(wǎng)查看詳細用法

    視頻

    MPMoviePlayerController

    在iOS中播放視頻可以使用MediaPlayer.framework種的MPMoviePlayerController類來完成,它支持本地視頻和網(wǎng)絡視頻播放。這個類實現(xiàn)了MPMediaPlayback協(xié)議,因此具備一般的播放器控制功能,例如播放、暫停、停止等。但是MPMediaPlayerController自身并不是一個完整的視圖控制器,如果要在UI中展示視頻需要將view屬性添加到界面中。下面列出了MPMoviePlayerController的常用屬性和方法:

    屬性說明
    @property (nonatomic, copy) NSURL *contentURL播放媒體URL,這個URL可以是本地路徑,也可以是網(wǎng)絡路徑
    @property (nonatomic, readonly) UIView *view播放器視圖,如果要顯示視頻必須將此視圖添加到控制器視圖中
    @property (nonatomic, readonly) UIView *backgroundView播放器背景視圖
    @property (nonatomic, readonly) MPMoviePlaybackState playbackState媒體播放狀態(tài),枚舉類型:
    MPMoviePlaybackStateStopped:停止播放
    MPMoviePlaybackStatePlaying:正在播放
    MPMoviePlaybackStatePaused:暫停
    MPMoviePlaybackStateInterrupted:中斷
    MPMoviePlaybackStateSeekingForward:向前定位
    MPMoviePlaybackStateSeekingBackward:向后定位
    @property (nonatomic, readonly) MPMovieLoadState loadState網(wǎng)絡媒體加載狀態(tài),枚舉類型:
    MPMovieLoadStateUnknown:位置類型
    MPMovieLoadStatePlayable:
    MPMovieLoadStatePlaythroughOK:這種狀態(tài)如果shouldAutoPlay為YES將自動播放
    MPMovieLoadStateStalled:停滯狀態(tài)
    @property (nonatomic) MPMovieControlStyle controlStyle控制面板風格,枚舉類型:
    MPMovieControlStyleNone:無控制面板?
    MPMovieControlStyleEmbedded:嵌入視頻風格?
    MPMovieControlStyleFullscreen:全屏?
    MPMovieControlStyleDefault:默認風格
    @property (nonatomic) MPMovieRepeatMode repeatMode;重復播放模式,枚舉類型:
    MPMovieRepeatModeNone:不重復,默認值
    MPMovieRepeatModeOne:重復播放
    @property (nonatomic) BOOL shouldAutoplay當網(wǎng)絡媒體緩存到一定數(shù)據(jù)時是否自動播放,默認為YES
    @property (nonatomic, getter=isFullscreen) BOOL fullscreen是否全屏展示,默認為NO,注意如果要通過此屬性設置全屏必須在視圖顯示完成后設置,否則無效
    @property (nonatomic) MPMovieScalingMode scalingMode視頻縮放填充模式,枚舉類型:
    MPMovieScalingModeNone:不進行任何縮放
    MPMovieScalingModeAspectFit:固定縮放比例并且盡量全部展示視頻,不會裁切視頻
    MPMovieScalingModeAspectFill:固定縮放比例并填充滿整個視圖展示,可能會裁切視頻
    MPMovieScalingModeFill:不固定縮放比例壓縮填充整個視圖,視頻不會被裁切但是比例失衡
    @property (nonatomic, readonly) BOOL readyForDisplay是否有相關媒體被播放
    @property (nonatomic, readonly) MPMovieMediaTypeMask movieMediaTypes媒體類別,枚舉類型:
    MPMovieMediaTypeMaskNone:未知類型
    MPMovieMediaTypeMaskVideo:視頻
    MPMovieMediaTypeMaskAudio:音頻
    @property (nonatomic) MPMovieSourceType movieSourceType媒體源,枚舉類型:
    MPMovieSourceTypeUnknown:未知來源
    MPMovieSourceTypeFile:本地文件
    MPMovieSourceTypeStreaming:流媒體(直播或點播)
    @property (nonatomic, readonly) NSTimeInterval duration媒體時長,如果未知則返回0
    @property (nonatomic, readonly) NSTimeInterval playableDuration媒體可播放時長,主要用于表示網(wǎng)絡媒體已下載視頻時長
    @property (nonatomic, readonly) CGSize naturalSize視頻實際尺寸,如果未知則返回CGSizeZero
    @property (nonatomic) NSTimeInterval initialPlaybackTime起始播放時間
    @property (nonatomic) NSTimeInterval endPlaybackTime終止播放時間
    @property (nonatomic) BOOL allowsAirPlay是否允許無線播放,默認為YES
    @property (nonatomic, readonly, getter=isAirPlayVideoActive) BOOL airPlayVideoActive當前媒體是否正在通過AirPlay播放
    @property(nonatomic, readonly) BOOL isPreparedToPlay是否準備好播放
    @property(nonatomic) NSTimeInterval currentPlaybackTime當前播放時間,單位:秒
    @property(nonatomic) float currentPlaybackRate當前播放速度,如果暫停則為0,正常速度為1.0,非0數(shù)據(jù)表示倍率
    對象方法說明
    - (instancetype)initWithContentURL:(NSURL *)url使用指定的URL初始化媒體播放控制器對象
    - (void)setFullscreen:(BOOL)fullscreen animated:(BOOL)animated設置視頻全屏,注意如果要通過此方法設置全屏則必須在其視圖顯示之后設置,否則無效
    - (void)requestThumbnailImagesAtTimes:(NSArray *)playbackTimes timeOption:(MPMovieTimeOption)option獲取在指定播放時間的視頻縮略圖,第一個參數(shù)是獲取縮略圖的時間點數(shù)組;第二個參數(shù)代表時間點精度,枚舉類型:
    MPMovieTimeOptionNearestKeyFrame:時間點附近
    MPMovieTimeOptionExact:準確時間
    - (void)cancelAllThumbnailImageRequests取消所有縮略圖獲取請求
    - (void)prepareToPlay準備播放,加載視頻數(shù)據(jù)到緩存,當調用play方法時如果沒有準備好會自動調用此方法
    - (void)play開始播放
    - (void)pause暫停播放
    - (void)stop停止播放
    - (void)beginSeekingForward向前定位
    - (void)beginSeekingBackward向后定位
    - (void)endSeeking停止快進/快退
    通知說明
    MPMoviePlayerScalingModeDidChangeNotification視頻縮放填充模式發(fā)生改變
    MPMoviePlayerPlaybackDidFinishNotification媒體播放完成或用戶手動退出,具體完成原因可以通過通知userInfo中的key為MPMoviePlayerPlaybackDidFinishReasonUserInfoKey的對象獲取
    MPMoviePlayerPlaybackStateDidChangeNotification播放狀態(tài)改變,可配合playbakcState屬性獲取具體狀態(tài)
    MPMoviePlayerLoadStateDidChangeNotification媒體網(wǎng)絡加載狀態(tài)改變
    MPMoviePlayerNowPlayingMovieDidChangeNotification當前播放的媒體內容發(fā)生改變
    MPMoviePlayerWillEnterFullscreenNotification將要進入全屏
    MPMoviePlayerDidEnterFullscreenNotification進入全屏后
    MPMoviePlayerWillExitFullscreenNotification將要退出全屏
    MPMoviePlayerDidExitFullscreenNotification退出全屏后
    MPMoviePlayerIsAirPlayVideoActiveDidChangeNotification當媒體開始通過AirPlay播放或者結束AirPlay播放
    MPMoviePlayerReadyForDisplayDidChangeNotification視頻顯示狀態(tài)改變
    MPMovieMediaTypesAvailableNotification確定了媒體可用類型后
    MPMovieSourceTypeAvailableNotification確定了媒體來源后
    MPMovieDurationAvailableNotification確定了媒體播放時長后
    MPMovieNaturalSizeAvailableNotification確定了媒體的實際尺寸后
    MPMoviePlayerThumbnailImageRequestDidFinishNotification縮略圖請求完成之后
    MPMediaPlaybackIsPreparedToPlayDidChangeNotification做好播放準備后

    注意MPMediaPlayerController的狀態(tài)等信息并不是通過代理來和外界交互的,而是通過通知中心,因此從上面的列表中可以看到常用的一些通知。由于MPMoviePlayerController本身對于媒體播放做了深度的封裝,使用起來就相當簡單:創(chuàng)建MPMoviePlayerController對象,設置frame屬性,將MPMoviePlayerController的view添加到控制器視圖中。下面的示例中將創(chuàng)建一個播放控制器并添加播放狀態(tài)改變及播放完成的通知:

    // // ViewController.m // MPMoviePlayerController // // Created by Kenshin Cui on 14/03/30. // Copyright (c) 2014年 cmjstudio. All rights reserved. //#import "ViewController.h" #import <MediaPlayer/MediaPlayer.h>@interface ViewController ()@property (nonatomic,strong) MPMoviePlayerController *moviePlayer;//視頻播放控制器@end@implementation ViewController#pragma mark - 控制器視圖方法 - (void)viewDidLoad {[super viewDidLoad];//播放[self.moviePlayer play];//添加通知[self addNotification];}-(void)dealloc{//移除所有通知監(jiān)控[[NSNotificationCenter defaultCenter] removeObserver:self]; }#pragma mark - 私有方法 /*** 取得本地文件路徑** @return 文件路徑*/ -(NSURL *)getFileUrl{NSString *urlStr=[[NSBundle mainBundle] pathForResource:@"The New Look of OS X Yosemite.mp4" ofType:nil];NSURL *url=[NSURL fileURLWithPath:urlStr];return url; }/*** 取得網(wǎng)絡文件路徑** @return 文件路徑*/ -(NSURL *)getNetworkUrl{NSString *urlStr=@"http://192.168.1.161/The New Look of OS X Yosemite.mp4";urlStr=[urlStr stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];NSURL *url=[NSURL URLWithString:urlStr];return url; }/*** 創(chuàng)建媒體播放控制器** @return 媒體播放控制器*/ -(MPMoviePlayerController *)moviePlayer{if (!_moviePlayer) {NSURL *url=[self getNetworkUrl];_moviePlayer=[[MPMoviePlayerController alloc]initWithContentURL:url];_moviePlayer.view.frame=self.view.bounds;_moviePlayer.view.autoresizingMask=UIViewAutoresizingFlexibleWidth|UIViewAutoresizingFlexibleHeight;[self.view addSubview:_moviePlayer.view];}return _moviePlayer; }/*** 添加通知監(jiān)控媒體播放控制器狀態(tài)*/ -(void)addNotification{NSNotificationCenter *notificationCenter=[NSNotificationCenter defaultCenter];[notificationCenter addObserver:self selector:@selector(mediaPlayerPlaybackStateChange:) name:MPMoviePlayerPlaybackStateDidChangeNotification object:self.moviePlayer];[notificationCenter addObserver:self selector:@selector(mediaPlayerPlaybackFinished:) name:MPMoviePlayerPlaybackDidFinishNotification object:self.moviePlayer];}/*** 播放狀態(tài)改變,注意播放完成時的狀態(tài)是暫停** @param notification 通知對象*/ -(void)mediaPlayerPlaybackStateChange:(NSNotification *)notification{switch (self.moviePlayer.playbackState) {case MPMoviePlaybackStatePlaying:NSLog(@"正在播放...");break;case MPMoviePlaybackStatePaused:NSLog(@"暫停播放.");break;case MPMoviePlaybackStateStopped:NSLog(@"停止播放.");break;default:NSLog(@"播放狀態(tài):%li",self.moviePlayer.playbackState);break;} }/*** 播放完成** @param notification 通知對象*/ -(void)mediaPlayerPlaybackFinished:(NSNotification *)notification{NSLog(@"播放完成.%li",self.moviePlayer.playbackState); }@end

    運行效果:


    從上面的API大家也不難看出其實MPMoviePlayerController功能相當強大,日常開發(fā)中作為一般的媒體播放器也完全沒有問題。MPMoviePlayerController除了一般的視頻播放和控制外還有一些強大的功能,例如截取視頻縮略圖。請求視頻縮略圖時只要調用- (void)requestThumbnailImagesAtTimes:(NSArray *)playbackTimes timeOption:(MPMovieTimeOption)option方法指定獲得縮略圖的時間點,然后監(jiān)控MPMoviePlayerThumbnailImageRequestDidFinishNotification通知,每個時間點的縮略圖請求完成就會調用通知,在通知調用方法中可以通過MPMoviePlayerThumbnailImageKey獲得UIImage對象處理即可。例如下面的程序演示了在程序啟動后獲得兩個時間點的縮略圖的過程,截圖成功后保存到相冊:

    // // ViewController.m // MPMoviePlayerController // // Created by Kenshin Cui on 14/03/30. // Copyright (c) 2014年 cmjstudio. All rights reserved. // 視頻截圖#import "ViewController.h" #import <MediaPlayer/MediaPlayer.h>@interface ViewController ()@property (nonatomic,strong) MPMoviePlayerController *moviePlayer;//視頻播放控制器@end@implementation ViewController#pragma mark - 控制器視圖方法 - (void)viewDidLoad {[super viewDidLoad];//播放[self.moviePlayer play];//添加通知[self addNotification];//獲取縮略圖[self thumbnailImageRequest]; }-(void)dealloc{//移除所有通知監(jiān)控[[NSNotificationCenter defaultCenter] removeObserver:self]; }#pragma mark - 私有方法 /*** 取得本地文件路徑** @return 文件路徑*/ -(NSURL *)getFileUrl{NSString *urlStr=[[NSBundle mainBundle] pathForResource:@"The New Look of OS X Yosemite.mp4" ofType:nil];NSURL *url=[NSURL fileURLWithPath:urlStr];return url; }/*** 取得網(wǎng)絡文件路徑** @return 文件路徑*/ -(NSURL *)getNetworkUrl{NSString *urlStr=@"http://192.168.1.161/The New Look of OS X Yosemite.mp4";urlStr=[urlStr stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];NSURL *url=[NSURL URLWithString:urlStr];return url; }/*** 創(chuàng)建媒體播放控制器** @return 媒體播放控制器*/ -(MPMoviePlayerController *)moviePlayer{if (!_moviePlayer) {NSURL *url=[self getNetworkUrl];_moviePlayer=[[MPMoviePlayerController alloc]initWithContentURL:url];_moviePlayer.view.frame=self.view.bounds;_moviePlayer.view.autoresizingMask=UIViewAutoresizingFlexibleWidth|UIViewAutoresizingFlexibleHeight;[self.view addSubview:_moviePlayer.view];}return _moviePlayer; }/*** 獲取視頻縮略圖*/ -(void)thumbnailImageRequest{//獲取13.0s、21.5s的縮略圖[self.moviePlayer requestThumbnailImagesAtTimes:@[@13.0,@21.5] timeOption:MPMovieTimeOptionNearestKeyFrame]; }#pragma mark - 控制器通知 /*** 添加通知監(jiān)控媒體播放控制器狀態(tài)*/ -(void)addNotification{NSNotificationCenter *notificationCenter=[NSNotificationCenter defaultCenter];[notificationCenter addObserver:self selector:@selector(mediaPlayerPlaybackStateChange:) name:MPMoviePlayerPlaybackStateDidChangeNotification object:self.moviePlayer];[notificationCenter addObserver:self selector:@selector(mediaPlayerPlaybackFinished:) name:MPMoviePlayerPlaybackDidFinishNotification object:self.moviePlayer];[notificationCenter addObserver:self selector:@selector(mediaPlayerThumbnailRequestFinished:) name:MPMoviePlayerThumbnailImageRequestDidFinishNotification object:self.moviePlayer];}/*** 播放狀態(tài)改變,注意播放完成時的狀態(tài)是暫停** @param notification 通知對象*/ -(void)mediaPlayerPlaybackStateChange:(NSNotification *)notification{switch (self.moviePlayer.playbackState) {case MPMoviePlaybackStatePlaying:NSLog(@"正在播放...");break;case MPMoviePlaybackStatePaused:NSLog(@"暫停播放.");break;case MPMoviePlaybackStateStopped:NSLog(@"停止播放.");break;default:NSLog(@"播放狀態(tài):%li",self.moviePlayer.playbackState);break;} }/*** 播放完成** @param notification 通知對象*/ -(void)mediaPlayerPlaybackFinished:(NSNotification *)notification{NSLog(@"播放完成.%li",self.moviePlayer.playbackState); }/*** 縮略圖請求完成,此方法每次截圖成功都會調用一次** @param notification 通知對象*/ -(void)mediaPlayerThumbnailRequestFinished:(NSNotification *)notification{NSLog(@"視頻截圖完成.");UIImage *image=notification.userInfo[MPMoviePlayerThumbnailImageKey];//保存圖片到相冊(首次調用會請求用戶獲得訪問相冊權限)UIImageWriteToSavedPhotosAlbum(image, nil, nil, nil); }@end

    截圖效果:

    ?????

    擴展--使用AVFoundation生成縮略圖

    通過前面的方法大家應該已經(jīng)看到,使用MPMoviePlayerController來生成縮略圖足夠簡單,但是如果僅僅是是為了生成縮略圖而不進行視頻播放的話,此刻使用MPMoviePlayerController就有點大材小用了。其實使用AVFundation框架中的AVAssetImageGenerator就可以獲取視頻縮略圖。使用AVAssetImageGenerator獲取縮略圖大致分為三個步驟:

  • 創(chuàng)建AVURLAsset對象(此類主要用于獲取媒體信息,包括視頻、聲音等)。
  • 根據(jù)AVURLAsset創(chuàng)建AVAssetImageGenerator對象。
  • 使用AVAssetImageGenerator的copyCGImageAtTime::方法獲得指定時間點的截圖。
  • // // ViewController.m // AVAssetImageGenerator // // Created by Kenshin Cui on 14/03/30. // Copyright (c) 2014年 cmjstudio. All rights reserved. //#import "ViewController.h" #import <AVFoundation/AVFoundation.h>@interface ViewController ()@end@implementation ViewController- (void)viewDidLoad {[super viewDidLoad];//獲取第13.0s的縮略圖[self thumbnailImageRequest:13.0]; }#pragma mark - 私有方法 /*** 取得本地文件路徑** @return 文件路徑*/ -(NSURL *)getFileUrl{NSString *urlStr=[[NSBundle mainBundle] pathForResource:@"The New Look of OS X Yosemite.mp4" ofType:nil];NSURL *url=[NSURL fileURLWithPath:urlStr];return url; }/*** 取得網(wǎng)絡文件路徑** @return 文件路徑*/ -(NSURL *)getNetworkUrl{NSString *urlStr=@"http://192.168.1.161/The New Look of OS X Yosemite.mp4";urlStr=[urlStr stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];NSURL *url=[NSURL URLWithString:urlStr];return url; }/*** 截取指定時間的視頻縮略圖** @param timeBySecond 時間點*/ -(void)thumbnailImageRequest:(CGFloat )timeBySecond{//創(chuàng)建URLNSURL *url=[self getNetworkUrl];//根據(jù)url創(chuàng)建AVURLAssetAVURLAsset *urlAsset=[AVURLAsset assetWithURL:url];//根據(jù)AVURLAsset創(chuàng)建AVAssetImageGeneratorAVAssetImageGenerator *imageGenerator=[AVAssetImageGenerator assetImageGeneratorWithAsset:urlAsset];/*截圖* requestTime:縮略圖創(chuàng)建時間* actualTime:縮略圖實際生成的時間*/NSError *error=nil;CMTime time=CMTimeMakeWithSeconds(timeBySecond, 10);//CMTime是表示電影時間信息的結構體,第一個參數(shù)表示是視頻第幾秒,第二個參數(shù)表示每秒幀數(shù).(如果要活的某一秒的第幾幀可以使用CMTimeMake方法)CMTime actualTime;CGImageRef cgImage= [imageGenerator copyCGImageAtTime:time actualTime:&actualTime error:&error];if(error){NSLog(@"截取視頻縮略圖時發(fā)生錯誤,錯誤信息:%@",error.localizedDescription);return;}CMTimeShow(actualTime);UIImage *image=[UIImage imageWithCGImage:cgImage];//轉化為UIImage//保存到相冊UIImageWriteToSavedPhotosAlbum(image,nil, nil, nil);CGImageRelease(cgImage); }@end

    生成的縮略圖效果:

    MPMoviePlayerViewController

    其實MPMoviePlayerController如果不作為嵌入視頻來播放(例如在新聞中嵌入一個視頻),通常在播放時都是占滿一個屏幕的,特別是在iPhone、iTouch上。因此從iOS3.2以后蘋果也在思考既然MPMoviePlayerController在使用時通常都是將其視圖view添加到另外一個視圖控制器中作為子視圖,那么何不直接創(chuàng)建一個控制器視圖內部創(chuàng)建一個MPMoviePlayerController屬性并且默認全屏播放,開發(fā)者在開發(fā)的時候直接使用這個視圖控制器。這個內部有一個MPMoviePlayerController的視圖控制器就是MPMoviePlayerViewController,它繼承于UIViewController。MPMoviePlayerViewController內部多了一個moviePlayer屬性和一個帶有url的初始化方法,同時它內部實現(xiàn)了一些作為模態(tài)視圖展示所特有的功能,例如默認是全屏模式展示、彈出后自動播放、作為模態(tài)窗口展示時如果點擊“Done”按鈕會自動退出模態(tài)窗口等。在下面的示例中就不直接將播放器放到主視圖控制器,而是放到一個模態(tài)視圖控制器中,簡單演示MPMoviePlayerViewController的使用。

    // // ViewController.m // MPMoviePlayerViewController // // Created by Kenshin Cui on 14/03/30. // Copyright (c) 2014年 cmjstudio. All rights reserved. // MPMoviePlayerViewController使用#import "ViewController.h" #import <MediaPlayer/MediaPlayer.h>@interface ViewController ()//播放器視圖控制器 @property (nonatomic,strong) MPMoviePlayerViewController *moviePlayerViewController;@end@implementation ViewController#pragma mark - 控制器視圖方法 - (void)viewDidLoad {[super viewDidLoad];}-(void)dealloc{//移除所有通知監(jiān)控[[NSNotificationCenter defaultCenter] removeObserver:self]; }#pragma mark - 私有方法 /*** 取得本地文件路徑** @return 文件路徑*/ -(NSURL *)getFileUrl{NSString *urlStr=[[NSBundle mainBundle] pathForResource:@"The New Look of OS X Yosemite.mp4" ofType:nil];NSURL *url=[NSURL fileURLWithPath:urlStr];return url; }/*** 取得網(wǎng)絡文件路徑** @return 文件路徑*/ -(NSURL *)getNetworkUrl{NSString *urlStr=@"http://192.168.1.161/The New Look of OS X Yosemite.mp4";urlStr=[urlStr stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];NSURL *url=[NSURL URLWithString:urlStr];return url; }-(MPMoviePlayerViewController *)moviePlayerViewController{if (!_moviePlayerViewController) {NSURL *url=[self getNetworkUrl];_moviePlayerViewController=[[MPMoviePlayerViewController alloc]initWithContentURL:url];[self addNotification];}return _moviePlayerViewController; } #pragma mark - UI事件 - (IBAction)playClick:(UIButton *)sender {self.moviePlayerViewController=nil;//保證每次點擊都重新創(chuàng)建視頻播放控制器視圖,避免再次點擊時由于不播放的問題 // [self presentViewController:self.moviePlayerViewController animated:YES completion:nil];//注意,在MPMoviePlayerViewController.h中對UIViewController擴展兩個用于模態(tài)展示和關閉MPMoviePlayerViewController的方法,增加了一種下拉展示動畫效果[self presentMoviePlayerViewControllerAnimated:self.moviePlayerViewController]; }#pragma mark - 控制器通知 /*** 添加通知監(jiān)控媒體播放控制器狀態(tài)*/ -(void)addNotification{NSNotificationCenter *notificationCenter=[NSNotificationCenter defaultCenter];[notificationCenter addObserver:self selector:@selector(mediaPlayerPlaybackStateChange:) name:MPMoviePlayerPlaybackStateDidChangeNotification object:self.moviePlayerViewController.moviePlayer];[notificationCenter addObserver:self selector:@selector(mediaPlayerPlaybackFinished:) name:MPMoviePlayerPlaybackDidFinishNotification object:self.moviePlayerViewController.moviePlayer];}/*** 播放狀態(tài)改變,注意播放完成時的狀態(tài)是暫停** @param notification 通知對象*/ -(void)mediaPlayerPlaybackStateChange:(NSNotification *)notification{switch (self.moviePlayerViewController.moviePlayer.playbackState) {case MPMoviePlaybackStatePlaying:NSLog(@"正在播放...");break;case MPMoviePlaybackStatePaused:NSLog(@"暫停播放.");break;case MPMoviePlaybackStateStopped:NSLog(@"停止播放.");break;default:NSLog(@"播放狀態(tài):%li",self.moviePlayerViewController.moviePlayer.playbackState);break;} }/*** 播放完成** @param notification 通知對象*/ -(void)mediaPlayerPlaybackFinished:(NSNotification *)notification{NSLog(@"播放完成.%li",self.moviePlayerViewController.moviePlayer.playbackState); }@end

    運行效果:

    這里需要強調一下,由于MPMoviePlayerViewController的初始化方法做了大量工作(例如設置URL、自動播放、添加點擊Done完成的監(jiān)控等),所以當再次點擊播放彈出新的模態(tài)窗口的時如果不銷毀之前的MPMoviePlayerViewController,那么新的對象就無法完成初始化,這樣也就不能再次進行播放。

    AVPlayer

    MPMoviePlayerController足夠強大,幾乎不用寫幾行代碼就能完成一個播放器,但是正是由于它的高度封裝使得要自定義這個播放器變得很復雜,甚至是不可能完成。例如有些時候需要自定義播放器的樣式,那么如果要使用MPMoviePlayerController就不合適了,如果要對視頻有自由的控制則可以使用AVPlayer。AVPlayer存在于AVFoundation中,它更加接近于底層,所以靈活性也更強:

    AVPlayer本身并不能顯示視頻,而且它也不像MPMoviePlayerController有一個view屬性。如果AVPlayer要顯示必須創(chuàng)建一個播放器層AVPlayerLayer用于展示,播放器層繼承于CALayer,有了AVPlayerLayer之添加到控制器視圖的layer中即可。要使用AVPlayer首先了解一下幾個常用的類:

    AVAsset:主要用于獲取多媒體信息,是一個抽象類,不能直接使用。

    AVURLAsset:AVAsset的子類,可以根據(jù)一個URL路徑創(chuàng)建一個包含媒體信息的AVURLAsset對象。

    AVPlayerItem:一個媒體資源管理對象,管理者視頻的一些基本信息和狀態(tài),一個AVPlayerItem對應著一個視頻資源。

    下面簡單通過一個播放器來演示AVPlayer的使用,播放器的效果如下:

    在這個自定義的播放器中實現(xiàn)了視頻播放、暫停、進度展示和視頻列表功能,下面將對這些功能一一介紹。

    首先說一下視頻的播放、暫停功能,這也是最基本的功能,AVPlayer對應著兩個方法play、pause來實現(xiàn)。但是關鍵問題是如何判斷當前視頻是否在播放,在前面的內容中無論是音頻播放器還是視頻播放器都有對應的狀態(tài)來判斷,但是AVPlayer卻沒有這樣的狀態(tài)屬性,通常情況下可以通過判斷播放器的播放速度來獲得播放狀態(tài)。如果rate為0說明是停止狀態(tài),1是則是正常播放狀態(tài)。

    其次要展示播放進度就沒有其他播放器那么簡單了。在前面的播放器中通常是使用通知來獲得播放器的狀態(tài),媒體加載狀態(tài)等,但是無論是AVPlayer還是AVPlayerItem(AVPlayer有一個屬性currentItem是AVPlayerItem類型,表示當前播放的視頻對象)都無法獲得這些信息。當然AVPlayerItem是有通知的,但是對于獲得播放狀態(tài)和加載狀態(tài)有用的通知只有一個:播放完成通知AVPlayerItemDidPlayToEndTimeNotification。在播放視頻時,特別是播放網(wǎng)絡視頻往往需要知道視頻加載情況、緩沖情況、播放情況,這些信息可以通過KVO監(jiān)控AVPlayerItem的status、loadedTimeRanges屬性來獲得。當AVPlayerItem的status屬性為AVPlayerStatusReadyToPlay是說明正在播放,只有處于這個狀態(tài)時才能獲得視頻時長等信息;當loadedTimeRanges的改變時(每緩沖一部分數(shù)據(jù)就會更新此屬性)可以獲得本次緩沖加載的視頻范圍(包含起始時間、本次加載時長),這樣一來就可以實時獲得緩沖情況。然后就是依靠AVPlayer的- (id)addPeriodicTimeObserverForInterval:(CMTime)interval queue:(dispatch_queue_t)queue usingBlock:(void (^)(CMTime time))block方法獲得播放進度,這個方法會在設定的時間間隔內定時更新播放進度,通過time參數(shù)通知客戶端。相信有了這些視頻信息播放進度就不成問題了,事實上通過這些信息就算是平時看到的其他播放器的緩沖進度顯示以及拖動播放的功能也可以順利的實現(xiàn)。

    最后就是視頻切換的功能,在前面介紹的所有播放器中每個播放器對象一次只能播放一個視頻,如果要切換視頻只能重新創(chuàng)建一個對象,但是AVPlayer卻提供了- (void)replaceCurrentItemWithPlayerItem:(AVPlayerItem *)item方法用于在不同的視頻之間切換(事實上在AVFoundation內部還有一個AVQueuePlayer專門處理播放列表切換,有興趣的朋友可以自行研究,這里不再贅述)。

    下面附上代碼:

    // // ViewController.m // AVPlayer // // Created by Kenshin Cui on 14/03/30. // Copyright (c) 2014年 cmjstudio. All rights reserved. //#import "ViewController.h" #import <AVFoundation/AVFoundation.h>@interface ViewController ()@property (nonatomic,strong) AVPlayer *player;//播放器對象@property (weak, nonatomic) IBOutlet UIView *container; //播放器容器 @property (weak, nonatomic) IBOutlet UIButton *playOrPause; //播放/暫停按鈕 @property (weak, nonatomic) IBOutlet UIProgressView *progress;//播放進度@end@implementation ViewController#pragma mark - 控制器視圖方法 - (void)viewDidLoad {[super viewDidLoad];[self setupUI];[self.player play]; }-(void)dealloc{[self removeObserverFromPlayerItem:self.player.currentItem];[self removeNotification]; }#pragma mark - 私有方法 -(void)setupUI{//創(chuàng)建播放器層AVPlayerLayer *playerLayer=[AVPlayerLayer playerLayerWithPlayer:self.player];playerLayer.frame=self.container.frame;//playerLayer.videoGravity=AVLayerVideoGravityResizeAspect;//視頻填充模式[self.container.layer addSublayer:playerLayer]; }/*** 截取指定時間的視頻縮略圖** @param timeBySecond 時間點*//*** 初始化播放器** @return 播放器對象*/ -(AVPlayer *)player{if (!_player) {AVPlayerItem *playerItem=[self getPlayItem:0];_player=[AVPlayer playerWithPlayerItem:playerItem];[self addProgressObserver];[self addObserverToPlayerItem:playerItem];}return _player; }/*** 根據(jù)視頻索引取得AVPlayerItem對象** @param videoIndex 視頻順序索引** @return AVPlayerItem對象*/ -(AVPlayerItem *)getPlayItem:(int)videoIndex{NSString *urlStr=[NSString stringWithFormat:@"http://192.168.1.161/%i.mp4",videoIndex];urlStr =[urlStr stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];NSURL *url=[NSURL URLWithString:urlStr];AVPlayerItem *playerItem=[AVPlayerItem playerItemWithURL:url];return playerItem; } #pragma mark - 通知 /*** 添加播放器通知*/ -(void)addNotification{//給AVPlayerItem添加播放完成通知[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(playbackFinished:) name:AVPlayerItemDidPlayToEndTimeNotification object:self.player.currentItem]; }-(void)removeNotification{[[NSNotificationCenter defaultCenter] removeObserver:self]; }/*** 播放完成通知** @param notification 通知對象*/ -(void)playbackFinished:(NSNotification *)notification{NSLog(@"視頻播放完成."); }#pragma mark - 監(jiān)控 /*** 給播放器添加進度更新*/ -(void)addProgressObserver{AVPlayerItem *playerItem=self.player.currentItem;UIProgressView *progress=self.progress;//這里設置每秒執(zhí)行一次[self.player addPeriodicTimeObserverForInterval:CMTimeMake(1.0, 1.0) queue:dispatch_get_main_queue() usingBlock:^(CMTime time) {float current=CMTimeGetSeconds(time);float total=CMTimeGetSeconds([playerItem duration]);NSLog(@"當前已經(jīng)播放%.2fs.",current);if (current) {[progress setProgress:(current/total) animated:YES];}}]; }/*** 給AVPlayerItem添加監(jiān)控** @param playerItem AVPlayerItem對象*/ -(void)addObserverToPlayerItem:(AVPlayerItem *)playerItem{//監(jiān)控狀態(tài)屬性,注意AVPlayer也有一個status屬性,通過監(jiān)控它的status也可以獲得播放狀態(tài)[playerItem addObserver:self forKeyPath:@"status" options:NSKeyValueObservingOptionNew context:nil];//監(jiān)控網(wǎng)絡加載情況屬性[playerItem addObserver:self forKeyPath:@"loadedTimeRanges" options:NSKeyValueObservingOptionNew context:nil]; } -(void)removeObserverFromPlayerItem:(AVPlayerItem *)playerItem{[playerItem removeObserver:self forKeyPath:@"status"];[playerItem removeObserver:self forKeyPath:@"loadedTimeRanges"]; } /*** 通過KVO監(jiān)控播放器狀態(tài)** @param keyPath 監(jiān)控屬性* @param object 監(jiān)視器* @param change 狀態(tài)改變* @param context 上下文*/ -(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context{AVPlayerItem *playerItem=object;if ([keyPath isEqualToString:@"status"]) {AVPlayerStatus status= [[change objectForKey:@"new"] intValue];if(status==AVPlayerStatusReadyToPlay){NSLog(@"正在播放...,視頻總長度:%.2f",CMTimeGetSeconds(playerItem.duration));}}else if([keyPath isEqualToString:@"loadedTimeRanges"]){NSArray *array=playerItem.loadedTimeRanges;CMTimeRange timeRange = [array.firstObject CMTimeRangeValue];//本次緩沖時間范圍float startSeconds = CMTimeGetSeconds(timeRange.start);float durationSeconds = CMTimeGetSeconds(timeRange.duration);NSTimeInterval totalBuffer = startSeconds + durationSeconds;//緩沖總長度NSLog(@"共緩沖:%.2f",totalBuffer); //} }#pragma mark - UI事件 /*** 點擊播放/暫停按鈕** @param sender 播放/暫停按鈕*/ - (IBAction)playClick:(UIButton *)sender { // AVPlayerItemDidPlayToEndTimeNotification//AVPlayerItem *playerItem= self.player.currentItem;if(self.player.rate==0){ //說明時暫停[sender setImage:[UIImage imageNamed:@"player_pause"] forState:UIControlStateNormal];[self.player play];}else if(self.player.rate==1){//正在播放[self.player pause];[sender setImage:[UIImage imageNamed:@"player_play"] forState:UIControlStateNormal];} }/*** 切換選集,這里使用按鈕的tag代表視頻名稱** @param sender 點擊按鈕對象*/ - (IBAction)navigationButtonClick:(UIButton *)sender {[self removeNotification];[self removeObserverFromPlayerItem:self.player.currentItem];AVPlayerItem *playerItem=[self getPlayItem:sender.tag];[self addObserverToPlayerItem:playerItem];//切換視頻[self.player replaceCurrentItemWithPlayerItem:playerItem];[self addNotification]; }@end

    運行效果:

    到目前為止無論是MPMoviePlayerController還是AVPlayer來播放視頻都相當強大,但是它也存在著一些不可回避的問題,那就是支持的視頻編碼格式很有限:H.264、MPEG-4,擴展名(壓縮格式):.mp4、.mov、.m4v、.m2v、.3gp、.3g2等。但是無論是MPMoviePlayerController還是AVPlayer它們都支持絕大多數(shù)音頻編碼,所以大家如果純粹是為了播放音樂的話也可以考慮使用這兩個播放器。那么如何支持更多視頻編碼格式呢?目前來說主要還是依靠第三方框架,在iOS上常用的視頻編碼、解碼框架有:VLC、ffmpeg, 具體使用方式今天就不再做詳細介紹。

    攝像頭

    UIImagePickerController拍照和視頻錄制

    下面看一下在iOS如何拍照和錄制視頻。在iOS中要拍照和錄制視頻最簡單的方法就是使用UIImagePickerController。UIImagePickerController繼承于UINavigationController,前面的文章中主要使用它來選取照片,其實UIImagePickerController的功能不僅如此,它還可以用來拍照和錄制視頻。首先看一下這個類常用的屬性和方法:

    屬性說明
    @property(nonatomic)?????????? UIImagePickerControllerSourceType???? sourceType拾取源類型,sourceType是枚舉類型:
    UIImagePickerControllerSourceTypePhotoLibrary:照片庫
    ,默認值
    UIImagePickerControllerSourceTypeCamera:攝像頭
    UIImagePickerControllerSourceTypeSavedPhotosAlbum:相簿
    @property(nonatomic,copy)????? NSArray????????????????????????????? *mediaTypes媒體類型,默認情況下此數(shù)組包含kUTTypeImage,所以拍照時可以不用設置;但是當要錄像的時候必須設置,可以設置為kUTTypeVideo(視頻,但不帶聲音)或者kUTTypeMovie(視頻并帶有聲音)
    @property(nonatomic)?????????? NSTimeInterval??????????????????????? videoMaximumDuration視頻最大錄制時長,默認為10 s
    @property(nonatomic)?????????? UIImagePickerControllerQualityType??? videoQuality視頻質量,枚舉類型:
    UIImagePickerControllerQualityTypeHigh:高清質量
    UIImagePickerControllerQualityTypeMedium:中等質量,適合WiFi傳輸
    UIImagePickerControllerQualityTypeLow:低質量,適合蜂窩網(wǎng)傳輸
    UIImagePickerControllerQualityType640x480:640*480
    UIImagePickerControllerQualityTypeIFrame1280x720:1280*720
    UIImagePickerControllerQualityTypeIFrame960x540:960*540
    @property(nonatomic)?????????? BOOL????????????????????????????????? showsCameraControls是否顯示攝像頭控制面板,默認為YES
    @property(nonatomic,retain)??? UIView??????????????????????????????? *cameraOverlayView攝像頭上覆蓋的視圖,可用通過這個視頻來自定義拍照或錄像界面
    @property(nonatomic)?????????? CGAffineTransform???????????????????? cameraViewTransform攝像頭形變
    @property(nonatomic) UIImagePickerControllerCameraCaptureMode cameraCaptureMode攝像頭捕獲模式,捕獲模式是枚舉類型:
    UIImagePickerControllerCameraCaptureModePhoto:拍照模式
    UIImagePickerControllerCameraCaptureModeVideo:視頻錄制模式
    @property(nonatomic) UIImagePickerControllerCameraDevice????? cameraDevice攝像頭設備,cameraDevice是枚舉類型:
    UIImagePickerControllerCameraDeviceRear:前置攝像頭
    UIImagePickerControllerCameraDeviceFront:后置攝像頭
    @property(nonatomic) UIImagePickerControllerCameraFlashMode?? cameraFlashMode閃光燈模式,枚舉類型:
    UIImagePickerControllerCameraFlashModeOff:關閉閃光燈
    UIImagePickerControllerCameraFlashModeAuto:閃光燈自動
    UIImagePickerControllerCameraFlashModeOn:打開閃光燈
    類方法說明
    + (BOOL)isSourceTypeAvailable:(UIImagePickerControllerSourceType)sourceType指定的源類型是否可用,sourceType是枚舉類型:
    UIImagePickerControllerSourceTypePhotoLibrary:照片庫
    UIImagePickerControllerSourceTypeCamera:攝像頭
    UIImagePickerControllerSourceTypeSavedPhotosAlbum:相簿
    + (NSArray *)availableMediaTypesForSourceType:(UIImagePickerControllerSourceType)sourceType指定的源設備上可用的媒體類型,一般就是圖片和視頻
    + (BOOL)isCameraDeviceAvailable:(UIImagePickerControllerCameraDevice)cameraDevice指定的攝像頭是否可用,cameraDevice是枚舉類型:
    UIImagePickerControllerCameraDeviceRear:前置攝像頭
    UIImagePickerControllerCameraDeviceFront:后置攝像頭
    + (BOOL)isFlashAvailableForCameraDevice:(UIImagePickerControllerCameraDevice)cameraDevice指定攝像頭的閃光燈是否可用
    + (NSArray *)availableCaptureModesForCameraDevice:(UIImagePickerControllerCameraDevice)cameraDevice獲得指定攝像頭上的可用捕獲模式,捕獲模式是枚舉類型:
    UIImagePickerControllerCameraCaptureModePhoto:拍照模式
    UIImagePickerControllerCameraCaptureModeVideo:視頻錄制模式
    對象方法說明
    - (void)takePicture編程方式拍照
    - (BOOL)startVideoCapture編程方式錄制視頻
    - (void)stopVideoCapture編程方式停止錄制視頻
    代理方法說明
    - (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info媒體拾取完成
    - (void)imagePickerControllerDidCancel:(UIImagePickerController *)picker取消拾取
    擴展方法(主要用于保存照片、視頻到相簿)說明
    UIImageWriteToSavedPhotosAlbum(UIImage *image, id completionTarget, SEL completionSelector, void *contextInfo)保存照片到相簿
    UIVideoAtPathIsCompatibleWithSavedPhotosAlbum(NSString *videoPath)能否將視頻保存到相簿
    void UISaveVideoAtPathToSavedPhotosAlbum(NSString *videoPath, id completionTarget, SEL completionSelector, void *contextInfo)保存視頻到相簿

    要用UIImagePickerController來拍照或者錄制視頻通??梢苑譃槿缦虏襟E:

  • 創(chuàng)建UIImagePickerController對象。
  • 指定拾取源,平時選擇照片時使用的拾取源是照片庫或者相簿,此刻需要指定為攝像頭類型。
  • 指定攝像頭,前置攝像頭或者后置攝像頭。
  • 設置媒體類型mediaType,注意如果是錄像必須設置,如果是拍照此步驟可以省略,因為mediaType默認包含kUTTypeImage(注意媒體類型定義在MobileCoreServices.framework中)
  • 指定捕獲模式,拍照或者錄制視頻。(視頻錄制時必須先設置媒體類型再設置捕獲模式
  • 展示UIImagePickerController(通常以模態(tài)窗口形式打開)。
  • 拍照和錄制視頻結束后在代理方法中展示/保存照片或視頻。
  • 當然這個過程中有很多細節(jié)可以設置,例如是否顯示拍照控制面板,拍照后是否允許編輯等等,通過上面的屬性/方法列表相信并不難理解。下面就以一個示例展示如何使用UIImagePickerController來拍照和錄制視頻,下面的程序中只要將_isVideo設置為YES就是視頻錄制模式,錄制完后在主視圖控制器中自動播放;如果將_isVideo設置為NO則為拍照模式,拍照完成之后在主視圖控制器中顯示拍攝的照片:

    // // ViewController.m // UIImagePickerController // // Created by Kenshin Cui on 14/04/05. // Copyright (c) 2014年 cmjstudio. All rights reserved. //#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(@"保存視頻過程中發(fā)生錯誤,錯誤信息:%@",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

    運行效果(視頻錄制):

    AVFoundation拍照和錄制視頻

    不得不說UIImagePickerController確實強大,但是與MPMoviePlayerController類似,由于它的高度封裝性,要進行某些自定義工作就比較復雜了。例如要做出一款類似于美顏相機的拍照界面就比較難以實現(xiàn)了,此時就可以考慮使用AVFoundation來實現(xiàn)。AVFoundation中提供了很多現(xiàn)成的播放器和錄音機,但是事實上它還有更加底層的內容可以供開發(fā)者使用。因為AVFoundation中抽了很多和底層輸入、輸出設備打交道的類,依靠這些類開發(fā)人員面對的不再是封裝好的音頻播放器AVAudioPlayer、錄音機(AVAudioRecorder)、視頻(包括音頻)播放器AVPlayer,而是輸入設備(例如麥克風、攝像頭)、輸出設備(圖片、視頻)等。首先了解一下使用AVFoundation做拍照和視頻錄制開發(fā)用到的相關類:

    AVCaptureSession:媒體(音、視頻)捕獲會話,負責把捕獲的音視頻數(shù)據(jù)輸出到輸出設備中。一個AVCaptureSession可以有多個輸入輸出:

    AVCaptureDevice:輸入設備,包括麥克風、攝像頭,通過該對象可以設置物理設備的一些屬性(例如相機聚焦、白平衡等)。

    AVCaptureDeviceInput:設備輸入數(shù)據(jù)管理對象,可以根據(jù)AVCaptureDevice創(chuàng)建對應的AVCaptureDeviceInput對象,該對象將會被添加到AVCaptureSession中管理。

    AVCaptureOutput:輸出數(shù)據(jù)管理對象,用于接收各類輸出數(shù)據(jù),通常使用對應的子類AVCaptureAudioDataOutput、AVCaptureStillImageOutput、AVCaptureVideoDataOutput、AVCaptureFileOutput,該對象將會被添加到AVCaptureSession中管理。注意:前面幾個對象的輸出數(shù)據(jù)都是NSData類型,而AVCaptureFileOutput代表數(shù)據(jù)以文件形式輸出,類似的,AVCcaptureFileOutput也不會直接創(chuàng)建使用,通常會使用其子類:AVCaptureAudioFileOutput、AVCaptureMovieFileOutput。當把一個輸入或者輸出添加到AVCaptureSession之后AVCaptureSession就會在所有相符的輸入、輸出設備之間建立連接(AVCaptionConnection):

    AVCaptureVideoPreviewLayer:相機拍攝預覽圖層,是CALayer的子類,使用該對象可以實時查看拍照或視頻錄制效果,創(chuàng)建該對象需要指定對應的AVCaptureSession對象。

    使用AVFoundation拍照和錄制視頻的一般步驟如下:

  • 創(chuàng)建AVCaptureSession對象。
  • 使用AVCaptureDevice的靜態(tài)方法獲得需要使用的設備,例如拍照和錄像就需要獲得攝像頭設備,錄音就要獲得麥克風設備。
  • 利用輸入設備AVCaptureDevice初始化AVCaptureDeviceInput對象。
  • 初始化輸出數(shù)據(jù)管理對象,如果要拍照就初始化AVCaptureStillImageOutput對象;如果拍攝視頻就初始化AVCaptureMovieFileOutput對象。
  • 將數(shù)據(jù)輸入對象AVCaptureDeviceInput、數(shù)據(jù)輸出對象AVCaptureOutput添加到媒體會話管理對象AVCaptureSession中。
  • 創(chuàng)建視頻預覽圖層AVCaptureVideoPreviewLayer并指定媒體會話,添加圖層到顯示容器中,調用AVCaptureSession的startRuning方法開始捕獲。
  • 將捕獲的音頻或視頻數(shù)據(jù)輸出到指定文件。
  • 拍照

    下面看一下如何使用AVFoundation實現(xiàn)一個拍照程序,在這個程序中將實現(xiàn)攝像頭預覽、切換前后攝像頭、閃光燈設置、對焦、拍照保存等功能。應用大致效果如下:

    在程序中定義會話、輸入、輸出等相關對象。

    @interface ViewController () @property (strong,nonatomic) AVCaptureSession *captureSession;//負責輸入和輸出設備之間的數(shù)據(jù)傳遞 @property (strong,nonatomic) AVCaptureDeviceInput *captureDeviceInput;//負責從AVCaptureDevice獲得輸入數(shù)據(jù) @property (strong,nonatomic) AVCaptureStillImageOutput *captureStillImageOutput;//照片輸出流 @property (strong,nonatomic) AVCaptureVideoPreviewLayer *captureVideoPreviewLayer;//相機拍攝預覽圖層 @property (weak, nonatomic) IBOutlet UIView *viewContainer; @property (weak, nonatomic) IBOutlet UIButton *takeButton;//拍照按鈕 @property (weak, nonatomic) IBOutlet UIButton *flashAutoButton;//自動閃光燈按鈕 @property (weak, nonatomic) IBOutlet UIButton *flashOnButton;//打開閃光燈按鈕 @property (weak, nonatomic) IBOutlet UIButton *flashOffButton;//關閉閃光燈按鈕 @property (weak, nonatomic) IBOutlet UIImageView *focusCursor; //聚焦光標 @end

    在控制器視圖將要展示時創(chuàng)建并初始化會話、攝像頭設備、輸入、輸出、預覽圖層,并且添加預覽圖層到視圖中,除此之外還做了一些初始化工作,例如添加手勢(點擊屏幕進行聚焦)、初始化界面等。

    -(void)viewWillAppear:(BOOL)animated{[super viewWillAppear:animated];//初始化會話_captureSession=[[AVCaptureSession alloc]init];if ([_captureSession canSetSessionPreset:AVCaptureSessionPreset1280x720]) {//設置分辨率_captureSession.sessionPreset=AVCaptureSessionPreset1280x720;}//獲得輸入設備AVCaptureDevice *captureDevice=[self getCameraDeviceWithPosition:AVCaptureDevicePositionBack];//取得后置攝像頭if (!captureDevice) {NSLog(@"取得后置攝像頭時出現(xiàn)問題.");return;}NSError *error=nil;//根據(jù)輸入設備初始化設備輸入對象,用于獲得輸入數(shù)據(jù)_captureDeviceInput=[[AVCaptureDeviceInput alloc]initWithDevice:captureDevice error:&error];if (error) {NSLog(@"取得設備輸入對象時出錯,錯誤原因:%@",error.localizedDescription);return;}//初始化設備輸出對象,用于獲得輸出數(shù)據(jù)_captureStillImageOutput=[[AVCaptureStillImageOutput alloc]init];NSDictionary *outputSettings = @{AVVideoCodecKey:AVVideoCodecJPEG};[_captureStillImageOutput setOutputSettings:outputSettings];//輸出設置//將設備輸入添加到會話中if ([_captureSession canAddInput:_captureDeviceInput]) {[_captureSession addInput:_captureDeviceInput];}//將設備輸出添加到會話中if ([_captureSession canAddOutput:_captureStillImageOutput]) {[_captureSession addOutput:_captureStillImageOutput];}//創(chuàng)建視頻預覽層,用于實時展示攝像頭狀態(tài)_captureVideoPreviewLayer=[[AVCaptureVideoPreviewLayer alloc]initWithSession:self.captureSession];CALayer *layer=self.viewContainer.layer;layer.masksToBounds=YES;_captureVideoPreviewLayer.frame=layer.bounds;_captureVideoPreviewLayer.videoGravity=AVLayerVideoGravityResizeAspectFill;//填充模式//將視頻預覽層添加到界面中//[layer addSublayer:_captureVideoPreviewLayer];[layer insertSublayer:_captureVideoPreviewLayer below:self.focusCursor.layer];[self addNotificationToCaptureDevice:captureDevice];[self addGenstureRecognizer];[self setFlashModeButtonStatus]; }

    在控制器視圖展示和視圖離開界面時啟動、停止會話。

    -(void)viewDidAppear:(BOOL)animated{[super viewDidAppear:animated];[self.captureSession startRunning]; }-(void)viewDidDisappear:(BOOL)animated{[super viewDidDisappear:animated];[self.captureSession stopRunning]; }

    定義閃光燈開閉及自動模式功能,注意無論是設置閃光燈、白平衡還是其他輸入設備屬性,在設置之前必須先鎖定配置,修改完后解鎖。

    /*** 改變設備屬性的統(tǒng)一操作方法** @param propertyChange 屬性改變操作*/ -(void)changeDeviceProperty:(PropertyChangeBlock)propertyChange{AVCaptureDevice *captureDevice= [self.captureDeviceInput device];NSError *error;//注意改變設備屬性前一定要首先調用lockForConfiguration:調用完之后使用unlockForConfiguration方法解鎖if ([captureDevice lockForConfiguration:&error]) {propertyChange(captureDevice);[captureDevice unlockForConfiguration];}else{NSLog(@"設置設備屬性過程發(fā)生錯誤,錯誤信息:%@",error.localizedDescription);} }/*** 設置閃光燈模式** @param flashMode 閃光燈模式*/ -(void)setFlashMode:(AVCaptureFlashMode )flashMode{[self changeDeviceProperty:^(AVCaptureDevice *captureDevice) {if ([captureDevice isFlashModeSupported:flashMode]) {[captureDevice setFlashMode:flashMode];}}]; }

    定義切換攝像頭功能,切換攝像頭的過程就是將原有輸入移除,在會話中添加新的輸入,但是注意動態(tài)修改會話需要首先開啟配置,配置成功后提交配置。

    #pragma mark 切換前后攝像頭 - (IBAction)toggleButtonClick:(UIButton *)sender {AVCaptureDevice *currentDevice=[self.captureDeviceInput device];AVCaptureDevicePosition currentPosition=[currentDevice position];[self removeNotificationFromCaptureDevice:currentDevice];AVCaptureDevice *toChangeDevice;AVCaptureDevicePosition toChangePosition=AVCaptureDevicePositionFront;if (currentPosition==AVCaptureDevicePositionUnspecified||currentPosition==AVCaptureDevicePositionFront) {toChangePosition=AVCaptureDevicePositionBack;}toChangeDevice=[self getCameraDeviceWithPosition:toChangePosition];[self addNotificationToCaptureDevice:toChangeDevice];//獲得要調整的設備輸入對象AVCaptureDeviceInput *toChangeDeviceInput=[[AVCaptureDeviceInput alloc]initWithDevice:toChangeDevice error:nil];//改變會話的配置前一定要先開啟配置,配置完成后提交配置改變[self.captureSession beginConfiguration];//移除原有輸入對象[self.captureSession removeInput:self.captureDeviceInput];//添加新的輸入對象if ([self.captureSession canAddInput:toChangeDeviceInput]) {[self.captureSession addInput:toChangeDeviceInput];self.captureDeviceInput=toChangeDeviceInput;}//提交會話配置[self.captureSession commitConfiguration];[self setFlashModeButtonStatus]; }

    添加點擊手勢操作,點按預覽視圖時進行聚焦、白平衡設置。

    /*** 設置聚焦點** @param point 聚焦點*/ -(void)focusWithMode:(AVCaptureFocusMode)focusMode exposureMode:(AVCaptureExposureMode)exposureMode atPoint:(CGPoint)point{[self changeDeviceProperty:^(AVCaptureDevice *captureDevice) {if ([captureDevice isFocusModeSupported:focusMode]) {[captureDevice setFocusMode:AVCaptureFocusModeAutoFocus];}if ([captureDevice isFocusPointOfInterestSupported]) {[captureDevice setFocusPointOfInterest:point];}if ([captureDevice isExposureModeSupported:exposureMode]) {[captureDevice setExposureMode:AVCaptureExposureModeAutoExpose];}if ([captureDevice isExposurePointOfInterestSupported]) {[captureDevice setExposurePointOfInterest:point];}}]; }/*** 添加點按手勢,點按時聚焦*/ -(void)addGenstureRecognizer{UITapGestureRecognizer *tapGesture=[[UITapGestureRecognizer alloc]initWithTarget:self action:@selector(tapScreen:)];[self.viewContainer addGestureRecognizer:tapGesture]; } -(void)tapScreen:(UITapGestureRecognizer *)tapGesture{CGPoint point= [tapGesture locationInView:self.viewContainer];//將UI坐標轉化為攝像頭坐標CGPoint cameraPoint= [self.captureVideoPreviewLayer captureDevicePointOfInterestForPoint:point];[self setFocusCursorWithPoint:point];[self focusWithMode:AVCaptureFocusModeAutoFocus exposureMode:AVCaptureExposureModeAutoExpose atPoint:cameraPoint]; }

    定義拍照功能,拍照的過程就是獲取連接,從連接中獲得捕獲的輸出數(shù)據(jù)并做保存操作。

    #pragma mark 拍照 - (IBAction)takeButtonClick:(UIButton *)sender {//根據(jù)設備輸出獲得連接AVCaptureConnection *captureConnection=[self.captureStillImageOutput connectionWithMediaType:AVMediaTypeVideo];//根據(jù)連接取得設備輸出的數(shù)據(jù)[self.captureStillImageOutput captureStillImageAsynchronouslyFromConnection:captureConnection completionHandler:^(CMSampleBufferRef imageDataSampleBuffer, NSError *error) {if (imageDataSampleBuffer) {NSData *imageData=[AVCaptureStillImageOutput jpegStillImageNSDataRepresentation:imageDataSampleBuffer];UIImage *image=[UIImage imageWithData:imageData];UIImageWriteToSavedPhotosAlbum(image, nil, nil, nil); // ALAssetsLibrary *assetsLibrary=[[ALAssetsLibrary alloc]init]; // [assetsLibrary writeImageToSavedPhotosAlbum:[image CGImage] orientation:(ALAssetOrientation)[image imageOrientation] completionBlock:nil];}}]; }

    最后附上完整代碼:

    // // ViewController.m // AVFoundationCamera // // Created by Kenshin Cui on 14/04/05. // Copyright (c) 2014年 cmjstudio. All rights reserved. //#import "ViewController.h" #import <AVFoundation/AVFoundation.h> #import <AssetsLibrary/AssetsLibrary.h> typedef void(^PropertyChangeBlock)(AVCaptureDevice *captureDevice);@interface ViewController ()@property (strong,nonatomic) AVCaptureSession *captureSession;//負責輸入和輸出設備之間的數(shù)據(jù)傳遞 @property (strong,nonatomic) AVCaptureDeviceInput *captureDeviceInput;//負責從AVCaptureDevice獲得輸入數(shù)據(jù) @property (strong,nonatomic) AVCaptureStillImageOutput *captureStillImageOutput;//照片輸出流 @property (strong,nonatomic) AVCaptureVideoPreviewLayer *captureVideoPreviewLayer;//相機拍攝預覽圖層 @property (weak, nonatomic) IBOutlet UIView *viewContainer; @property (weak, nonatomic) IBOutlet UIButton *takeButton;//拍照按鈕 @property (weak, nonatomic) IBOutlet UIButton *flashAutoButton;//自動閃光燈按鈕 @property (weak, nonatomic) IBOutlet UIButton *flashOnButton;//打開閃光燈按鈕 @property (weak, nonatomic) IBOutlet UIButton *flashOffButton;//關閉閃光燈按鈕 @property (weak, nonatomic) IBOutlet UIImageView *focusCursor; //聚焦光標@end@implementation ViewController#pragma mark - 控制器視圖方法 - (void)viewDidLoad {[super viewDidLoad];}-(void)viewWillAppear:(BOOL)animated{[super viewWillAppear:animated];//初始化會話_captureSession=[[AVCaptureSession alloc]init];if ([_captureSession canSetSessionPreset:AVCaptureSessionPreset1280x720]) {//設置分辨率_captureSession.sessionPreset=AVCaptureSessionPreset1280x720;}//獲得輸入設備AVCaptureDevice *captureDevice=[self getCameraDeviceWithPosition:AVCaptureDevicePositionBack];//取得后置攝像頭if (!captureDevice) {NSLog(@"取得后置攝像頭時出現(xiàn)問題.");return;}NSError *error=nil;//根據(jù)輸入設備初始化設備輸入對象,用于獲得輸入數(shù)據(jù)_captureDeviceInput=[[AVCaptureDeviceInput alloc]initWithDevice:captureDevice error:&error];if (error) {NSLog(@"取得設備輸入對象時出錯,錯誤原因:%@",error.localizedDescription);return;}//初始化設備輸出對象,用于獲得輸出數(shù)據(jù)_captureStillImageOutput=[[AVCaptureStillImageOutput alloc]init];NSDictionary *outputSettings = @{AVVideoCodecKey:AVVideoCodecJPEG};[_captureStillImageOutput setOutputSettings:outputSettings];//輸出設置//將設備輸入添加到會話中if ([_captureSession canAddInput:_captureDeviceInput]) {[_captureSession addInput:_captureDeviceInput];}//將設備輸出添加到會話中if ([_captureSession canAddOutput:_captureStillImageOutput]) {[_captureSession addOutput:_captureStillImageOutput];}//創(chuàng)建視頻預覽層,用于實時展示攝像頭狀態(tài)_captureVideoPreviewLayer=[[AVCaptureVideoPreviewLayer alloc]initWithSession:self.captureSession];CALayer *layer=self.viewContainer.layer;layer.masksToBounds=YES;_captureVideoPreviewLayer.frame=layer.bounds;_captureVideoPreviewLayer.videoGravity=AVLayerVideoGravityResizeAspectFill;//填充模式//將視頻預覽層添加到界面中//[layer addSublayer:_captureVideoPreviewLayer];[layer insertSublayer:_captureVideoPreviewLayer below:self.focusCursor.layer];[self addNotificationToCaptureDevice:captureDevice];[self addGenstureRecognizer];[self setFlashModeButtonStatus]; }-(void)viewDidAppear:(BOOL)animated{[super viewDidAppear:animated];[self.captureSession startRunning]; }-(void)viewDidDisappear:(BOOL)animated{[super viewDidDisappear:animated];[self.captureSession stopRunning]; }-(void)dealloc{[self removeNotification]; } #pragma mark - UI方法 #pragma mark 拍照 - (IBAction)takeButtonClick:(UIButton *)sender {//根據(jù)設備輸出獲得連接AVCaptureConnection *captureConnection=[self.captureStillImageOutput connectionWithMediaType:AVMediaTypeVideo];//根據(jù)連接取得設備輸出的數(shù)據(jù)[self.captureStillImageOutput captureStillImageAsynchronouslyFromConnection:captureConnection completionHandler:^(CMSampleBufferRef imageDataSampleBuffer, NSError *error) {if (imageDataSampleBuffer) {NSData *imageData=[AVCaptureStillImageOutput jpegStillImageNSDataRepresentation:imageDataSampleBuffer];UIImage *image=[UIImage imageWithData:imageData];UIImageWriteToSavedPhotosAlbum(image, nil, nil, nil); // ALAssetsLibrary *assetsLibrary=[[ALAssetsLibrary alloc]init]; // [assetsLibrary writeImageToSavedPhotosAlbum:[image CGImage] orientation:(ALAssetOrientation)[image imageOrientation] completionBlock:nil];}}]; } #pragma mark 切換前后攝像頭 - (IBAction)toggleButtonClick:(UIButton *)sender {AVCaptureDevice *currentDevice=[self.captureDeviceInput device];AVCaptureDevicePosition currentPosition=[currentDevice position];[self removeNotificationFromCaptureDevice:currentDevice];AVCaptureDevice *toChangeDevice;AVCaptureDevicePosition toChangePosition=AVCaptureDevicePositionFront;if (currentPosition==AVCaptureDevicePositionUnspecified||currentPosition==AVCaptureDevicePositionFront) {toChangePosition=AVCaptureDevicePositionBack;}toChangeDevice=[self getCameraDeviceWithPosition:toChangePosition];[self addNotificationToCaptureDevice:toChangeDevice];//獲得要調整的設備輸入對象AVCaptureDeviceInput *toChangeDeviceInput=[[AVCaptureDeviceInput alloc]initWithDevice:toChangeDevice error:nil];//改變會話的配置前一定要先開啟配置,配置完成后提交配置改變[self.captureSession beginConfiguration];//移除原有輸入對象[self.captureSession removeInput:self.captureDeviceInput];//添加新的輸入對象if ([self.captureSession canAddInput:toChangeDeviceInput]) {[self.captureSession addInput:toChangeDeviceInput];self.captureDeviceInput=toChangeDeviceInput;}//提交會話配置[self.captureSession commitConfiguration];[self setFlashModeButtonStatus]; }#pragma mark 自動閃光燈開啟 - (IBAction)flashAutoClick:(UIButton *)sender {[self setFlashMode:AVCaptureFlashModeAuto];[self setFlashModeButtonStatus]; } #pragma mark 打開閃光燈 - (IBAction)flashOnClick:(UIButton *)sender {[self setFlashMode:AVCaptureFlashModeOn];[self setFlashModeButtonStatus]; } #pragma mark 關閉閃光燈 - (IBAction)flashOffClick:(UIButton *)sender {[self setFlashMode:AVCaptureFlashModeOff];[self setFlashModeButtonStatus]; }#pragma mark - 通知 /*** 給輸入設備添加通知*/ -(void)addNotificationToCaptureDevice:(AVCaptureDevice *)captureDevice{//注意添加區(qū)域改變捕獲通知必須首先設置設備允許捕獲[self changeDeviceProperty:^(AVCaptureDevice *captureDevice) {captureDevice.subjectAreaChangeMonitoringEnabled=YES;}];NSNotificationCenter *notificationCenter= [NSNotificationCenter defaultCenter];//捕獲區(qū)域發(fā)生改變[notificationCenter addObserver:self selector:@selector(areaChange:) name:AVCaptureDeviceSubjectAreaDidChangeNotification object:captureDevice]; } -(void)removeNotificationFromCaptureDevice:(AVCaptureDevice *)captureDevice{NSNotificationCenter *notificationCenter= [NSNotificationCenter defaultCenter];[notificationCenter removeObserver:self name:AVCaptureDeviceSubjectAreaDidChangeNotification object:captureDevice]; } /*** 移除所有通知*/ -(void)removeNotification{NSNotificationCenter *notificationCenter= [NSNotificationCenter defaultCenter];[notificationCenter removeObserver:self]; }-(void)addNotificationToCaptureSession:(AVCaptureSession *)captureSession{NSNotificationCenter *notificationCenter= [NSNotificationCenter defaultCenter];//會話出錯[notificationCenter addObserver:self selector:@selector(sessionRuntimeError:) name:AVCaptureSessionRuntimeErrorNotification object:captureSession]; }/*** 設備連接成功** @param notification 通知對象*/ -(void)deviceConnected:(NSNotification *)notification{NSLog(@"設備已連接..."); } /*** 設備連接斷開** @param notification 通知對象*/ -(void)deviceDisconnected:(NSNotification *)notification{NSLog(@"設備已斷開."); } /*** 捕獲區(qū)域改變** @param notification 通知對象*/ -(void)areaChange:(NSNotification *)notification{NSLog(@"捕獲區(qū)域改變..."); }/*** 會話出錯** @param notification 通知對象*/ -(void)sessionRuntimeError:(NSNotification *)notification{NSLog(@"會話發(fā)生錯誤."); }#pragma mark - 私有方法/*** 取得指定位置的攝像頭** @param position 攝像頭位置** @return 攝像頭設備*/ -(AVCaptureDevice *)getCameraDeviceWithPosition:(AVCaptureDevicePosition )position{NSArray *cameras= [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo];for (AVCaptureDevice *camera in cameras) {if ([camera position]==position) {return camera;}}return nil; }/*** 改變設備屬性的統(tǒng)一操作方法** @param propertyChange 屬性改變操作*/ -(void)changeDeviceProperty:(PropertyChangeBlock)propertyChange{AVCaptureDevice *captureDevice= [self.captureDeviceInput device];NSError *error;//注意改變設備屬性前一定要首先調用lockForConfiguration:調用完之后使用unlockForConfiguration方法解鎖if ([captureDevice lockForConfiguration:&error]) {propertyChange(captureDevice);[captureDevice unlockForConfiguration];}else{NSLog(@"設置設備屬性過程發(fā)生錯誤,錯誤信息:%@",error.localizedDescription);} }/*** 設置閃光燈模式** @param flashMode 閃光燈模式*/ -(void)setFlashMode:(AVCaptureFlashMode )flashMode{[self changeDeviceProperty:^(AVCaptureDevice *captureDevice) {if ([captureDevice isFlashModeSupported:flashMode]) {[captureDevice setFlashMode:flashMode];}}]; } /*** 設置聚焦模式** @param focusMode 聚焦模式*/ -(void)setFocusMode:(AVCaptureFocusMode )focusMode{[self changeDeviceProperty:^(AVCaptureDevice *captureDevice) {if ([captureDevice isFocusModeSupported:focusMode]) {[captureDevice setFocusMode:focusMode];}}]; } /*** 設置曝光模式** @param exposureMode 曝光模式*/ -(void)setExposureMode:(AVCaptureExposureMode)exposureMode{[self changeDeviceProperty:^(AVCaptureDevice *captureDevice) {if ([captureDevice isExposureModeSupported:exposureMode]) {[captureDevice setExposureMode:exposureMode];}}]; } /*** 設置聚焦點** @param point 聚焦點*/ -(void)focusWithMode:(AVCaptureFocusMode)focusMode exposureMode:(AVCaptureExposureMode)exposureMode atPoint:(CGPoint)point{[self changeDeviceProperty:^(AVCaptureDevice *captureDevice) {if ([captureDevice isFocusModeSupported:focusMode]) {[captureDevice setFocusMode:AVCaptureFocusModeAutoFocus];}if ([captureDevice isFocusPointOfInterestSupported]) {[captureDevice setFocusPointOfInterest:point];}if ([captureDevice isExposureModeSupported:exposureMode]) {[captureDevice setExposureMode:AVCaptureExposureModeAutoExpose];}if ([captureDevice isExposurePointOfInterestSupported]) {[captureDevice setExposurePointOfInterest:point];}}]; }/*** 添加點按手勢,點按時聚焦*/ -(void)addGenstureRecognizer{UITapGestureRecognizer *tapGesture=[[UITapGestureRecognizer alloc]initWithTarget:self action:@selector(tapScreen:)];[self.viewContainer addGestureRecognizer:tapGesture]; } -(void)tapScreen:(UITapGestureRecognizer *)tapGesture{CGPoint point= [tapGesture locationInView:self.viewContainer];//將UI坐標轉化為攝像頭坐標CGPoint cameraPoint= [self.captureVideoPreviewLayer captureDevicePointOfInterestForPoint:point];[self setFocusCursorWithPoint:point];[self focusWithMode:AVCaptureFocusModeAutoFocus exposureMode:AVCaptureExposureModeAutoExpose atPoint:cameraPoint]; }/*** 設置閃光燈按鈕狀態(tài)*/ -(void)setFlashModeButtonStatus{AVCaptureDevice *captureDevice=[self.captureDeviceInput device];AVCaptureFlashMode flashMode=captureDevice.flashMode;if([captureDevice isFlashAvailable]){self.flashAutoButton.hidden=NO;self.flashOnButton.hidden=NO;self.flashOffButton.hidden=NO;self.flashAutoButton.enabled=YES;self.flashOnButton.enabled=YES;self.flashOffButton.enabled=YES;switch (flashMode) {case AVCaptureFlashModeAuto:self.flashAutoButton.enabled=NO;break;case AVCaptureFlashModeOn:self.flashOnButton.enabled=NO;break;case AVCaptureFlashModeOff:self.flashOffButton.enabled=NO;break;default:break;}}else{self.flashAutoButton.hidden=YES;self.flashOnButton.hidden=YES;self.flashOffButton.hidden=YES;} }/*** 設置聚焦光標位置** @param point 光標位置*/ -(void)setFocusCursorWithPoint:(CGPoint)point{self.focusCursor.center=point;self.focusCursor.transform=CGAffineTransformMakeScale(1.5, 1.5);self.focusCursor.alpha=1.0;[UIView animateWithDuration:1.0 animations:^{self.focusCursor.transform=CGAffineTransformIdentity;} completion:^(BOOL finished) {self.focusCursor.alpha=0;}]; } @end

    運行效果:

    視頻錄制

    其實有了前面的拍照應用之后要在此基礎上做視頻錄制功能并不復雜,程序只需要做如下修改:

  • 添加一個音頻輸入到會話(使用[[AVCaptureDevice devicesWithMediaType:AVMediaTypeAudio] firstObject]獲得輸入設備,然后根據(jù)此輸入設備創(chuàng)建一個設備輸入對象),在拍照程序中已經(jīng)添加了視頻輸入所以此時不需要添加視頻輸入。
  • 創(chuàng)建一個音樂播放文件輸出對象AVCaptureMovieFileOutput取代原來的照片輸出對象。
  • 將捕獲到的視頻數(shù)據(jù)寫入到臨時文件并在停止錄制之后保存到相簿(通過AVCaptureMovieFileOutput的代理方法)。
  • 相比拍照程序,程序的修改主要就是以上三點。當然為了讓程序更加完善在下面的視頻錄制程序中加入了屏幕旋轉視頻、自動布局和后臺保存任務等細節(jié)。下面是修改后的程序:

    // // ViewController.m // AVFoundationCamera // // Created by Kenshin Cui on 14/04/05. // Copyright (c) 2014年 cmjstudio. All rights reserved. // 視頻錄制#import "ViewController.h" #import <AVFoundation/AVFoundation.h> #import <AssetsLibrary/AssetsLibrary.h> typedef void(^PropertyChangeBlock)(AVCaptureDevice *captureDevice);@interface ViewController ()<AVCaptureFileOutputRecordingDelegate>//視頻文件輸出代理@property (strong,nonatomic) AVCaptureSession *captureSession;//負責輸入和輸出設備之間的數(shù)據(jù)傳遞 @property (strong,nonatomic) AVCaptureDeviceInput *captureDeviceInput;//負責從AVCaptureDevice獲得輸入數(shù)據(jù) @property (strong,nonatomic) AVCaptureMovieFileOutput *captureMovieFileOutput;//視頻輸出流 @property (strong,nonatomic) AVCaptureVideoPreviewLayer *captureVideoPreviewLayer;//相機拍攝預覽圖層 @property (assign,nonatomic) BOOL enableRotation;//是否允許旋轉(注意在視頻錄制過程中禁止屏幕旋轉) @property (assign,nonatomic) CGRect *lastBounds;//旋轉的前大小 @property (assign,nonatomic) UIBackgroundTaskIdentifier backgroundTaskIdentifier;//后臺任務標識 @property (weak, nonatomic) IBOutlet UIView *viewContainer; @property (weak, nonatomic) IBOutlet UIButton *takeButton;//拍照按鈕 @property (weak, nonatomic) IBOutlet UIImageView *focusCursor; //聚焦光標@end@implementation ViewController#pragma mark - 控制器視圖方法 - (void)viewDidLoad {[super viewDidLoad]; }-(void)viewWillAppear:(BOOL)animated{[super viewWillAppear:animated];//初始化會話_captureSession=[[AVCaptureSession alloc]init];if ([_captureSession canSetSessionPreset:AVCaptureSessionPreset1280x720]) {//設置分辨率_captureSession.sessionPreset=AVCaptureSessionPreset1280x720;}//獲得輸入設備AVCaptureDevice *captureDevice=[self getCameraDeviceWithPosition:AVCaptureDevicePositionBack];//取得后置攝像頭if (!captureDevice) {NSLog(@"取得后置攝像頭時出現(xiàn)問題.");return;}//添加一個音頻輸入設備AVCaptureDevice *audioCaptureDevice=[[AVCaptureDevice devicesWithMediaType:AVMediaTypeAudio] firstObject];NSError *error=nil;//根據(jù)輸入設備初始化設備輸入對象,用于獲得輸入數(shù)據(jù)_captureDeviceInput=[[AVCaptureDeviceInput alloc]initWithDevice:captureDevice error:&error];if (error) {NSLog(@"取得設備輸入對象時出錯,錯誤原因:%@",error.localizedDescription);return;}AVCaptureDeviceInput *audioCaptureDeviceInput=[[AVCaptureDeviceInput alloc]initWithDevice:audioCaptureDevice error:&error];if (error) {NSLog(@"取得設備輸入對象時出錯,錯誤原因:%@",error.localizedDescription);return;}//初始化設備輸出對象,用于獲得輸出數(shù)據(jù)_captureMovieFileOutput=[[AVCaptureMovieFileOutput alloc]init];//將設備輸入添加到會話中if ([_captureSession canAddInput:_captureDeviceInput]) {[_captureSession addInput:_captureDeviceInput];[_captureSession addInput:audioCaptureDeviceInput];AVCaptureConnection *captureConnection=[_captureMovieFileOutput connectionWithMediaType:AVMediaTypeVideo];if ([captureConnection isVideoStabilizationSupported ]) {captureConnection.preferredVideoStabilizationMode=AVCaptureVideoStabilizationModeAuto;}}//將設備輸出添加到會話中if ([_captureSession canAddOutput:_captureMovieFileOutput]) {[_captureSession addOutput:_captureMovieFileOutput];}//創(chuàng)建視頻預覽層,用于實時展示攝像頭狀態(tài)_captureVideoPreviewLayer=[[AVCaptureVideoPreviewLayer alloc]initWithSession:self.captureSession];CALayer *layer=self.viewContainer.layer;layer.masksToBounds=YES;_captureVideoPreviewLayer.frame=layer.bounds;_captureVideoPreviewLayer.videoGravity=AVLayerVideoGravityResizeAspectFill;//填充模式//將視頻預覽層添加到界面中//[layer addSublayer:_captureVideoPreviewLayer];[layer insertSublayer:_captureVideoPreviewLayer below:self.focusCursor.layer];_enableRotation=YES;[self addNotificationToCaptureDevice:captureDevice];[self addGenstureRecognizer]; }-(void)viewDidAppear:(BOOL)animated{[super viewDidAppear:animated];[self.captureSession startRunning]; }-(void)viewDidDisappear:(BOOL)animated{[super viewDidDisappear:animated];[self.captureSession stopRunning]; }-(BOOL)shouldAutorotate{return self.enableRotation; }屏幕旋轉時調整視頻預覽圖層的方向 //-(void)willTransitionToTraitCollection:(UITraitCollection *)newCollection withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator{ // [super willTransitionToTraitCollection:newCollection withTransitionCoordinator:coordinator];NSLog(@"%i,%i",newCollection.verticalSizeClass,newCollection.horizontalSizeClass); // UIInterfaceOrientation orientation = [[UIApplication sharedApplication] statusBarOrientation]; // NSLog(@"%i",orientation); // AVCaptureConnection *captureConnection=[self.captureVideoPreviewLayer connection]; // captureConnection.videoOrientation=orientation; // //} //屏幕旋轉時調整視頻預覽圖層的方向 -(void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration{AVCaptureConnection *captureConnection=[self.captureVideoPreviewLayer connection];captureConnection.videoOrientation=(AVCaptureVideoOrientation)toInterfaceOrientation; } //旋轉后重新設置大小 -(void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation{_captureVideoPreviewLayer.frame=self.viewContainer.bounds; }-(void)dealloc{[self removeNotification]; } #pragma mark - UI方法 #pragma mark 視頻錄制 - (IBAction)takeButtonClick:(UIButton *)sender {//根據(jù)設備輸出獲得連接AVCaptureConnection *captureConnection=[self.captureMovieFileOutput connectionWithMediaType:AVMediaTypeVideo];//根據(jù)連接取得設備輸出的數(shù)據(jù)if (![self.captureMovieFileOutput isRecording]) {self.enableRotation=NO;//如果支持多任務則則開始多任務if ([[UIDevice currentDevice] isMultitaskingSupported]) {self.backgroundTaskIdentifier=[[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:nil];}//預覽圖層和視頻方向保持一致captureConnection.videoOrientation=[self.captureVideoPreviewLayer connection].videoOrientation;NSString *outputFielPath=[NSTemporaryDirectory() stringByAppendingString:@"myMovie.mov"];NSLog(@"save path is :%@",outputFielPath);NSURL *fileUrl=[NSURL fileURLWithPath:outputFielPath];[self.captureMovieFileOutput startRecordingToOutputFileURL:fileUrl recordingDelegate:self];}else{[self.captureMovieFileOutput stopRecording];//停止錄制} } #pragma mark 切換前后攝像頭 - (IBAction)toggleButtonClick:(UIButton *)sender {AVCaptureDevice *currentDevice=[self.captureDeviceInput device];AVCaptureDevicePosition currentPosition=[currentDevice position];[self removeNotificationFromCaptureDevice:currentDevice];AVCaptureDevice *toChangeDevice;AVCaptureDevicePosition toChangePosition=AVCaptureDevicePositionFront;if (currentPosition==AVCaptureDevicePositionUnspecified||currentPosition==AVCaptureDevicePositionFront) {toChangePosition=AVCaptureDevicePositionBack;}toChangeDevice=[self getCameraDeviceWithPosition:toChangePosition];[self addNotificationToCaptureDevice:toChangeDevice];//獲得要調整的設備輸入對象AVCaptureDeviceInput *toChangeDeviceInput=[[AVCaptureDeviceInput alloc]initWithDevice:toChangeDevice error:nil];//改變會話的配置前一定要先開啟配置,配置完成后提交配置改變[self.captureSession beginConfiguration];//移除原有輸入對象[self.captureSession removeInput:self.captureDeviceInput];//添加新的輸入對象if ([self.captureSession canAddInput:toChangeDeviceInput]) {[self.captureSession addInput:toChangeDeviceInput];self.captureDeviceInput=toChangeDeviceInput;}//提交會話配置[self.captureSession commitConfiguration];}#pragma mark - 視頻輸出代理 -(void)captureOutput:(AVCaptureFileOutput *)captureOutput didStartRecordingToOutputFileAtURL:(NSURL *)fileURL fromConnections:(NSArray *)connections{NSLog(@"開始錄制..."); } -(void)captureOutput:(AVCaptureFileOutput *)captureOutput didFinishRecordingToOutputFileAtURL:(NSURL *)outputFileURL fromConnections:(NSArray *)connections error:(NSError *)error{NSLog(@"視頻錄制完成.");//視頻錄入完成之后在后臺將視頻存儲到相簿self.enableRotation=YES;UIBackgroundTaskIdentifier lastBackgroundTaskIdentifier=self.backgroundTaskIdentifier;self.backgroundTaskIdentifier=UIBackgroundTaskInvalid;ALAssetsLibrary *assetsLibrary=[[ALAssetsLibrary alloc]init];[assetsLibrary writeVideoAtPathToSavedPhotosAlbum:outputFileURL completionBlock:^(NSURL *assetURL, NSError *error) {if (error) {NSLog(@"保存視頻到相簿過程中發(fā)生錯誤,錯誤信息:%@",error.localizedDescription);}if (lastBackgroundTaskIdentifier!=UIBackgroundTaskInvalid) {[[UIApplication sharedApplication] endBackgroundTask:lastBackgroundTaskIdentifier];}NSLog(@"成功保存視頻到相簿.");}];}#pragma mark - 通知 /*** 給輸入設備添加通知*/ -(void)addNotificationToCaptureDevice:(AVCaptureDevice *)captureDevice{//注意添加區(qū)域改變捕獲通知必須首先設置設備允許捕獲[self changeDeviceProperty:^(AVCaptureDevice *captureDevice) {captureDevice.subjectAreaChangeMonitoringEnabled=YES;}];NSNotificationCenter *notificationCenter= [NSNotificationCenter defaultCenter];//捕獲區(qū)域發(fā)生改變[notificationCenter addObserver:self selector:@selector(areaChange:) name:AVCaptureDeviceSubjectAreaDidChangeNotification object:captureDevice]; } -(void)removeNotificationFromCaptureDevice:(AVCaptureDevice *)captureDevice{NSNotificationCenter *notificationCenter= [NSNotificationCenter defaultCenter];[notificationCenter removeObserver:self name:AVCaptureDeviceSubjectAreaDidChangeNotification object:captureDevice]; } /*** 移除所有通知*/ -(void)removeNotification{NSNotificationCenter *notificationCenter= [NSNotificationCenter defaultCenter];[notificationCenter removeObserver:self]; }-(void)addNotificationToCaptureSession:(AVCaptureSession *)captureSession{NSNotificationCenter *notificationCenter= [NSNotificationCenter defaultCenter];//會話出錯[notificationCenter addObserver:self selector:@selector(sessionRuntimeError:) name:AVCaptureSessionRuntimeErrorNotification object:captureSession]; }/*** 設備連接成功** @param notification 通知對象*/ -(void)deviceConnected:(NSNotification *)notification{NSLog(@"設備已連接..."); } /*** 設備連接斷開** @param notification 通知對象*/ -(void)deviceDisconnected:(NSNotification *)notification{NSLog(@"設備已斷開."); } /*** 捕獲區(qū)域改變** @param notification 通知對象*/ -(void)areaChange:(NSNotification *)notification{NSLog(@"捕獲區(qū)域改變..."); }/*** 會話出錯** @param notification 通知對象*/ -(void)sessionRuntimeError:(NSNotification *)notification{NSLog(@"會話發(fā)生錯誤."); }#pragma mark - 私有方法/*** 取得指定位置的攝像頭** @param position 攝像頭位置** @return 攝像頭設備*/ -(AVCaptureDevice *)getCameraDeviceWithPosition:(AVCaptureDevicePosition )position{NSArray *cameras= [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo];for (AVCaptureDevice *camera in cameras) {if ([camera position]==position) {return camera;}}return nil; }/*** 改變設備屬性的統(tǒng)一操作方法** @param propertyChange 屬性改變操作*/ -(void)changeDeviceProperty:(PropertyChangeBlock)propertyChange{AVCaptureDevice *captureDevice= [self.captureDeviceInput device];NSError *error;//注意改變設備屬性前一定要首先調用lockForConfiguration:調用完之后使用unlockForConfiguration方法解鎖if ([captureDevice lockForConfiguration:&error]) {propertyChange(captureDevice);[captureDevice unlockForConfiguration];}else{NSLog(@"設置設備屬性過程發(fā)生錯誤,錯誤信息:%@",error.localizedDescription);} }/*** 設置閃光燈模式** @param flashMode 閃光燈模式*/ -(void)setFlashMode:(AVCaptureFlashMode )flashMode{[self changeDeviceProperty:^(AVCaptureDevice *captureDevice) {if ([captureDevice isFlashModeSupported:flashMode]) {[captureDevice setFlashMode:flashMode];}}]; } /*** 設置聚焦模式** @param focusMode 聚焦模式*/ -(void)setFocusMode:(AVCaptureFocusMode )focusMode{[self changeDeviceProperty:^(AVCaptureDevice *captureDevice) {if ([captureDevice isFocusModeSupported:focusMode]) {[captureDevice setFocusMode:focusMode];}}]; } /*** 設置曝光模式** @param exposureMode 曝光模式*/ -(void)setExposureMode:(AVCaptureExposureMode)exposureMode{[self changeDeviceProperty:^(AVCaptureDevice *captureDevice) {if ([captureDevice isExposureModeSupported:exposureMode]) {[captureDevice setExposureMode:exposureMode];}}]; } /*** 設置聚焦點** @param point 聚焦點*/ -(void)focusWithMode:(AVCaptureFocusMode)focusMode exposureMode:(AVCaptureExposureMode)exposureMode atPoint:(CGPoint)point{[self changeDeviceProperty:^(AVCaptureDevice *captureDevice) {if ([captureDevice isFocusModeSupported:focusMode]) {[captureDevice setFocusMode:AVCaptureFocusModeAutoFocus];}if ([captureDevice isFocusPointOfInterestSupported]) {[captureDevice setFocusPointOfInterest:point];}if ([captureDevice isExposureModeSupported:exposureMode]) {[captureDevice setExposureMode:AVCaptureExposureModeAutoExpose];}if ([captureDevice isExposurePointOfInterestSupported]) {[captureDevice setExposurePointOfInterest:point];}}]; }/*** 添加點按手勢,點按時聚焦*/ -(void)addGenstureRecognizer{UITapGestureRecognizer *tapGesture=[[UITapGestureRecognizer alloc]initWithTarget:self action:@selector(tapScreen:)];[self.viewContainer addGestureRecognizer:tapGesture]; } -(void)tapScreen:(UITapGestureRecognizer *)tapGesture{CGPoint point= [tapGesture locationInView:self.viewContainer];//將UI坐標轉化為攝像頭坐標CGPoint cameraPoint= [self.captureVideoPreviewLayer captureDevicePointOfInterestForPoint:point];[self setFocusCursorWithPoint:point];[self focusWithMode:AVCaptureFocusModeAutoFocus exposureMode:AVCaptureExposureModeAutoExpose atPoint:cameraPoint]; }/*** 設置聚焦光標位置** @param point 光標位置*/ -(void)setFocusCursorWithPoint:(CGPoint)point{self.focusCursor.center=point;self.focusCursor.transform=CGAffineTransformMakeScale(1.5, 1.5);self.focusCursor.alpha=1.0;[UIView animateWithDuration:1.0 animations:^{self.focusCursor.transform=CGAffineTransformIdentity;} completion:^(BOOL finished) {self.focusCursor.alpha=0;}]; } @end

    運行效果:

    總結

    前面用了大量的篇幅介紹了iOS中的音、視頻播放和錄制,有些地方用到了封裝好的播放器、錄音機直接使用,有些是直接調用系統(tǒng)服務自己組織封裝,正如本篇開頭所言,iOS對于多媒體支持相當靈活和完善,那么開放過程中如何選擇呢,下面就以一個表格簡單對比一下各個開發(fā)技術的優(yōu)缺點。

    提示:從本文及以后的文章中可能慢慢使用storyboard或xib,原因如下:1.蘋果官方目前主推storyboard;2.后面的文章中做屏幕適配牽扯到很多內容都是storyboard中進行(盡管純代碼也可以實現(xiàn),但是純代碼對autolayout支持不太好)3.通過前面的一系列文章大家對于純代碼編程應該已經(jīng)有一定的積累了(純代碼確實可以另初學者更加了解程序運行原理)。

    轉載于:https://www.cnblogs.com/huangfang1314/p/5676743.html

    總結

    以上是生活随笔為你收集整理的iOS开发系列--音频播放、录音、视频播放、拍照、视频录制(转)的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內容還不錯,歡迎將生活随笔推薦給好友。

    无套内谢的新婚少妇国语播放 | 人妻与老人中文字幕 | 国产精品99爱免费视频 | 精品成人av一区二区三区 | 中文字幕无码av波多野吉衣 | 女人色极品影院 | 久久国产36精品色熟妇 | 18禁黄网站男男禁片免费观看 | 麻花豆传媒剧国产免费mv在线 | 曰韩无码二三区中文字幕 | 亚洲中文字幕在线无码一区二区 | 中文字幕无码免费久久99 | 红桃av一区二区三区在线无码av | 国产精品va在线观看无码 | 国产午夜视频在线观看 | 99久久久无码国产aaa精品 | 高清国产亚洲精品自在久久 | 国产精品18久久久久久麻辣 | 精品 日韩 国产 欧美 视频 | 国产又爽又猛又粗的视频a片 | 亚洲乱码日产精品bd | 成人片黄网站色大片免费观看 | 一个人看的视频www在线 | 又大又硬又黄的免费视频 | 男女爱爱好爽视频免费看 | 特级做a爰片毛片免费69 | 亚洲欧美日韩综合久久久 | 日韩精品无码一本二本三本色 | 任你躁在线精品免费 | 丰满岳乱妇在线观看中字无码 | 亚洲国产精品一区二区美利坚 | 国产在线精品一区二区高清不卡 | 无码福利日韩神码福利片 | 又大又硬又黄的免费视频 | 粗大的内捧猛烈进出视频 | 亚洲码国产精品高潮在线 | 国产精品-区区久久久狼 | 欧美xxxxx精品 | 日日碰狠狠躁久久躁蜜桃 | 欧美一区二区三区视频在线观看 | 亚洲s色大片在线观看 | 99久久久无码国产精品免费 | 国产免费无码一区二区视频 | 无码国产色欲xxxxx视频 | 欧美野外疯狂做受xxxx高潮 | 狠狠色丁香久久婷婷综合五月 | 男人的天堂2018无码 | 亚洲成av人片天堂网无码】 | 久久国产自偷自偷免费一区调 | 久久久无码中文字幕久... | 中文字幕人成乱码熟女app | 欧美午夜特黄aaaaaa片 | 亚洲乱码中文字幕在线 | 亚洲国产精品无码一区二区三区 | 99久久精品国产一区二区蜜芽 | 久久久久久久女国产乱让韩 | 中文字幕无线码免费人妻 | 妺妺窝人体色www婷婷 | 一本一道久久综合久久 | 无码人妻精品一区二区三区下载 | 国产av无码专区亚洲awww | 色综合久久久无码中文字幕 | 色综合久久久无码中文字幕 | 三上悠亚人妻中文字幕在线 | 野狼第一精品社区 | 国产va免费精品观看 | 精品人妻中文字幕有码在线 | 巨爆乳无码视频在线观看 | 麻豆md0077饥渴少妇 | 久久精品中文闷骚内射 | 国产舌乚八伦偷品w中 | 欧美肥老太牲交大战 | 欧美性生交活xxxxxdddd | 性欧美videos高清精品 | 久久无码中文字幕免费影院蜜桃 | 色狠狠av一区二区三区 | 久久精品中文字幕一区 | 性色欲网站人妻丰满中文久久不卡 | 人妻天天爽夜夜爽一区二区 | 久久国产精品二国产精品 | 成人精品视频一区二区 | 国内精品久久久久久中文字幕 | 天天拍夜夜添久久精品大 | 中文毛片无遮挡高清免费 | 131美女爱做视频 | 亚洲国产欧美国产综合一区 | 夜先锋av资源网站 | 欧美亚洲日韩国产人成在线播放 | 久久无码专区国产精品s | 国产麻豆精品精东影业av网站 | 对白脏话肉麻粗话av | 天干天干啦夜天干天2017 | 特黄特色大片免费播放器图片 | 在线观看免费人成视频 | 中文字幕人成乱码熟女app | 国产激情无码一区二区 | 成人亚洲精品久久久久 | 人妻体内射精一区二区三四 | 亚洲成色在线综合网站 | 高中生自慰www网站 | 久久久久99精品成人片 | 天堂亚洲2017在线观看 | 欧美人妻一区二区三区 | 日韩人妻无码一区二区三区久久99 | 四虎永久在线精品免费网址 | 免费播放一区二区三区 | 亚洲国精产品一二二线 | 亚洲另类伦春色综合小说 | 国产综合久久久久鬼色 | 日本免费一区二区三区最新 | 久久精品国产99精品亚洲 | 2019nv天堂香蕉在线观看 | 日韩av无码一区二区三区 | 国产精品无套呻吟在线 | 亚洲一区二区三区在线观看网站 | 欧洲熟妇色 欧美 | 在线精品国产一区二区三区 | 3d动漫精品啪啪一区二区中 | 精品午夜福利在线观看 | 久久久婷婷五月亚洲97号色 | 国内老熟妇对白xxxxhd | 久久精品中文字幕一区 | 国产精品久久久 | 学生妹亚洲一区二区 | 成人亚洲精品久久久久软件 | 亚洲中文字幕va福利 | 伊人久久婷婷五月综合97色 | 亚洲综合精品香蕉久久网 | 55夜色66夜色国产精品视频 | 久久www免费人成人片 | 久久久久久久人妻无码中文字幕爆 | 桃花色综合影院 | 四虎国产精品一区二区 | 国产成人人人97超碰超爽8 | 国产精品美女久久久久av爽李琼 | 18禁止看的免费污网站 | 精品aⅴ一区二区三区 | 国产精品人妻一区二区三区四 | 人妻少妇精品无码专区二区 | 国产无套内射久久久国产 | 国产九九九九九九九a片 | 婷婷色婷婷开心五月四房播播 | 精品久久久无码人妻字幂 | 波多野结衣av在线观看 | 日本精品人妻无码77777 天堂一区人妻无码 | 国产办公室秘书无码精品99 | 国产莉萝无码av在线播放 | 精品乱子伦一区二区三区 | 国内精品人妻无码久久久影院 | av无码不卡在线观看免费 | 中文字幕 亚洲精品 第1页 | 天天摸天天碰天天添 | 午夜精品一区二区三区在线观看 | 色诱久久久久综合网ywww | 色综合久久久无码中文字幕 | 亚洲春色在线视频 | 精品无人国产偷自产在线 | 性开放的女人aaa片 | 99精品久久毛片a片 | 亚洲精品国产品国语在线观看 | 欧美第一黄网免费网站 | 成人精品天堂一区二区三区 | 欧美 日韩 人妻 高清 中文 | 亚洲日本一区二区三区在线 | 老司机亚洲精品影院 | 国产人妻久久精品二区三区老狼 | 一本久久伊人热热精品中文字幕 | 久久久久久国产精品无码下载 | 国产亚洲精品久久久久久久 | 精品少妇爆乳无码av无码专区 | 午夜嘿嘿嘿影院 | 中文字幕无码av波多野吉衣 | 少妇厨房愉情理9仑片视频 | 亚洲中文字幕va福利 | 国产麻豆精品一区二区三区v视界 | 偷窥村妇洗澡毛毛多 | 女人被爽到呻吟gif动态图视看 | 乱码午夜-极国产极内射 | 永久免费精品精品永久-夜色 | 国产午夜无码精品免费看 | 久久99热只有频精品8 | 日韩人妻无码中文字幕视频 | 给我免费的视频在线观看 | 亚洲va欧美va天堂v国产综合 | 国产办公室秘书无码精品99 | 久久无码人妻影院 | 久久99精品国产.久久久久 | 无码人妻精品一区二区三区不卡 | 中文字幕人妻无码一夲道 | 少妇无码av无码专区在线观看 | 色狠狠av一区二区三区 | 国产suv精品一区二区五 | 日韩无套无码精品 | 久久国产精品精品国产色婷婷 | 东京热无码av男人的天堂 | 日本精品久久久久中文字幕 | 99国产欧美久久久精品 | 精品亚洲成av人在线观看 | 亚洲国产成人av在线观看 | 久久久中文字幕日本无吗 | 日本精品高清一区二区 | 国产在线一区二区三区四区五区 | 久久久久se色偷偷亚洲精品av | 两性色午夜免费视频 | 国产色视频一区二区三区 | 久久久久av无码免费网 | 牲欲强的熟妇农村老妇女视频 | 在线观看欧美一区二区三区 | 俄罗斯老熟妇色xxxx | 大乳丰满人妻中文字幕日本 | 免费无码一区二区三区蜜桃大 | 国产精品久久久久7777 | 欧美人与禽猛交狂配 | 精品亚洲韩国一区二区三区 | 欧美国产日韩亚洲中文 | 亚欧洲精品在线视频免费观看 | 色综合久久久久综合一本到桃花网 | 丰满人妻被黑人猛烈进入 | 欧美日韩人成综合在线播放 | 俺去俺来也www色官网 | 久久亚洲日韩精品一区二区三区 | 日韩欧美中文字幕在线三区 | 又大又紧又粉嫩18p少妇 | 丰满妇女强制高潮18xxxx | 久久精品国产精品国产精品污 | 国产av一区二区三区最新精品 | www成人国产高清内射 | 波多野结衣av一区二区全免费观看 | 正在播放老肥熟妇露脸 | 色偷偷人人澡人人爽人人模 | 日本一卡2卡3卡四卡精品网站 | 色妞www精品免费视频 | 人妻中文无码久热丝袜 | 国产内射爽爽大片视频社区在线 | 亚拍精品一区二区三区探花 | 精品无人区无码乱码毛片国产 | 久久无码人妻影院 | 呦交小u女精品视频 | 亚洲国产精品久久人人爱 | 国产明星裸体无码xxxx视频 | 少妇无码av无码专区在线观看 | 久久午夜无码鲁丝片午夜精品 | aa片在线观看视频在线播放 | 夜夜躁日日躁狠狠久久av | 亚洲欧美精品伊人久久 | 日本一卡2卡3卡4卡无卡免费网站 国产一区二区三区影院 | 色婷婷av一区二区三区之红樱桃 | 一本久久伊人热热精品中文字幕 | 亚洲日韩中文字幕在线播放 | 人妻无码αv中文字幕久久琪琪布 | 秋霞成人午夜鲁丝一区二区三区 | 久久久久av无码免费网 | 一本久道久久综合婷婷五月 | 精品无码成人片一区二区98 | 无码毛片视频一区二区本码 | 十八禁真人啪啪免费网站 | 欧美性黑人极品hd | 欧美性生交xxxxx久久久 | 中文字幕人妻无码一区二区三区 | 亚洲精品一区二区三区四区五区 | 成人性做爰aaa片免费看不忠 | 久久精品女人的天堂av | 疯狂三人交性欧美 | 无码播放一区二区三区 | 午夜性刺激在线视频免费 | 亚洲精品久久久久久一区二区 | 领导边摸边吃奶边做爽在线观看 | 亚洲熟悉妇女xxx妇女av | 一本久道久久综合狠狠爱 | 午夜福利试看120秒体验区 | 丝袜美腿亚洲一区二区 | 国产香蕉97碰碰久久人人 | 欧美人与物videos另类 | 精品国产成人一区二区三区 | 久久国产精品偷任你爽任你 | 国产精品无码一区二区桃花视频 | 中文字幕日产无线码一区 | 十八禁视频网站在线观看 | 欧美精品国产综合久久 | 亚洲乱码中文字幕在线 | 国产97在线 | 亚洲 | 免费人成在线观看网站 | 奇米影视7777久久精品 | 蜜桃视频韩日免费播放 | 精品人妻人人做人人爽夜夜爽 | 亚洲男人av天堂午夜在 | 黑人巨大精品欧美一区二区 | 久久aⅴ免费观看 | 久久精品女人天堂av免费观看 | 妺妺窝人体色www婷婷 | 国产另类ts人妖一区二区 | 极品尤物被啪到呻吟喷水 | 夜精品a片一区二区三区无码白浆 | 乱码av麻豆丝袜熟女系列 | 欧美日韩一区二区综合 | 亚洲色在线无码国产精品不卡 | 国产精品亚洲综合色区韩国 | 日本大香伊一区二区三区 | 日本一卡2卡3卡四卡精品网站 | 亚洲爆乳精品无码一区二区三区 | 免费看少妇作爱视频 | 永久黄网站色视频免费直播 | 亚洲精品一区二区三区在线 | 亚拍精品一区二区三区探花 | 在线 国产 欧美 亚洲 天堂 | 成人无码精品1区2区3区免费看 | 日本精品久久久久中文字幕 | 妺妺窝人体色www婷婷 | 成熟妇人a片免费看网站 | 帮老师解开蕾丝奶罩吸乳网站 | 亚洲自偷自拍另类第1页 | 人人澡人人透人人爽 | 国产一区二区三区精品视频 | 国产激情一区二区三区 | 97久久国产亚洲精品超碰热 | 国产成人久久精品流白浆 | 成人精品一区二区三区中文字幕 | 老熟女重囗味hdxx69 | 亚洲午夜久久久影院 | 成人性做爰aaa片免费看不忠 | 国产内射爽爽大片视频社区在线 | 欧美国产亚洲日韩在线二区 | 久久综合狠狠综合久久综合88 | √天堂资源地址中文在线 | аⅴ资源天堂资源库在线 | 欧美 日韩 亚洲 在线 | 午夜无码人妻av大片色欲 | 荫蒂被男人添的好舒服爽免费视频 | 国产乱人伦app精品久久 国产在线无码精品电影网 国产国产精品人在线视 | 高清国产亚洲精品自在久久 | 亚洲成色在线综合网站 | 亚洲国产精品一区二区第一页 | 在线播放免费人成毛片乱码 | 在线成人www免费观看视频 | 久久久www成人免费毛片 | 精品日本一区二区三区在线观看 | 黑人粗大猛烈进出高潮视频 | 偷窥日本少妇撒尿chinese | 国産精品久久久久久久 | 国产精品美女久久久网av | 亚洲成av人综合在线观看 | 无遮无挡爽爽免费视频 | 动漫av网站免费观看 | 日本一卡2卡3卡4卡无卡免费网站 国产一区二区三区影院 | 日韩成人一区二区三区在线观看 | 国产亚洲人成在线播放 | 中文字幕无码日韩欧毛 | 亚洲中文字幕无码中字 | 亚洲人成人无码网www国产 | 捆绑白丝粉色jk震动捧喷白浆 | 欧美黑人巨大xxxxx | 四虎4hu永久免费 | 日日鲁鲁鲁夜夜爽爽狠狠 | 国产激情综合五月久久 | 国产黑色丝袜在线播放 | 亚洲精品无码人妻无码 | 露脸叫床粗话东北少妇 | av香港经典三级级 在线 | 亚洲国产精品无码久久久久高潮 | 内射爽无广熟女亚洲 | 成人精品视频一区二区三区尤物 | 亚洲熟悉妇女xxx妇女av | 天堂无码人妻精品一区二区三区 | 国产精品久久久一区二区三区 | 国产熟妇高潮叫床视频播放 | 欧美 丝袜 自拍 制服 另类 | 在线а√天堂中文官网 | 亚洲爆乳精品无码一区二区三区 | 日本一卡2卡3卡4卡无卡免费网站 国产一区二区三区影院 | 国产午夜精品一区二区三区嫩草 | 亚洲国产午夜精品理论片 | 女人被爽到呻吟gif动态图视看 | 亚洲成色www久久网站 | 对白脏话肉麻粗话av | 亚洲日本va午夜在线电影 | 国内少妇偷人精品视频免费 | 国产真实夫妇视频 | 丝袜 中出 制服 人妻 美腿 | 亚洲欧美国产精品久久 | 蜜臀aⅴ国产精品久久久国产老师 | 黑人巨大精品欧美黑寡妇 | av无码电影一区二区三区 | 国产综合久久久久鬼色 | 丰满少妇弄高潮了www | 天堂久久天堂av色综合 | 成人精品一区二区三区中文字幕 | 白嫩日本少妇做爰 | 亚洲精品国偷拍自产在线观看蜜桃 | 伦伦影院午夜理论片 | 男女超爽视频免费播放 | 国精产品一区二区三区 | 精品夜夜澡人妻无码av蜜桃 | 国产高潮视频在线观看 | 亚洲日本在线电影 | 一区二区三区高清视频一 | 影音先锋中文字幕无码 | 亚洲精品一区三区三区在线观看 | 丰满少妇熟乱xxxxx视频 | av无码电影一区二区三区 | 亚洲va中文字幕无码久久不卡 | 欧美刺激性大交 | ass日本丰满熟妇pics | 亚洲a无码综合a国产av中文 | 欧美日本免费一区二区三区 | 疯狂三人交性欧美 | 亚洲日本va午夜在线电影 | 乌克兰少妇xxxx做受 | 久精品国产欧美亚洲色aⅴ大片 | 九九久久精品国产免费看小说 | 久久午夜无码鲁丝片秋霞 | 天天躁日日躁狠狠躁免费麻豆 | 国内综合精品午夜久久资源 | 国产精品久久久一区二区三区 | 无码人妻av免费一区二区三区 | 给我免费的视频在线观看 | 性做久久久久久久久 | 丰满少妇人妻久久久久久 | 帮老师解开蕾丝奶罩吸乳网站 | 国产乱人伦app精品久久 国产在线无码精品电影网 国产国产精品人在线视 | 午夜精品久久久久久久 | 一本无码人妻在中文字幕免费 | 国产色xx群视频射精 | 极品嫩模高潮叫床 | 最近的中文字幕在线看视频 | av香港经典三级级 在线 | 国产人妻大战黑人第1集 | 无码国产色欲xxxxx视频 | 成人动漫在线观看 | 国产网红无码精品视频 | 在线精品亚洲一区二区 | 亚洲国产精品美女久久久久 | 亚洲熟妇自偷自拍另类 | 男人的天堂2018无码 | 婷婷五月综合激情中文字幕 | 成年美女黄网站色大免费视频 | 无码人中文字幕 | 国产人妻精品一区二区三区不卡 | 国内精品九九久久久精品 | 色婷婷综合中文久久一本 | 国产av一区二区精品久久凹凸 | 精品国产国产综合精品 | 国产精品久久久一区二区三区 | 麻豆国产人妻欲求不满 | 亚洲区小说区激情区图片区 | 亚洲中文字幕乱码av波多ji | 国产精品鲁鲁鲁 | 国产va免费精品观看 | 日本欧美一区二区三区乱码 | 无码人妻丰满熟妇区五十路百度 | 精品无人国产偷自产在线 | 夜夜夜高潮夜夜爽夜夜爰爰 | 久久精品人人做人人综合 | 好爽又高潮了毛片免费下载 | 国产精品自产拍在线观看 | 久久久婷婷五月亚洲97号色 | 亚洲欧美综合区丁香五月小说 | 国产精品a成v人在线播放 | 天堂无码人妻精品一区二区三区 | 好男人社区资源 | 亚洲 日韩 欧美 成人 在线观看 | 国产国语老龄妇女a片 | 色综合天天综合狠狠爱 | 精品无码av一区二区三区 | 国产在线一区二区三区四区五区 | 亚洲伊人久久精品影院 | 人人澡人人妻人人爽人人蜜桃 | 亚洲码国产精品高潮在线 | 婷婷五月综合激情中文字幕 | 无遮无挡爽爽免费视频 | 黑森林福利视频导航 | 综合网日日天干夜夜久久 | 欧美自拍另类欧美综合图片区 | 国产小呦泬泬99精品 | 无码国产乱人伦偷精品视频 | 久久97精品久久久久久久不卡 | 精品久久久久久亚洲精品 | 国产极品视觉盛宴 | 18精品久久久无码午夜福利 | 日本在线高清不卡免费播放 | 国产精品免费大片 | 国产亲子乱弄免费视频 | 午夜不卡av免费 一本久久a久久精品vr综合 | 亚洲自偷精品视频自拍 | 亚洲熟妇自偷自拍另类 | yw尤物av无码国产在线观看 | 国产成人精品视频ⅴa片软件竹菊 | 日本一区二区三区免费播放 | 国产精品怡红院永久免费 | 国産精品久久久久久久 | 天天躁日日躁狠狠躁免费麻豆 | 久久久国产精品无码免费专区 | 国产av久久久久精东av | 国产凸凹视频一区二区 | 精品久久久久香蕉网 | 亚洲中文字幕av在天堂 | 波多野42部无码喷潮在线 | 97精品人妻一区二区三区香蕉 | 精品成人av一区二区三区 | 黄网在线观看免费网站 | 久久久久se色偷偷亚洲精品av | 亚洲爆乳无码专区 | 日韩精品一区二区av在线 | 久久久久久久人妻无码中文字幕爆 | 国产成人无码av一区二区 | 国产成人精品三级麻豆 | 一本久道久久综合婷婷五月 | 日本熟妇人妻xxxxx人hd | 日韩精品无码一区二区中文字幕 | 国产农村乱对白刺激视频 | 国产精品无码一区二区三区不卡 | 国产黄在线观看免费观看不卡 | 两性色午夜视频免费播放 | 国产成人午夜福利在线播放 | 欧美性猛交xxxx富婆 | 男女下面进入的视频免费午夜 | 无码人妻丰满熟妇区五十路百度 | 久久伊人色av天堂九九小黄鸭 | 午夜福利一区二区三区在线观看 | 色欲人妻aaaaaaa无码 | 久久99精品国产.久久久久 | 国产av一区二区三区最新精品 | 国产性生大片免费观看性 | 天堂在线观看www | 欧美xxxxx精品 | 欧美激情内射喷水高潮 | 特级做a爰片毛片免费69 | 日日摸日日碰夜夜爽av | 亚洲成色在线综合网站 | 久久久久国色av免费观看性色 | 丝袜 中出 制服 人妻 美腿 | 国产亚洲日韩欧美另类第八页 | 中文字幕精品av一区二区五区 | 精品国偷自产在线 | 国产精品99爱免费视频 | 中文字幕无码热在线视频 | 日本护士毛茸茸高潮 | 爱做久久久久久 | 乱码av麻豆丝袜熟女系列 | 国产成人午夜福利在线播放 | 久久久久免费看成人影片 | 美女毛片一区二区三区四区 | 欧美高清在线精品一区 | 欧美性生交活xxxxxdddd | 丰满诱人的人妻3 | 国产美女精品一区二区三区 | 亚洲精品一区二区三区大桥未久 | 男女猛烈xx00免费视频试看 | 久久国产精品二国产精品 | 一本久道高清无码视频 | 久久综合狠狠综合久久综合88 | 亚洲中文字幕无码一久久区 | 国语精品一区二区三区 | 99久久久无码国产精品免费 | 亚洲春色在线视频 | 少妇人妻av毛片在线看 | 亚洲色成人中文字幕网站 | 久久亚洲日韩精品一区二区三区 | www国产亚洲精品久久网站 | 人人澡人人妻人人爽人人蜜桃 | 亚洲爆乳精品无码一区二区三区 | 久久伊人色av天堂九九小黄鸭 | 色一情一乱一伦一区二区三欧美 | 国产精品久久久久久久9999 | 亚洲a无码综合a国产av中文 | 噜噜噜亚洲色成人网站 | 欧美真人作爱免费视频 | 蜜桃av蜜臀av色欲av麻 999久久久国产精品消防器材 | 亚洲精品久久久久久一区二区 | 中文无码成人免费视频在线观看 | 亚洲综合另类小说色区 | 在线看片无码永久免费视频 | 久久久中文久久久无码 | 国产精品多人p群无码 | 高潮喷水的毛片 | 最近中文2019字幕第二页 | 少妇厨房愉情理9仑片视频 | 国产在线一区二区三区四区五区 | 欧美国产日韩亚洲中文 | 欧美成人家庭影院 | 国产色在线 | 国产 | 人人爽人人澡人人人妻 | 国产精品人人妻人人爽 | 黑人巨大精品欧美黑寡妇 | 成 人影片 免费观看 | 精品亚洲韩国一区二区三区 | 精品欧洲av无码一区二区三区 | 国产一区二区三区四区五区加勒比 | 日韩亚洲欧美精品综合 | 九九综合va免费看 | 欧美成人免费全部网站 | 97久久精品无码一区二区 | 久久久精品成人免费观看 | 樱花草在线播放免费中文 | 国产办公室秘书无码精品99 | 亚洲精品综合一区二区三区在线 | 国产午夜无码视频在线观看 | 波多野结衣一区二区三区av免费 | 国产精品-区区久久久狼 | 久久久www成人免费毛片 | 亚洲乱码日产精品bd | 国产成人精品必看 | 最近的中文字幕在线看视频 | 国产两女互慰高潮视频在线观看 | 三上悠亚人妻中文字幕在线 | 老司机亚洲精品影院无码 | 国产特级毛片aaaaaa高潮流水 | 欧美35页视频在线观看 | 久久国产自偷自偷免费一区调 | 久久99热只有频精品8 | 色综合久久久无码中文字幕 | 色综合久久88色综合天天 | 波多野结衣 黑人 | 性色av无码免费一区二区三区 | 亚洲综合伊人久久大杳蕉 | 少妇邻居内射在线 | 亚洲aⅴ无码成人网站国产app | 午夜成人1000部免费视频 | 久久精品国产精品国产精品污 | 久久综合久久自在自线精品自 | 无人区乱码一区二区三区 | 18黄暴禁片在线观看 | 亚洲精品国偷拍自产在线麻豆 | 亚洲爆乳精品无码一区二区三区 | 中文字幕色婷婷在线视频 | 久久99精品久久久久婷婷 | 无码av最新清无码专区吞精 | 成人无码视频在线观看网站 | 性欧美疯狂xxxxbbbb | 精品乱码久久久久久久 | 熟女少妇人妻中文字幕 | 亚洲日本在线电影 | 国产在线精品一区二区高清不卡 | 国产av无码专区亚洲awww | 又大又硬又爽免费视频 | 成人综合网亚洲伊人 | 中文字幕无码日韩专区 | 欧美人与动性行为视频 | 青青草原综合久久大伊人精品 | 中文字幕无码视频专区 | 女人被男人躁得好爽免费视频 | 六月丁香婷婷色狠狠久久 | 性做久久久久久久久 | 丝袜人妻一区二区三区 | 波多野结衣av一区二区全免费观看 | 成人欧美一区二区三区 | 中文字幕无码视频专区 | 又湿又紧又大又爽a视频国产 | 毛片内射-百度 | 亚洲国产精品美女久久久久 | 牛和人交xxxx欧美 | 亚洲精品美女久久久久久久 | 性欧美牲交在线视频 | 国产精品va在线观看无码 | 国产精品久久久久影院嫩草 | 国产精品福利视频导航 | 成人精品视频一区二区 | 欧美精品免费观看二区 | 国产69精品久久久久app下载 | 亚洲熟妇自偷自拍另类 | 国产精品亚洲а∨无码播放麻豆 | 亚洲另类伦春色综合小说 | 国产人妻人伦精品 | 国产精品资源一区二区 | 久久人人97超碰a片精品 | 久久国产自偷自偷免费一区调 | 久久人人爽人人人人片 | 国产女主播喷水视频在线观看 | 最近免费中文字幕中文高清百度 | 99精品无人区乱码1区2区3区 | 精品夜夜澡人妻无码av蜜桃 | 亚洲 欧美 激情 小说 另类 | 婷婷五月综合激情中文字幕 | 精品人妻人人做人人爽 | 国产精品亚洲专区无码不卡 | 久久99精品久久久久婷婷 | yw尤物av无码国产在线观看 | 日韩av激情在线观看 | 欧美老人巨大xxxx做受 | 国产免费观看黄av片 | 精品夜夜澡人妻无码av蜜桃 | 亚洲gv猛男gv无码男同 | 四十如虎的丰满熟妇啪啪 | 国产一区二区三区日韩精品 | 综合网日日天干夜夜久久 | 曰韩无码二三区中文字幕 | 国产乱码精品一品二品 | 欧美大屁股xxxxhd黑色 | 无码成人精品区在线观看 | 少妇被黑人到高潮喷出白浆 | 欧美兽交xxxx×视频 | 天下第一社区视频www日本 | 国产成人精品久久亚洲高清不卡 | 波多野结衣一区二区三区av免费 | 99精品国产综合久久久久五月天 | 又粗又大又硬毛片免费看 | 少妇激情av一区二区 | 丰满人妻翻云覆雨呻吟视频 | 无码乱肉视频免费大全合集 | 亚洲中文字幕无码中字 | 色婷婷欧美在线播放内射 | 少妇高潮喷潮久久久影院 | 蜜桃av抽搐高潮一区二区 | 久久久精品国产sm最大网站 | 精品亚洲韩国一区二区三区 | 精品无人国产偷自产在线 | 国产 精品 自在自线 | 国产成人精品一区二区在线小狼 | 日本va欧美va欧美va精品 | 四虎国产精品一区二区 | 奇米综合四色77777久久 东京无码熟妇人妻av在线网址 | 欧美性黑人极品hd | 亚洲国产午夜精品理论片 | 人妻天天爽夜夜爽一区二区 | 精品久久8x国产免费观看 | 图片小说视频一区二区 | 强伦人妻一区二区三区视频18 | 奇米影视7777久久精品 | 日日天日日夜日日摸 | 特黄特色大片免费播放器图片 | 中文字幕无码热在线视频 | 久久久久免费精品国产 | 欧美阿v高清资源不卡在线播放 | 国产亚洲精品久久久久久久 | 丰满少妇人妻久久久久久 | 亚洲中文字幕久久无码 | 中文久久乱码一区二区 | 麻豆国产丝袜白领秘书在线观看 | 成人毛片一区二区 | 国产精品美女久久久久av爽李琼 | 人妻无码久久精品人妻 | 欧美35页视频在线观看 | 国产美女极度色诱视频www | 国产一区二区三区影院 | 兔费看少妇性l交大片免费 | 无码成人精品区在线观看 | 丝袜美腿亚洲一区二区 | 久久久久免费精品国产 | 欧美人与物videos另类 | 中文字幕无码人妻少妇免费 | 一二三四社区在线中文视频 | 日本精品久久久久中文字幕 | 成人精品一区二区三区中文字幕 | 少妇人妻av毛片在线看 | 中文无码精品a∨在线观看不卡 | 午夜男女很黄的视频 | 黑人粗大猛烈进出高潮视频 | 精品无码国产自产拍在线观看蜜 | 娇妻被黑人粗大高潮白浆 | 亚洲国产精品美女久久久久 | 夫妻免费无码v看片 | 大色综合色综合网站 | av小次郎收藏 | 国产香蕉97碰碰久久人人 | 天堂亚洲免费视频 | 国产成人精品视频ⅴa片软件竹菊 | aⅴ亚洲 日韩 色 图网站 播放 | 午夜无码人妻av大片色欲 | 国产精品无码一区二区三区不卡 | 天天av天天av天天透 | 国产精品久久久av久久久 | 亚洲天堂2017无码中文 | 国产精品久久久 | 欧美性生交活xxxxxdddd | 久久人人爽人人爽人人片ⅴ | 四十如虎的丰满熟妇啪啪 | 青春草在线视频免费观看 | 久久无码中文字幕免费影院蜜桃 | 婷婷六月久久综合丁香 | 国产情侣作爱视频免费观看 | 国产精品久久国产精品99 | 精品一区二区三区无码免费视频 | 久久久久久久女国产乱让韩 | 精品乱码久久久久久久 | 久久精品国产亚洲精品 | 欧美日韩综合一区二区三区 | 欧美老熟妇乱xxxxx | ass日本丰满熟妇pics | aⅴ在线视频男人的天堂 | 精品国产一区二区三区四区在线看 | yw尤物av无码国产在线观看 | 亚洲精品国产精品乱码不卡 | 狂野欧美性猛xxxx乱大交 | 国产精品久免费的黄网站 | 天天综合网天天综合色 | 国产免费久久精品国产传媒 | 亚洲日韩中文字幕在线播放 | 东京无码熟妇人妻av在线网址 | 亚洲区小说区激情区图片区 | 国产乱人无码伦av在线a | 国产人妻精品一区二区三区不卡 | 女人色极品影院 | 久久婷婷五月综合色国产香蕉 | 亚洲热妇无码av在线播放 | 纯爱无遮挡h肉动漫在线播放 | 久久久久亚洲精品男人的天堂 | 少妇高潮喷潮久久久影院 | 国产亚洲视频中文字幕97精品 | 国产免费久久久久久无码 | 亚洲理论电影在线观看 | 色老头在线一区二区三区 | 国内精品久久久久久中文字幕 | 亚洲 日韩 欧美 成人 在线观看 | 亚洲春色在线视频 | 欧美老人巨大xxxx做受 | 亚洲一区二区三区偷拍女厕 | 亚洲阿v天堂在线 | 国产亚洲人成a在线v网站 | 麻豆国产97在线 | 欧洲 | 欧美日韩视频无码一区二区三 | 一个人免费观看的www视频 | 精品无码一区二区三区爱欲 | 伦伦影院午夜理论片 | 国产两女互慰高潮视频在线观看 | 亚洲а∨天堂久久精品2021 | 亚洲人成影院在线观看 | av人摸人人人澡人人超碰下载 | 欧美zoozzooz性欧美 | 国产舌乚八伦偷品w中 | 久久人人爽人人爽人人片ⅴ | 亚洲中文字幕在线无码一区二区 | 免费人成网站视频在线观看 | 无码福利日韩神码福利片 | 无码人妻黑人中文字幕 | 欧美日韩综合一区二区三区 | 一本久道久久综合狠狠爱 | 少女韩国电视剧在线观看完整 | 永久免费观看美女裸体的网站 | 在线看片无码永久免费视频 | 又色又爽又黄的美女裸体网站 | 又大又紧又粉嫩18p少妇 | 美女扒开屁股让男人桶 | 国产成人无码av片在线观看不卡 | 999久久久国产精品消防器材 | 一本久久a久久精品亚洲 | 亚洲熟妇色xxxxx亚洲 | 真人与拘做受免费视频一 | 亚洲综合无码一区二区三区 | 自拍偷自拍亚洲精品被多人伦好爽 | 女人被男人躁得好爽免费视频 | 一本久久a久久精品亚洲 | 在线播放免费人成毛片乱码 | 在线视频网站www色 | 国产三级久久久精品麻豆三级 | 国产三级精品三级男人的天堂 | 亚洲熟妇色xxxxx欧美老妇y | 2020最新国产自产精品 | 人妻无码αv中文字幕久久琪琪布 | 精品国偷自产在线视频 | 少妇激情av一区二区 | 久久精品99久久香蕉国产色戒 | 欧美亚洲国产一区二区三区 | 欧美性色19p | 久久久久久久久蜜桃 | 国产成人精品久久亚洲高清不卡 | 白嫩日本少妇做爰 | 中文字幕无码日韩欧毛 | 免费无码肉片在线观看 | 久久久久亚洲精品男人的天堂 | 九月婷婷人人澡人人添人人爽 | av无码不卡在线观看免费 | 天堂а√在线中文在线 | 日本熟妇浓毛 | 成人试看120秒体验区 | 色婷婷香蕉在线一区二区 | 亚洲国产一区二区三区在线观看 | 久久久国产精品无码免费专区 | 日本精品久久久久中文字幕 | 牛和人交xxxx欧美 | 亚洲欧美日韩国产精品一区二区 | 亚洲精品综合一区二区三区在线 | 亚洲一区二区三区国产精华液 | 亚洲一区二区三区香蕉 | 大肉大捧一进一出好爽视频 | 成人性做爰aaa片免费看 | 色一情一乱一伦一视频免费看 | 国产乱人伦app精品久久 国产在线无码精品电影网 国产国产精品人在线视 | 麻豆果冻传媒2021精品传媒一区下载 | 国产精品亚洲а∨无码播放麻豆 | 又紧又大又爽精品一区二区 | 人人爽人人爽人人片av亚洲 | 一区二区三区高清视频一 | 狠狠噜狠狠狠狠丁香五月 | 最近的中文字幕在线看视频 | 日本成熟视频免费视频 | 日本免费一区二区三区最新 | 噜噜噜亚洲色成人网站 | 亚洲人成影院在线无码按摩店 | 久激情内射婷内射蜜桃人妖 | 日本护士毛茸茸高潮 | 又色又爽又黄的美女裸体网站 | 精品一区二区三区无码免费视频 | 无码av免费一区二区三区试看 | 97久久精品无码一区二区 | 天干天干啦夜天干天2017 | 精品国产青草久久久久福利 | 亚洲综合无码久久精品综合 | 日韩av无码中文无码电影 | 波多野结衣av一区二区全免费观看 | 久久人人爽人人爽人人片ⅴ | 美女黄网站人色视频免费国产 | 国产精品人妻一区二区三区四 | 任你躁在线精品免费 | 亚洲成a人一区二区三区 | 亚洲欧美国产精品专区久久 | 午夜成人1000部免费视频 | 国产精品无码久久av | 精品厕所偷拍各类美女tp嘘嘘 | 亚洲一区二区三区香蕉 | 国产真实夫妇视频 | 樱花草在线播放免费中文 | 精品偷自拍另类在线观看 | 亚洲精品午夜无码电影网 | 色老头在线一区二区三区 | 国内少妇偷人精品视频 | 色欲久久久天天天综合网精品 | 久久国内精品自在自线 | 精品人妻av区 | 国产凸凹视频一区二区 | 无码一区二区三区在线 | 人妻无码αv中文字幕久久琪琪布 | 亚洲成av人片在线观看无码不卡 | 乱码av麻豆丝袜熟女系列 | 国产日产欧产精品精品app | 亚洲国产欧美日韩精品一区二区三区 | 老子影院午夜伦不卡 | 亚洲中文字幕在线观看 | 中文字幕av伊人av无码av | 东京无码熟妇人妻av在线网址 | 国产亚洲美女精品久久久2020 | 国产麻豆精品精东影业av网站 | 亚洲中文字幕乱码av波多ji | 国产成人无码区免费内射一片色欲 | 人妻有码中文字幕在线 | 亚洲天堂2017无码 | 国产莉萝无码av在线播放 | av香港经典三级级 在线 | 在线 国产 欧美 亚洲 天堂 | 国精产品一区二区三区 | 亚洲中文字幕在线观看 | 美女黄网站人色视频免费国产 | 国产乱人伦app精品久久 国产在线无码精品电影网 国产国产精品人在线视 | 捆绑白丝粉色jk震动捧喷白浆 | 亚洲欧美国产精品专区久久 | 国产色xx群视频射精 | 日韩无码专区 | 免费无码的av片在线观看 | 国产免费无码一区二区视频 | 久久午夜夜伦鲁鲁片无码免费 | 国产手机在线αⅴ片无码观看 | 成人三级无码视频在线观看 | 乱人伦人妻中文字幕无码 | 日本成熟视频免费视频 | 中文字幕色婷婷在线视频 | 7777奇米四色成人眼影 | 成人一在线视频日韩国产 | 色综合久久久久综合一本到桃花网 | 扒开双腿疯狂进出爽爽爽视频 | 亚洲熟妇色xxxxx欧美老妇 | 中文字幕日韩精品一区二区三区 | 妺妺窝人体色www婷婷 | 欧美精品无码一区二区三区 | 国产成人无码区免费内射一片色欲 | 麻豆人妻少妇精品无码专区 | 久久精品国产日本波多野结衣 | 麻豆国产人妻欲求不满谁演的 | 亚洲色在线无码国产精品不卡 | 丰满人妻一区二区三区免费视频 | 99久久精品无码一区二区毛片 | 夫妻免费无码v看片 | 久久久亚洲欧洲日产国码αv | 亚洲国产欧美在线成人 | 自拍偷自拍亚洲精品被多人伦好爽 | 日本又色又爽又黄的a片18禁 | 久久久精品人妻久久影视 | 综合网日日天干夜夜久久 | 久久久久99精品成人片 | 人妻aⅴ无码一区二区三区 | 成人无码精品1区2区3区免费看 | www国产亚洲精品久久久日本 | 国产疯狂伦交大片 | 精品午夜福利在线观看 | 国产高清av在线播放 | 大乳丰满人妻中文字幕日本 | 国语自产偷拍精品视频偷 | 少妇无码一区二区二三区 | 2020久久超碰国产精品最新 | 无码吃奶揉捏奶头高潮视频 | 亚洲の无码国产の无码影院 | 亚洲欧美色中文字幕在线 | 国产人妻精品午夜福利免费 | 国产手机在线αⅴ片无码观看 | 精品亚洲韩国一区二区三区 | 午夜精品一区二区三区的区别 | 久久精品女人天堂av免费观看 | 欧美 日韩 人妻 高清 中文 | 久久久久久久女国产乱让韩 | 国产精品怡红院永久免费 | 精品亚洲韩国一区二区三区 | 亚洲国产成人av在线观看 | 精品无人区无码乱码毛片国产 | 国产真实乱对白精彩久久 | 久久精品国产日本波多野结衣 | 精品国产乱码久久久久乱码 | 偷窥日本少妇撒尿chinese | 黑森林福利视频导航 | 对白脏话肉麻粗话av | 久久亚洲中文字幕无码 | 国产一区二区不卡老阿姨 | 97久久超碰中文字幕 | 国产午夜手机精彩视频 | 在线天堂新版最新版在线8 | 亚洲成av人片天堂网无码】 | 99精品久久毛片a片 | 国产 浪潮av性色四虎 | 午夜精品久久久久久久 | 九一九色国产 | 久久久久久亚洲精品a片成人 | 免费人成在线视频无码 | 无码人妻丰满熟妇区五十路百度 | 久久无码中文字幕免费影院蜜桃 | 无码乱肉视频免费大全合集 | 日本精品人妻无码77777 天堂一区人妻无码 | 久久久久久久久蜜桃 | 一二三四在线观看免费视频 | 日产国产精品亚洲系列 | 久久天天躁狠狠躁夜夜免费观看 | 日欧一片内射va在线影院 | 日本免费一区二区三区最新 | 欧洲极品少妇 | 亚洲爆乳精品无码一区二区三区 | 亚洲国产精品一区二区美利坚 | 国产偷自视频区视频 | 久久zyz资源站无码中文动漫 | 国内精品久久久久久中文字幕 | 亚洲乱码国产乱码精品精 | 欧美野外疯狂做受xxxx高潮 | 欧美精品一区二区精品久久 | 精品国产国产综合精品 | 亚洲乱码国产乱码精品精 | 无遮无挡爽爽免费视频 | 中文无码精品a∨在线观看不卡 | 动漫av网站免费观看 | 精品无码成人片一区二区98 | 51国偷自产一区二区三区 | 亚洲精品午夜国产va久久成人 | 欧美日本免费一区二区三区 | 久久久精品人妻久久影视 | 久久精品女人的天堂av | 亚洲成a人片在线观看日本 | 国产性猛交╳xxx乱大交 国产精品久久久久久无码 欧洲欧美人成视频在线 | 久久精品视频在线看15 | 国产在线精品一区二区三区直播 | 人人澡人摸人人添 | 一本色道久久综合亚洲精品不卡 | 精品国产一区二区三区av 性色 | 久久久久99精品国产片 | 丰满妇女强制高潮18xxxx | 人妻夜夜爽天天爽三区 | 伊人久久大香线蕉av一区二区 | 老太婆性杂交欧美肥老太 | 精品少妇爆乳无码av无码专区 | 亚欧洲精品在线视频免费观看 | 日本高清一区免费中文视频 | 亚洲国产精品无码一区二区三区 | 国产sm调教视频在线观看 | 中国女人内谢69xxxx | 亚洲日韩一区二区 | 四虎4hu永久免费 | 精品熟女少妇av免费观看 | 中文字幕日韩精品一区二区三区 | 国产激情综合五月久久 | 给我免费的视频在线观看 | 中文字幕av伊人av无码av | 亚洲国产精品一区二区第一页 | 亚洲熟妇色xxxxx欧美老妇y | 精品国产一区二区三区av 性色 | 精品国产福利一区二区 | 国产欧美熟妇另类久久久 | 国产精品无码一区二区三区不卡 | 午夜肉伦伦影院 | 色偷偷人人澡人人爽人人模 | 日本熟妇浓毛 | 日本丰满护士爆乳xxxx | 亚洲 另类 在线 欧美 制服 | 国产香蕉尹人视频在线 | 男女下面进入的视频免费午夜 | 成熟人妻av无码专区 | 亚洲精品国偷拍自产在线麻豆 | 国产97色在线 | 免 | 窝窝午夜理论片影院 | 国产精品高潮呻吟av久久4虎 | 国内精品久久久久久中文字幕 | 美女黄网站人色视频免费国产 | 久久精品女人的天堂av | 色综合久久久久综合一本到桃花网 | 国产女主播喷水视频在线观看 | 好爽又高潮了毛片免费下载 | 久久国产自偷自偷免费一区调 | 欧美人与物videos另类 | 国产sm调教视频在线观看 | 清纯唯美经典一区二区 | 久久国产自偷自偷免费一区调 | 国产色xx群视频射精 | 久久zyz资源站无码中文动漫 | 乌克兰少妇xxxx做受 | 水蜜桃色314在线观看 | 久久天天躁夜夜躁狠狠 | 国产香蕉尹人视频在线 | 色欲av亚洲一区无码少妇 | 无码人妻久久一区二区三区不卡 | 免费无码av一区二区 | 亚洲中文字幕久久无码 | 国产卡一卡二卡三 | 亚洲无人区午夜福利码高清完整版 | 天堂а√在线中文在线 | 久久久久免费精品国产 | 国产精品亚洲专区无码不卡 | 久久精品国产99久久6动漫 | 黑人巨大精品欧美黑寡妇 | 久精品国产欧美亚洲色aⅴ大片 | 久久99精品国产麻豆 | 中文字幕无码免费久久99 | 国精品人妻无码一区二区三区蜜柚 | 人妻少妇被猛烈进入中文字幕 | 国产av久久久久精东av | 久久精品女人天堂av免费观看 | 水蜜桃亚洲一二三四在线 | 亚洲熟妇色xxxxx亚洲 | 蜜臀av在线观看 在线欧美精品一区二区三区 | 国产高清不卡无码视频 | 亚洲の无码国产の无码影院 | 中文字幕乱码人妻二区三区 | 丰满诱人的人妻3 | 色欲人妻aaaaaaa无码 | 高潮毛片无遮挡高清免费视频 | 成人aaa片一区国产精品 | 99久久精品午夜一区二区 | 免费国产成人高清在线观看网站 | 麻豆果冻传媒2021精品传媒一区下载 | 2019nv天堂香蕉在线观看 | 无码av岛国片在线播放 | 麻豆成人精品国产免费 | 中文字幕av伊人av无码av | 国产午夜亚洲精品不卡 | 国产三级精品三级男人的天堂 | 免费无码午夜福利片69 | 国产免费观看黄av片 | 亚洲成在人网站无码天堂 | 樱花草在线社区www | v一区无码内射国产 | 宝宝好涨水快流出来免费视频 | 欧美亚洲日韩国产人成在线播放 | 久久精品无码一区二区三区 | 亚洲 日韩 欧美 成人 在线观看 | 青青草原综合久久大伊人精品 | 久久久久久国产精品无码下载 | 欧美国产亚洲日韩在线二区 | 国产成人精品无码播放 | 亚洲欧美综合区丁香五月小说 | 亚洲男人av香蕉爽爽爽爽 | 丝袜 中出 制服 人妻 美腿 | 亚洲精品国产a久久久久久 | 成人无码精品1区2区3区免费看 | 我要看www免费看插插视频 | 久久五月精品中文字幕 | 性做久久久久久久久 | 少妇邻居内射在线 | 国产精品自产拍在线观看 | 狠狠噜狠狠狠狠丁香五月 | 国内少妇偷人精品视频免费 | 夜夜夜高潮夜夜爽夜夜爰爰 | 欧美性色19p | 亚洲精品欧美二区三区中文字幕 | 中文字幕乱码亚洲无线三区 | 人妻少妇被猛烈进入中文字幕 | 人妻插b视频一区二区三区 | 偷窥日本少妇撒尿chinese | 无码人妻精品一区二区三区下载 | 精品一区二区三区无码免费视频 | 国产免费无码一区二区视频 | 国产精品久久久久7777 | 亚洲欧美精品伊人久久 | 亚洲欧美日韩综合久久久 | 久久亚洲日韩精品一区二区三区 | 亚洲欧洲日本无在线码 | 国产精品鲁鲁鲁 | 亚洲 激情 小说 另类 欧美 | 天天拍夜夜添久久精品大 | 亚洲狠狠色丁香婷婷综合 | 国产肉丝袜在线观看 | 国产综合色产在线精品 | 在教室伦流澡到高潮hnp视频 | 特黄特色大片免费播放器图片 | 欧美猛少妇色xxxxx | 久久精品女人天堂av免费观看 | 精品国产一区二区三区四区 | 大肉大捧一进一出好爽视频 | 99精品无人区乱码1区2区3区 | 奇米综合四色77777久久 东京无码熟妇人妻av在线网址 | 精品人人妻人人澡人人爽人人 | 最近免费中文字幕中文高清百度 | 久久精品国产大片免费观看 | 久久久精品人妻久久影视 | 一本色道久久综合狠狠躁 | 亚洲精品一区二区三区在线观看 | 久久精品国产大片免费观看 | 少妇厨房愉情理9仑片视频 | 久久综合久久自在自线精品自 | 亚洲一区二区三区含羞草 | 亚洲欧美日韩成人高清在线一区 | 樱花草在线社区www | 亚洲精品午夜无码电影网 | 99riav国产精品视频 | 亚洲精品国产第一综合99久久 | 久久久久成人精品免费播放动漫 | 未满成年国产在线观看 | 久久国语露脸国产精品电影 | 未满成年国产在线观看 | 国产熟女一区二区三区四区五区 | 天天拍夜夜添久久精品 | 精品国产福利一区二区 | 真人与拘做受免费视频 | 国产肉丝袜在线观看 | 蜜桃臀无码内射一区二区三区 | 亚洲乱亚洲乱妇50p | 捆绑白丝粉色jk震动捧喷白浆 | 日日碰狠狠躁久久躁蜜桃 | 国产超级va在线观看视频 | 无码人妻丰满熟妇区毛片18 | 亚洲综合无码一区二区三区 | 亚洲s色大片在线观看 | 3d动漫精品啪啪一区二区中 | 亚洲欧洲日本无在线码 | 人妻尝试又大又粗久久 | 性欧美videos高清精品 | 亚洲中文字幕成人无码 | 鲁一鲁av2019在线 | 乱人伦人妻中文字幕无码 | 国产国产精品人在线视 | 偷窥村妇洗澡毛毛多 | 377p欧洲日本亚洲大胆 | 牲欲强的熟妇农村老妇女视频 | 久久人妻内射无码一区三区 | 熟妇人妻激情偷爽文 | 精品人妻人人做人人爽 | 久久久久免费看成人影片 | 一本色道久久综合狠狠躁 | 中国女人内谢69xxxxxa片 | 欧美肥老太牲交大战 | 欧美精品在线观看 | 国产超碰人人爽人人做人人添 | 国产午夜无码视频在线观看 | 亚洲成a人一区二区三区 | 男女作爱免费网站 | 妺妺窝人体色www婷婷 | 人妻互换免费中文字幕 | 欧美亚洲国产一区二区三区 | 国语自产偷拍精品视频偷 | 男女下面进入的视频免费午夜 | 亚洲熟熟妇xxxx | 国产熟妇高潮叫床视频播放 | √8天堂资源地址中文在线 | 精品一区二区三区无码免费视频 | 亚洲成a人一区二区三区 | 国产av人人夜夜澡人人爽麻豆 | 熟女少妇在线视频播放 | 无套内谢老熟女 | 性开放的女人aaa片 | 无码人妻丰满熟妇区毛片18 | 国内老熟妇对白xxxxhd | 欧美日韩一区二区综合 | 亚洲日本在线电影 | 丝袜美腿亚洲一区二区 | 国产精品久久久一区二区三区 | 无码人妻丰满熟妇区五十路百度 | 亚洲欧美综合区丁香五月小说 | 亚洲人亚洲人成电影网站色 | 亚洲一区二区观看播放 | 亚洲国产高清在线观看视频 | 综合激情五月综合激情五月激情1 | 亚洲精品久久久久中文第一幕 | 日韩成人一区二区三区在线观看 | 99久久精品午夜一区二区 | 亚洲精品综合一区二区三区在线 | 久久亚洲精品中文字幕无男同 | 欧美成人免费全部网站 | 亚洲色欲色欲天天天www | 国产又爽又猛又粗的视频a片 | 水蜜桃色314在线观看 | 日本护士毛茸茸高潮 | 欧美精品无码一区二区三区 | 麻豆国产丝袜白领秘书在线观看 | 欧美激情内射喷水高潮 | 久在线观看福利视频 | 亚洲人成影院在线观看 | 三级4级全黄60分钟 | 在教室伦流澡到高潮hnp视频 | 欧美自拍另类欧美综合图片区 | 免费无码午夜福利片69 | 国产综合在线观看 | 天下第一社区视频www日本 | 中文字幕乱妇无码av在线 | 亚洲人成影院在线无码按摩店 | 国内精品久久久久久中文字幕 | 在线a亚洲视频播放在线观看 | 欧美精品免费观看二区 | 中文字幕无码免费久久9一区9 | 成人无码视频在线观看网站 | 日本熟妇乱子伦xxxx | 久久天天躁夜夜躁狠狠 | 蜜桃av抽搐高潮一区二区 | 久久久久se色偷偷亚洲精品av | 亚洲第一网站男人都懂 | 狂野欧美性猛交免费视频 | 色欲综合久久中文字幕网 | 亚洲一区二区三区含羞草 | 天天av天天av天天透 | 亚洲无人区一区二区三区 | 国产精品99爱免费视频 | 欧美精品一区二区精品久久 | 国产精品成人av在线观看 | 国产成人无码a区在线观看视频app | 成人影院yy111111在线观看 | 亚洲精品综合一区二区三区在线 | 性史性农村dvd毛片 | 999久久久国产精品消防器材 | 中文字幕精品av一区二区五区 | 永久免费精品精品永久-夜色 | 亚洲无人区一区二区三区 | 久久成人a毛片免费观看网站 | 在线a亚洲视频播放在线观看 | 伊在人天堂亚洲香蕉精品区 | 熟妇女人妻丰满少妇中文字幕 | 中文无码伦av中文字幕 | 精品无人国产偷自产在线 | 亚洲狠狠色丁香婷婷综合 | 人人澡人人透人人爽 | 国产亚洲精品久久久久久国模美 | 内射后入在线观看一区 | 亚洲国产精品毛片av不卡在线 | 亚洲经典千人经典日产 | 亚洲中文字幕无码中文字在线 | 十八禁真人啪啪免费网站 | 精品偷拍一区二区三区在线看 | 日韩精品a片一区二区三区妖精 | 亚洲国产av精品一区二区蜜芽 | 国产精品人妻一区二区三区四 | 国产av一区二区精品久久凹凸 | 无遮无挡爽爽免费视频 | 国产色精品久久人妻 | 精品国产青草久久久久福利 | 精品 日韩 国产 欧美 视频 | 日日鲁鲁鲁夜夜爽爽狠狠 | 自拍偷自拍亚洲精品被多人伦好爽 | 国产av一区二区三区最新精品 | 精品国产麻豆免费人成网站 | 色欲av亚洲一区无码少妇 | 色综合久久久久综合一本到桃花网 | 日本精品久久久久中文字幕 | 日日天干夜夜狠狠爱 | 国内综合精品午夜久久资源 | 色综合久久久久综合一本到桃花网 | 欧美人与善在线com | 狠狠色欧美亚洲狠狠色www | 免费人成在线视频无码 | 欧美freesex黑人又粗又大 | 一本色道久久综合狠狠躁 | 在线精品国产一区二区三区 | 国产在热线精品视频 | 久久午夜无码鲁丝片午夜精品 | 亚洲一区二区三区含羞草 | 国内精品一区二区三区不卡 | 又色又爽又黄的美女裸体网站 | 最近免费中文字幕中文高清百度 | 国产亚洲精品久久久久久久 | 欧美国产亚洲日韩在线二区 | 免费人成网站视频在线观看 | 女人被男人躁得好爽免费视频 | 波多野结衣一区二区三区av免费 | 日韩av无码一区二区三区不卡 | 精品国产麻豆免费人成网站 | 亚洲 另类 在线 欧美 制服 | 色一情一乱一伦 | 久久精品国产一区二区三区肥胖 | 丰满少妇弄高潮了www | 欧美日韩综合一区二区三区 | 亚洲国产精品无码久久久久高潮 | 小鲜肉自慰网站xnxx | 性欧美熟妇videofreesex | 荡女精品导航 | 亚洲国产成人av在线观看 | 中文字幕无码人妻少妇免费 | 特黄特色大片免费播放器图片 | 亚洲区欧美区综合区自拍区 | 男女猛烈xx00免费视频试看 | 人人妻人人澡人人爽欧美精品 | 熟妇人妻中文av无码 | 亚洲码国产精品高潮在线 | 亚洲国产午夜精品理论片 | 亚洲综合无码一区二区三区 | 国产亚洲精品久久久ai换 | 大地资源中文第3页 | 亚洲性无码av中文字幕 | 亚洲 欧美 激情 小说 另类 | 四虎永久在线精品免费网址 | 鲁一鲁av2019在线 | 久久久www成人免费毛片 | 老熟妇乱子伦牲交视频 | 婷婷六月久久综合丁香 | 日欧一片内射va在线影院 | 亚洲国产av美女网站 | 国产成人精品久久亚洲高清不卡 | 天天拍夜夜添久久精品 | 亚洲熟妇色xxxxx欧美老妇 | 熟女俱乐部五十路六十路av | 色综合久久久无码中文字幕 | 中国女人内谢69xxxxxa片 | 蜜桃视频插满18在线观看 | 综合人妻久久一区二区精品 | 1000部啪啪未满十八勿入下载 | 日本欧美一区二区三区乱码 | 日日天日日夜日日摸 | 久久精品99久久香蕉国产色戒 | 蜜桃视频韩日免费播放 | 精品一区二区三区波多野结衣 | 免费无码一区二区三区蜜桃大 | 久久亚洲中文字幕精品一区 | 亚洲乱码中文字幕在线 | 人人超人人超碰超国产 | 久久国语露脸国产精品电影 | 亚洲日韩av一区二区三区四区 | 色婷婷综合激情综在线播放 | 亚洲成a人片在线观看日本 | 久久人人97超碰a片精品 | 久热国产vs视频在线观看 | 国产成人av免费观看 | 动漫av一区二区在线观看 | 亚洲成熟女人毛毛耸耸多 | 日本www一道久久久免费榴莲 | 欧美熟妇另类久久久久久不卡 | 久久久亚洲欧洲日产国码αv | 少妇一晚三次一区二区三区 | 精品人妻人人做人人爽 | 图片区 小说区 区 亚洲五月 | 色欲人妻aaaaaaa无码 | 在线精品国产一区二区三区 | a片免费视频在线观看 | 学生妹亚洲一区二区 | 无套内谢的新婚少妇国语播放 | aⅴ亚洲 日韩 色 图网站 播放 | 女人被男人躁得好爽免费视频 | 国产又粗又硬又大爽黄老大爷视 | 天天拍夜夜添久久精品大 | 日韩精品无码一区二区中文字幕 | 精品国产av色一区二区深夜久久 | 强辱丰满人妻hd中文字幕 | 成熟人妻av无码专区 | 秋霞成人午夜鲁丝一区二区三区 | 粉嫩少妇内射浓精videos | 中文字幕亚洲情99在线 | 色综合久久网 | 麻豆果冻传媒2021精品传媒一区下载 | 亚洲综合无码一区二区三区 | 久9re热视频这里只有精品 | 色偷偷av老熟女 久久精品人妻少妇一区二区三区 | 亚洲精品久久久久中文第一幕 | 精品人妻人人做人人爽夜夜爽 | 成人精品一区二区三区中文字幕 | 亚洲国产欧美在线成人 | 九月婷婷人人澡人人添人人爽 | 国产 浪潮av性色四虎 | 久久人人爽人人爽人人片ⅴ | 国产精品久久久久久亚洲毛片 | 荫蒂添的好舒服视频囗交 | 亚洲欧美日韩成人高清在线一区 | 日本一卡2卡3卡4卡无卡免费网站 国产一区二区三区影院 | 色综合久久88色综合天天 | 波多野结衣av一区二区全免费观看 | 久久综合给久久狠狠97色 | 日本丰满护士爆乳xxxx | 国产农村妇女高潮大叫 | 97夜夜澡人人爽人人喊中国片 | 无套内射视频囯产 | 亚洲狠狠婷婷综合久久 | 欧美性黑人极品hd | 欧美精品国产综合久久 | 无码一区二区三区在线 | 亚洲无人区一区二区三区 | 国产乱人伦偷精品视频 | 男女下面进入的视频免费午夜 | 精品乱子伦一区二区三区 | 日本爽爽爽爽爽爽在线观看免 | 精品久久久中文字幕人妻 | 男女下面进入的视频免费午夜 | 亚洲综合精品香蕉久久网 | 久久精品国产日本波多野结衣 | 中文字幕乱码人妻二区三区 | 男女下面进入的视频免费午夜 | 一本精品99久久精品77 | 人妻人人添人妻人人爱 | 久青草影院在线观看国产 | 国内精品久久毛片一区二区 | 内射爽无广熟女亚洲 | 俺去俺来也在线www色官网 | 欧美精品免费观看二区 | 国产精品毛片一区二区 | 国产sm调教视频在线观看 | 天下第一社区视频www日本 | 久久久中文字幕日本无吗 | 免费观看的无遮挡av | 精品欧美一区二区三区久久久 | 精品乱子伦一区二区三区 | 免费无码av一区二区 | 麻豆果冻传媒2021精品传媒一区下载 | 亚洲日韩精品欧美一区二区 | av在线亚洲欧洲日产一区二区 | 日日天日日夜日日摸 | 人人妻人人澡人人爽人人精品 | 亚洲中文无码av永久不收费 | 99久久精品国产一区二区蜜芽 | 亚洲狠狠色丁香婷婷综合 | 97精品国产97久久久久久免费 | 55夜色66夜色国产精品视频 | 免费网站看v片在线18禁无码 | 在教室伦流澡到高潮hnp视频 | 国产精品二区一区二区aⅴ污介绍 | 成熟妇人a片免费看网站 | 欧美国产日产一区二区 | 欧美成人免费全部网站 | 99视频精品全部免费免费观看 | 欧美成人高清在线播放 | 无码国模国产在线观看 | 久久成人a毛片免费观看网站 | 久久精品国产一区二区三区 | 少妇被粗大的猛进出69影院 | 性做久久久久久久久 | 国精产品一品二品国精品69xx | 青青青手机频在线观看 | 女人色极品影院 | 亚洲精品一区二区三区大桥未久 | 亚洲一区二区三区国产精华液 | 精品厕所偷拍各类美女tp嘘嘘 | 成年美女黄网站色大免费视频 | 欧洲精品码一区二区三区免费看 | 国产午夜无码精品免费看 | 日韩av无码一区二区三区不卡 | 狠狠色丁香久久婷婷综合五月 | 亚洲狠狠婷婷综合久久 | 亚洲阿v天堂在线 | 久久五月精品中文字幕 | 欧美猛少妇色xxxxx | 亚洲日韩av一区二区三区四区 | 亚洲小说图区综合在线 | 天天拍夜夜添久久精品 | 日日摸天天摸爽爽狠狠97 | 色妞www精品免费视频 | 老司机亚洲精品影院无码 | 亚洲自偷精品视频自拍 | 丰满少妇高潮惨叫视频 | 亚洲理论电影在线观看 | 亚洲欧洲日本无在线码 | 久久天天躁夜夜躁狠狠 | 一本一道久久综合久久 | 国产精品二区一区二区aⅴ污介绍 | 日本熟妇乱子伦xxxx | 俺去俺来也在线www色官网 | 一区二区三区乱码在线 | 欧洲 | 亚洲码国产精品高潮在线 | 国产乱人伦app精品久久 国产在线无码精品电影网 国产国产精品人在线视 | 国产亚洲精品久久久ai换 | 丰满人妻精品国产99aⅴ | 狠狠躁日日躁夜夜躁2020 | 国产亚洲精品久久久ai换 | 性生交大片免费看女人按摩摩 | 九九在线中文字幕无码 | 四十如虎的丰满熟妇啪啪 | 亚洲啪av永久无码精品放毛片 | 中文字幕无码热在线视频 | v一区无码内射国产 | 综合网日日天干夜夜久久 | 色妞www精品免费视频 | 亚洲国精产品一二二线 | 麻豆国产人妻欲求不满谁演的 | 久久国内精品自在自线 | 中国女人内谢69xxxx | 天海翼激烈高潮到腰振不止 | 久久久久亚洲精品男人的天堂 | 免费国产成人高清在线观看网站 | 亚洲色无码一区二区三区 | 久久99精品久久久久婷婷 | 狠狠cao日日穞夜夜穞av | 日本熟妇乱子伦xxxx | 无码国产乱人伦偷精品视频 | 亚洲热妇无码av在线播放 | 天天做天天爱天天爽综合网 | 久久99精品久久久久久 | 人妻熟女一区 | 乱码午夜-极国产极内射 | 丰满护士巨好爽好大乳 | 青青久在线视频免费观看 | 老头边吃奶边弄进去呻吟 | 人妻无码久久精品人妻 | 婷婷五月综合缴情在线视频 | 国产69精品久久久久app下载 | 女人被男人爽到呻吟的视频 | 国产av久久久久精东av | 免费无码肉片在线观看 | 日本肉体xxxx裸交 | 蜜臀aⅴ国产精品久久久国产老师 | 国产真实伦对白全集 | 国产肉丝袜在线观看 | 97资源共享在线视频 | 荡女精品导航 | 少妇人妻偷人精品无码视频 | 无码精品国产va在线观看dvd | 熟女少妇在线视频播放 | 午夜精品一区二区三区在线观看 | 精品国偷自产在线视频 | 中文字幕无码乱人伦 | 亚洲色大成网站www国产 | 国产精品亚洲一区二区三区喷水 | 国产av一区二区精品久久凹凸 | 国产无遮挡又黄又爽又色 | 在线精品亚洲一区二区 | 又湿又紧又大又爽a视频国产 | 免费无码的av片在线观看 | 欧美国产日韩亚洲中文 | 99精品视频在线观看免费 | 精品国偷自产在线 | 国产欧美精品一区二区三区 | 99久久人妻精品免费二区 | 久久天天躁夜夜躁狠狠 | 日日摸天天摸爽爽狠狠97 | 麻豆人妻少妇精品无码专区 | 久久午夜无码鲁丝片 | 人人妻人人澡人人爽人人精品浪潮 | 福利一区二区三区视频在线观看 | 亚洲精品www久久久 | 久久久久免费看成人影片 | 色综合久久久无码中文字幕 | 性欧美牲交xxxxx视频 | 中文字幕中文有码在线 | 丰满肥臀大屁股熟妇激情视频 | 色婷婷av一区二区三区之红樱桃 | 高潮毛片无遮挡高清免费 | 午夜福利不卡在线视频 | 亚洲精品成人av在线 | 伦伦影院午夜理论片 | 性欧美熟妇videofreesex | 国产成人一区二区三区在线观看 | 精品国产av色一区二区深夜久久 | 天堂无码人妻精品一区二区三区 | 熟妇人妻无码xxx视频 | 国产日产欧产精品精品app | 亚洲日本va中文字幕 | 亚洲综合伊人久久大杳蕉 | 中文字幕乱码人妻二区三区 | 欧美成人午夜精品久久久 | 丝袜人妻一区二区三区 | 丰满人妻精品国产99aⅴ | 蜜桃无码一区二区三区 | 无套内谢老熟女 | 亚洲国产精品久久久天堂 | 国产精品人人妻人人爽 | 日本精品久久久久中文字幕 | 一本色道久久综合亚洲精品不卡 | 水蜜桃亚洲一二三四在线 | 99久久久无码国产精品免费 | 丝袜美腿亚洲一区二区 | 波多野结衣乳巨码无在线观看 | 亚洲va欧美va天堂v国产综合 | 久久久久免费看成人影片 | 偷窥村妇洗澡毛毛多 | 粗大的内捧猛烈进出视频 | 国产特级毛片aaaaaa高潮流水 | 精品国精品国产自在久国产87 |