亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 系統 > iOS > 正文

ios開發:一個音樂播放器的設計與實現案例

2020-07-26 03:07:07
字體:
來源:轉載
供稿:網友

這個Demo,關于歌曲播放的主要功能都實現了的。下一曲、上一曲,暫停,根據歌曲的播放進度動態滾動歌詞,將當前正在播放的歌詞放大顯示,拖動進度條,歌曲跟著變化,并且使用Time Profiler進行了優化,還使用XCTest對幾個主要的類進行了單元測試。

已經經過真機調試,在真機上可以后臺播放音樂,并且鎖屏時,顯示一些主要的歌曲信息。

根據歌曲的播放來顯示對應歌詞的。用UITableView來顯示歌詞,可以手動滾動界面查看后面或者前面的歌詞。

并且,當拖動進度條,歌詞也會隨之變化,下一曲、上一曲依然是可以使用的。

代碼分析:

準備階段,先是寫了一個音頻播放的單例,用這個單例來播放這個demo中的音樂文件,代碼如下:

#import <Foundation/Foundation.h>#import <AVFoundation/AVFoundation.h>@interface ZYAudioManager : NSObject+ (instancetype)defaultManager; //播放音樂- (AVAudioPlayer *)playingMusic:(NSString *)filename;- (void)pauseMusic:(NSString *)filename;- (void)stopMusic:(NSString *)filename; //播放音效- (void)playSound:(NSString *)filename;- (void)disposeSound:(NSString *)filename;@end   #import "ZYAudioManager.h" @interface ZYAudioManager ()@property (nonatomic, strong) NSMutableDictionary *musicPlayers;@property (nonatomic, strong) NSMutableDictionary *soundIDs;@end static ZYAudioManager *_instance = nil; @implementation ZYAudioManager + (void)initialize{  // 音頻會話  AVAudioSession *session = [AVAudioSession sharedInstance];     // 設置會話類型(播放類型、播放模式,會自動停止其他音樂的播放)  [session setCategory:AVAudioSessionCategoryPlayback error:nil];     // 激活會話  [session setActive:YES error:nil];} + (instancetype)defaultManager{  static dispatch_once_t onceToken;  dispatch_once(&onceToken, ^{    _instance = [[self alloc] init];  });  return _instance;} - (instancetype)init{  __block ZYAudioManager *temp = self;     static dispatch_once_t onceToken;  dispatch_once(&onceToken, ^{    if ((temp = [super init]) != nil) {      _musicPlayers = [NSMutableDictionary dictionary];      _soundIDs = [NSMutableDictionary dictionary];    }  });  self = temp;  return self;} + (instancetype)allocWithZone:(struct _NSZone *)zone{  static dispatch_once_t onceToken;  dispatch_once(&onceToken, ^{    _instance = [super allocWithZone:zone];  });  return _instance;} //播放音樂- (AVAudioPlayer *)playingMusic:(NSString *)filename{  if (filename == nil || filename.length == 0) return nil;     AVAudioPlayer *player = self.musicPlayers[filename];   //先查詢對象是否緩存了     if (!player) {    NSURL *url = [[NSBundle mainBundle] URLForResource:filename withExtension:nil];         if (!url) return nil;         player = [[AVAudioPlayer alloc] initWithContentsOfURL:url error:nil];         if (![player prepareToPlay]) return nil;         self.musicPlayers[filename] = player;      //對象是最新創建的,那么對它進行一次緩存  }     if (![player isPlaying]) {         //如果沒有正在播放,那么開始播放,如果正在播放,那么不需要改變什么    [player play];  }  return player;} - (void)pauseMusic:(NSString *)filename{  if (filename == nil || filename.length == 0) return;     AVAudioPlayer *player = self.musicPlayers[filename];     if ([player isPlaying]) {    [player pause];  }}- (void)stopMusic:(NSString *)filename{  if (filename == nil || filename.length == 0) return;     AVAudioPlayer *player = self.musicPlayers[filename];     [player stop];     [self.musicPlayers removeObjectForKey:filename];} //播放音效- (void)playSound:(NSString *)filename{  if (!filename) return;     //取出對應的音效ID  SystemSoundID soundID = (int)[self.soundIDs[filename] unsignedLongValue];     if (!soundID) {    NSURL *url = [[NSBundle mainBundle] URLForResource:filename withExtension:nil];    if (!url) return;         AudioServicesCreateSystemSoundID((__bridge CFURLRef)(url), &soundID);         self.soundIDs[filename] = @(soundID);  }     // 播放  AudioServicesPlaySystemSound(soundID);} //摧毀音效- (void)disposeSound:(NSString *)filename{  if (!filename) return;        SystemSoundID soundID = (int)[self.soundIDs[filename] unsignedLongValue];     if (soundID) {    AudioServicesDisposeSystemSoundID(soundID);         [self.soundIDs removeObjectForKey:filename];  //音效被摧毀,那么對應的對象應該從緩存中移除  }}@end

 就是一個單例的設計,并沒有多大難度。我是用了一個字典來裝播放過的歌曲了,這樣如果是暫停了,然后再開始播放,就直接在緩存中加載即可。但是如果不注意,在 stopMusic:(NSString *)fileName  這個方法里面,不從字典中移除掉已經停止播放的歌曲,那么你下再播放這首歌的時候,就會在原先播放的進度上繼續播放。在編碼過程中,我就遇到了這個Bug,然后發現,在切換歌曲(上一曲、下一曲)的時候,我調用的是stopMusic方法,但由于我沒有從字典中將它移除,而導致它總是從上一次的進度開始播放,而不是從頭開始播放。

如果在真機上想要后臺播放歌曲,除了在appDelegate以及plist里面做相應操作之外,還得將播放模式設置為:AVAudioSessionCategoryPlayback。特別需要注意這里,我在模擬器上調試的時候,沒有設置這種模式也是可以進行后臺播放的,但是在真機上卻不行了。后來在StackOverFlow上找到了對應的答案,需要設置播放模式。

這個單例類,在整個demo中是至關重要的,要保證它是沒有錯誤的,所以我寫了這個類的XCTest進行單元測試,代碼如下:

#import <XCTest/XCTest.h>#import "ZYAudioManager.h"#import <AVFoundation/AVFoundation.h> @interface ZYAudioManagerTests : XCTestCase@property (nonatomic, strong) AVAudioPlayer *player;@endstatic NSString *_fileName = @"10405520.mp3";@implementation ZYAudioManagerTests - (void)setUp {  [super setUp];  // Put setup code here. This method is called before the invocation of each test method in the class.} - (void)tearDown {  // Put teardown code here. This method is called after the invocation of each test method in the class.  [super tearDown];} - (void)testExample {  // This is an example of a functional test case.  // Use XCTAssert and related functions to verify your tests produce the correct results.} /** * 測試是否為單例,要在并發條件下測試 */- (void)testAudioManagerSingle{  NSMutableArray *managers = [NSMutableArray array];     dispatch_group_t group = dispatch_group_create();     dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{    ZYAudioManager *tempManager = [[ZYAudioManager alloc] init];    [managers addObject:tempManager];  });     dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{    ZYAudioManager *tempManager = [[ZYAudioManager alloc] init];    [managers addObject:tempManager];  });     dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{    ZYAudioManager *tempManager = [[ZYAudioManager alloc] init];    [managers addObject:tempManager];  });     dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{    ZYAudioManager *tempManager = [[ZYAudioManager alloc] init];    [managers addObject:tempManager];  });     dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{    ZYAudioManager *tempManager = [[ZYAudioManager alloc] init];    [managers addObject:tempManager];  });     ZYAudioManager *managerOne = [ZYAudioManager defaultManager];     dispatch_group_notify(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{         [managers enumerateObjectsUsingBlock:^(ZYAudioManager *obj, NSUInteger idx, BOOL * _Nonnull stop) {      XCTAssertEqual(managerOne, obj, @"ZYAudioManager is not single");    }];       });} /** * 測試是否可以正常播放音樂 */- (void)testPlayingMusic{  self.player = [[ZYAudioManager defaultManager] playingMusic:_fileName];  XCTAssertTrue(self.player.playing, @"ZYAudioManager is not PlayingMusic");} /** * 測試是否可以正常停止音樂 */- (void)testStopMusic{  if (self.player == nil) {    self.player = [[ZYAudioManager defaultManager] playingMusic:_fileName];  }     if (self.player.playing == NO) [self.player play];     [[ZYAudioManager defaultManager] stopMusic:_fileName];  XCTAssertFalse(self.player.playing, @"ZYAudioManager is not StopMusic");} /** * 測試是否可以正常暫停音樂 */- (void)testPauseMusic{  if (self.player == nil) {    self.player = [[ZYAudioManager defaultManager] playingMusic:_fileName];  }  if (self.player.playing == NO) [self.player play];  [[ZYAudioManager defaultManager] pauseMusic:_fileName];  XCTAssertFalse(self.player.playing, @"ZYAudioManager is not pauseMusic");} @end

需要注意的是,單例要在并發的條件下測試,我采用的是dispatch_group,主要是考慮到,必須要等待所有并發結束才能比較結果,否則可能會出錯。比如說,并發條件下,x線程已經執行完畢了,它所對應的a對象已有值;而y線程還沒開始初始化,它所對應的b對象還是為nil,為了避免這種條件的產生,我采用dispatch_group來等待所有并發結束,再去做相應的判斷。

首頁控制器的代碼:

 #import "ZYMusicViewController.h"#import "ZYPlayingViewController.h"#import "ZYMusicTool.h"#import "ZYMusic.h"#import "ZYMusicCell.h" @interface ZYMusicViewController ()@property (nonatomic, strong) ZYPlayingViewController *playingVc; @property (nonatomic, assign) int currentIndex;@end @implementation ZYMusicViewController - (ZYPlayingViewController *)playingVc{  if (_playingVc == nil) {    _playingVc = [[ZYPlayingViewController alloc] initWithNibName:@"ZYPlayingViewController" bundle:nil];  }  return _playingVc;} - (void)viewDidLoad {  [super viewDidLoad];     [self setupNavigation];} - (void)setupNavigation{  self.navigationItem.title = @"音樂播放器";} #pragma mark ----TableViewDataSource- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {   return 1;} - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {  return [ZYMusicTool musics].count;} - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{  ZYMusicCell *cell = [ZYMusicCell musicCellWithTableView:tableView];  cell.music = [ZYMusicTool musics][indexPath.row];  return cell;} #pragma mark ----TableViewDelegate- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{  return 70;} - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{  [tableView deselectRowAtIndexPath:indexPath animated:YES];     [ZYMusicTool setPlayingMusic:[ZYMusicTool musics][indexPath.row]];     ZYMusic *preMusic = [ZYMusicTool musics][self.currentIndex];  preMusic.playing = NO;  ZYMusic *music = [ZYMusicTool musics][indexPath.row];  music.playing = YES;  NSArray *indexPaths = @[              [NSIndexPath indexPathForItem:self.currentIndex inSection:0],              indexPath              ];  [self.tableView reloadRowsAtIndexPaths:indexPaths withRowAnimation:UITableViewRowAnimationNone];     self.currentIndex = (int)indexPath.row;     [self.playingVc show];} @end 

重點需要說說的是這個界面的實現:

 這里做了比較多的細節控制,具體在代碼里面有相應的描述。主要是想說說,在實現播放進度拖拽中遇到的問題。

控制進度條的移動,我采用的是NSTimer,添加了一個定時器,并且在不需要它的地方都做了相應的移除操作。

這里開發的時候,遇到了一個問題是,我拖動滑塊的時候,發現歌曲播放的進度是不正確的。代碼中可以看到:

//得到挪動距離  CGPoint point = [sender translationInView:sender.view];  //將translation清空,免得重復疊加  [sender setTranslation:CGPointZero inView:sender.view];

 在使用translation的時候,一定要記住,每次處理過后,一定要將translation清空,以免它不斷疊加。

我使用的是ZYLrcView來展示歌詞界面的,需要注意的是,它繼承自UIImageView,所以要將userInteractionEnabled屬性設置為Yes。

代碼:

#import <UIKit/UIKit.h> @interface ZYLrcView : UIImageView@property (nonatomic, assign) NSTimeInterval currentTime;@property (nonatomic, copy) NSString *fileName;@end   #import "ZYLrcView.h"#import "ZYLrcLine.h"#import "ZYLrcCell.h"#import "UIView+AutoLayout.h" @interface ZYLrcView () <UITableViewDataSource, UITableViewDelegate>@property (nonatomic, weak) UITableView *tableView;@property (nonatomic, strong) NSMutableArray *lrcLines;/** * 記錄當前顯示歌詞在數組里面的index */@property (nonatomic, assign) int currentIndex;@end @implementation ZYLrcView #pragma mark ----setter/geter方法 - (NSMutableArray *)lrcLines{  if (_lrcLines == nil) {    _lrcLines = [ZYLrcLine lrcLinesWithFileName:self.fileName];  }  return _lrcLines;} - (void)setFileName:(NSString *)fileName{  if ([_fileName isEqualToString:fileName]) {    return;  }  _fileName = [fileName copy];  [_lrcLines removeAllObjects];  _lrcLines = nil;  [self.tableView reloadData];} - (void)setCurrentTime:(NSTimeInterval)currentTime{  if (_currentTime > currentTime) {    self.currentIndex = 0;  }  _currentTime = currentTime;     int minute = currentTime / 60;  int second = (int)currentTime % 60;  int msecond = (currentTime - (int)currentTime) * 100;  NSString *currentTimeStr = [NSString stringWithFormat:@"%02d:%02d.%02d", minute, second, msecond];     for (int i = self.currentIndex; i < self.lrcLines.count; i++) {    ZYLrcLine *currentLine = self.lrcLines[i];    NSString *currentLineTime = currentLine.time;    NSString *nextLineTime = nil;         if (i + 1 < self.lrcLines.count) {      ZYLrcLine *nextLine = self.lrcLines[i + 1];      nextLineTime = nextLine.time;    }         if (([currentTimeStr compare:currentLineTime] != NSOrderedAscending) && ([currentTimeStr compare:nextLineTime] == NSOrderedAscending) && (self.currentIndex != i)) {                    NSArray *reloadLines = @[                   [NSIndexPath indexPathForItem:self.currentIndex inSection:0],                   [NSIndexPath indexPathForItem:i inSection:0]                   ];      self.currentIndex = i;      [self.tableView reloadRowsAtIndexPaths:reloadLines withRowAnimation:UITableViewRowAnimationNone];                    [self.tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForItem:self.currentIndex inSection:0] atScrollPosition:UITableViewScrollPositionTop animated:YES];    }       }}#pragma mark ----初始化方法 - (instancetype)initWithFrame:(CGRect)frame{  if (self = [super initWithFrame:frame]) {    [self commitInit];  }  return self;} - (instancetype)initWithCoder:(NSCoder *)aDecoder{  if (self = [super initWithCoder:aDecoder]) {    [self commitInit];  }  return self;} - (void)commitInit{  self.userInteractionEnabled = YES;  self.image = [UIImage imageNamed:@"28131977_1383101943208"];  self.contentMode = UIViewContentModeScaleToFill;  self.clipsToBounds = YES;  UITableView *tableView = [[UITableView alloc] init];  tableView.delegate = self;  tableView.dataSource = self;  tableView.separatorStyle = UITableViewCellSeparatorStyleNone;  tableView.backgroundColor = [UIColor clearColor];  self.tableView = tableView;  [self addSubview:tableView];  [self.tableView autoPinEdgesToSuperviewEdgesWithInsets:UIEdgeInsetsMake(0, 0, 0, 0)];} #pragma mark ----UITableViewDataSource - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView{  return 1;} - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{  return self.lrcLines.count;} - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{  ZYLrcCell *cell = [ZYLrcCell lrcCellWithTableView:tableView];  cell.lrcLine = self.lrcLines[indexPath.row];     if (indexPath.row == self.currentIndex) {         cell.textLabel.font = [UIFont boldSystemFontOfSize:16];  }  else{    cell.textLabel.font = [UIFont systemFontOfSize:13];  }  return cell;} - (void)layoutSubviews{  [super layoutSubviews];   //  NSLog(@"++++++++++%@",NSStringFromCGRect(self.tableView.frame));  self.tableView.contentInset = UIEdgeInsetsMake(self.frame.size.height / 2, 0, self.frame.size.height / 2, 0);}@end

 也沒有什么好說的,整體思路就是,解析歌詞,將歌詞對應的播放時間、在當前播放時間的那句歌詞一一對應,然后持有一個歌詞播放的定時器,每次給ZYLrcView傳入歌曲播放的當前時間,如果,歌曲的currentTime > 當前歌詞的播放,并且小于下一句歌詞的播放時間,那么就是播放當前的這一句歌詞了。

我這里做了相應的優化,CADisplayLink生成的定時器,是每毫秒調用觸發一次,1s等于1000ms,如果不做一定的優化,性能是非常差的,畢竟一首歌怎么也有四五分鐘。在這里,我記錄了上一句歌詞的index,那么如果正常播放的話,它去查找歌詞應該是從上一句播放的歌詞在數組里面的索引開始查找,這樣就優化了很多。

這是鎖屏下的界面展示:

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持武林網。

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
久久视频免费观看| 欧美日韩电影在线观看| 超碰日本道色综合久久综合| 中文字幕精品一区二区精品| 欧美激情第1页| 国产亚洲欧美视频| 日韩欧美亚洲范冰冰与中字| 国产91精品视频在线观看| 91九色国产社区在线观看| 久久视频在线看| 色偷偷88888欧美精品久久久| 亚洲大胆人体在线| 2020欧美日韩在线视频| 在线视频国产日韩| 久久视频国产精品免费视频在线| 最新91在线视频| 国语自产精品视频在免费| 国语自产精品视频在线看一大j8| 欧美区在线播放| 欧美成人sm免费视频| 国产一区二区三区精品久久久| 久久久国产一区| 秋霞成人午夜鲁丝一区二区三区| 亚洲高清福利视频| 久久的精品视频| 欧美激情性做爰免费视频| 国产精品扒开腿做爽爽爽视频| 久久综合久中文字幕青草| 日韩av中文在线| 97视频在线观看免费高清完整版在线观看| 情事1991在线| 国产日韩精品一区二区| 日韩在线视频二区| 欧美国产日韩一区二区在线观看| 69国产精品成人在线播放| 国产精品视频yy9099| 96精品久久久久中文字幕| 久久久中精品2020中文| 日本精品久久中文字幕佐佐木| 精品国产依人香蕉在线精品| 欧美一级大片在线观看| 亚洲偷欧美偷国内偷| 日韩欧美在线视频日韩欧美在线视频| 欧美日韩亚洲一区二区三区| xvideos成人免费中文版| 久久亚洲综合国产精品99麻豆精品福利| 国产美女扒开尿口久久久| 日韩免费视频在线观看| www.xxxx欧美| 日韩成人av在线| 狠狠色香婷婷久久亚洲精品| 亚洲欧美国内爽妇网| 国产丝袜高跟一区| 久久久久久久国产| 黄网动漫久久久| 91成人精品网站| 黄色精品一区二区| 日韩有码在线视频| 日韩欧美一区二区三区| 色综合视频一区中文字幕| 91精品国产成人www| 色偷偷88888欧美精品久久久| 97国产精品免费视频| 欧美激情视频三区| 精品久久久久久电影| 久久免费国产视频| 亚洲电影成人av99爱色| 8090成年在线看片午夜| 国产一区二区三区久久精品| 国产亚洲精品日韩| 亚洲福利在线观看| 亚洲国产欧美日韩精品| 岛国视频午夜一区免费在线观看| 国产成人精品免高潮在线观看| 久久视频这里只有精品| 亚洲成人激情在线观看| 久久久久久久久久国产精品| 欧美xxxx做受欧美.88| 久久精品中文字幕| 国产精品你懂得| 久久精品99久久久香蕉| 久久在线免费视频| 国产欧美精品久久久| 欧美激情国产精品| 国产精品视频导航| 狠狠综合久久av一区二区小说| 海角国产乱辈乱精品视频| 欧美国产极速在线| 欧美日韩国产精品一区二区不卡中文| 日韩的一区二区| 国产精品美乳在线观看| 97免费在线视频| 亚洲美女视频网| 日韩欧美综合在线视频| 热99精品只有里视频精品| 亚洲女人天堂色在线7777| 国产黑人绿帽在线第一区| 中文字幕亚洲一区二区三区| 亚洲丁香婷深爱综合| 欧美一级视频免费在线观看| 国产成人精品免费久久久久| 国产精品爽爽ⅴa在线观看| 亚洲精品国产免费| 欧美天天综合色影久久精品| 国内精品一区二区三区| 国产精品网站大全| 国产有码一区二区| 国产福利精品视频| 欧美日韩亚洲系列| 久热精品视频在线观看一区| 一个色综合导航| 九九热这里只有在线精品视| 久久亚洲精品国产亚洲老地址| 欧美精品激情视频| 97视频免费在线看| 91精品在线观| 国产视频久久久久久久| 91免费观看网站| 成人黄色短视频在线观看| 亚洲视频日韩精品| 亚洲香蕉成人av网站在线观看| 亚洲一区国产精品| 伊人久久精品视频| 久久综合久久美利坚合众国| 欧美性资源免费| 国产精品极品美女在线观看免费| 欧美日韩国产精品一区二区三区四区| 久久久人成影片一区二区三区| 久久久久久高潮国产精品视| 国产日韩欧美91| 在线播放日韩欧美| 国产精品专区h在线观看| 日本久久久久久久久久久| 日韩成人在线视频网站| 综合网日日天干夜夜久久| 亚洲国产精品悠悠久久琪琪| 91最新国产视频| 亚洲人成毛片在线播放| 久久久噜噜噜久久中文字免| 日本精品性网站在线观看| 欧美专区在线播放| 亚洲最大的av网站| 高清欧美性猛交xxxx黑人猛交| yw.139尤物在线精品视频| 日本午夜在线亚洲.国产| 啪一啪鲁一鲁2019在线视频| 国产v综合v亚洲欧美久久| 久久99精品久久久久久琪琪| 国产精品欧美久久久| 国产亚洲欧洲黄色| 伊人久久免费视频| 亚洲老头老太hd| 高清视频欧美一级| 国产日本欧美视频| 欧美日韩国产一区二区三区| 国产精品久久久av久久久| 国产精品欧美一区二区三区奶水| 久久久久久久久久久网站| 成人a免费视频| 中文字幕日韩精品在线| 一区二区三区久久精品| 亚洲sss综合天堂久久| 日韩日本欧美亚洲| 欧美视频13p|