iOS中關於流媒體的簡介:介於下載本地播放與實時流媒體之間的一種播放形式,下載本地播放必須全部將文件下載完成後才能播放,而漸進式下載不必等到全部下載完成後再播放,它可以一邊下載一邊播放,在完成播放內容之後,整個文件會保存在手機上。
實時流媒體
實時流媒體是一邊接收數據包一邊播放,本地不保留文件副本,實時流式傳輸總是實時傳送,可以實時實況轉播,支持隨機訪問,用戶可以快進或者快退以觀看前面或後面的內容。實時流媒體傳輸必須保證數據包的傳輸速度大於文件的播放速度,否則用戶看到的視頻會出現暫停。當網絡堵塞情況下視頻質量會下降,所以要想保證視頻的質量漸進式下載會更好一些。
下面是本人親測的流媒體播放和下載教程:
1.界面搭建(如圖)
2.用到的第三方助手類
下載地址:http://pan.baidu.com/s/1hrvqXA8
3.開始項目-頭文件及相關宏
LO_ViewController.h
#import#import #import "M3U8Handler.h" #import "VideoDownloader.h" #import "HTTPServer.h" @interface LO_ViewController : UIViewController @property (nonatomic, strong)HTTPServer * httpServer; @property (nonatomic, strong)VideoDownloader *downloader; @end
LO_ViewController.m
#import "LO_ViewController.h" @interface LO_ViewController () @property (weak, nonatomic) IBOutlet UIProgressView *progressView; @property (weak, nonatomic) IBOutlet UILabel *progressLabel; @property (weak, nonatomic) IBOutlet UIButton *downloadButton; @property (weak, nonatomic) IBOutlet UIButton *clearButton; @end @implementation LO_ViewController - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view, typically from a nib. //打開本地服務器 [self openHttpServer]; if ([[[NSUserDefaults standardUserDefaults] objectForKey:@"isDownload"] boolValue]) { [self.downloadButton setTitle:@"已完成" forState:UIControlStateNormal]; self.downloadButton.enabled = NO; self.clearButton.enabled = YES; M3U8Handler *handler = [[M3U8Handler alloc] init]; [handler praseUrl:[NSString stringWithFormat:@"http://v.youku.com/player/getM3U8/vid/XNjUxMTE4NDAw/type/mp4"]]; handler.playlist.uuid = @"XNjUxMTE4NDAw"; self.downloader = [[VideoDownloader alloc]initWithM3U8List:handler.playlist]; [self.downloader addObserver:self forKeyPath:@"clearCaches" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:nil]; // 判斷是否清理緩存 } }
- (void)openHttpServer { self.httpServer = [[HTTPServer alloc] init]; [self.httpServer setType:@"_http._tcp."]; // 設置服務類型 [self.httpServer setPort:12345]; // 設置服務器端口 // 獲取本地Library/Cache路徑下downloads路徑 NSString *webPath = [kLibraryCache stringByAppendingPathComponent:kPathDownload]; NSLog(@"-------------\nSetting document root: %@\n", webPath); // 設置服務器路徑 [self.httpServer setDocumentRoot:webPath]; NSError *error; if(![self.httpServer start:&error]) { NSLog(@"-------------\nError starting HTTP Server: %@\n", error); } }
#pragma mark - 清理緩存 - (IBAction)clearCaches:(id)sender { [self.downloader cleanDownloadFiles]; }#pragma mark - 在線流媒體播放
- (IBAction)playStreamingMedia:(id)sender { // 優酷視頻m3u8新地址格式如下:http://pl.youku.com/playlist/m3u8?vid=XNjUxMTE4NDAw&type=mp4 // 如果上面的鏈接不可用,那麼使用這個鏈接http://v.youku.com/player/getM3U8/vid/XNjUxMTE4NDAw/type/mp4,如果兩個連接都不可以用的話,那麼很大可能是優酷的服務器掛掉了 // 如果上面的兩種格式都不行的話,考慮用這個格式,當然如果這個格式不行的話,是上面的,或者直接換個對應的m3u8的地址 http://pl.youku.com/playlist/m3u8?vid=162779600&ts=1407469897&ctype=12&token=3357&keyframe=1&sid=640746989782612d6cc70&ev=1&type=mp4&ep=dCaUHU2LX8YJ4ivdjj8bMyqxJ3APXP8M9BiCiNRiANQnS%2B24&oip=2043219268 NSURL *url = [[NSURL alloc] initWithString:@"http://pl.youku.com/playlist/m3u8?vid=162779600&ts=1407469897&ctype=12&token=3357&keyframe=1&sid=640746989782612d6cc70&ev=1&type=flv&ep=dCaUHU2LX8YJ4ivdjj8bMyqxJ3APXP8M9BiCiNRiANQnS%2B24&oip=2043219268"]; MPMoviePlayerViewController *player = [[MPMoviePlayerViewController alloc] initWithContentURL:url]; [self presentMoviePlayerViewControllerAnimated:player]; }#pragma mark - 視頻下載
- (IBAction)downloadStreamingMedia:(id)sender { UIButton *downloadButton = sender; // 獲取本地Library/Cache路徑 NSString *localDownloadsPath = [kLibraryCache stringByAppendingPathComponent:kPathDownload]; // 獲取視頻本地路徑 NSString *filePath = [localDownloadsPath stringByAppendingPathComponent:@"XNjUxMTE4NDAw/movie.m3u8"]; NSFileManager *fileManager = [NSFileManager defaultManager]; // 判斷視頻是否緩存完成,如果完成則播放本地緩存 if ([fileManager fileExistsAtPath:filePath]) { [downloadButton setTitle:@"已完成" forState:UIControlStateNormal]; downloadButton.enabled = NO; }else{ M3U8Handler *handler = [[M3U8Handler alloc] init]; handler.delegate = self; // 解析m3u8視頻地址 [handler praseUrl:@"http://pl.youku.com/playlist/m3u8?vid=162779600&ts=1407469897&ctype=12&token=3357&keyframe=1&sid=640746989782612d6cc70&ev=1&type=flv&ep=dCaUHU2LX8YJ4ivdjj8bMyqxJ3APXP8M9BiCiNRiANQnS%2B24&oip=2043219268"]; // 開啟網絡指示器 [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES]; } }#pragma mark - 播放本地緩存視頻
- (IBAction)playVideoFromLocal:(id)sender { NSString * playurl = [NSString stringWithFormat:@"http://127.0.0.1:12345/XNjUxMTE4NDAw/movie.m3u8"]; NSLog(@"本地視頻地址-----%@", playurl); // 獲取本地Library/Cache路徑 NSString *localDownloadsPath = [kLibraryCache stringByAppendingPathComponent:kPathDownload]; // 獲取視頻本地路徑 NSString *filePath = [localDownloadsPath stringByAppendingPathComponent:@"XNjUxMTE4NDAw/movie.m3u8"]; NSFileManager *fileManager = [NSFileManager defaultManager]; // 判斷視頻是否緩存完成,如果完成則播放本地緩存 if ([fileManager fileExistsAtPath:filePath]) { MPMoviePlayerViewController *playerViewController =[[MPMoviePlayerViewController alloc]initWithContentURL:[NSURL URLWithString: playurl]]; [self presentMoviePlayerViewControllerAnimated:playerViewController]; } else{ UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"Sorry" message:@"當前視頻未緩存" delegate:self cancelButtonTitle:@"確定" otherButtonTitles:nil, nil]; [alertView show]; } } #pragma mark - #pragma mark - 視頻解析完成 -(void)praseM3U8Finished:(M3U8Handler*)handler { handler.playlist.uuid = @"XNjUxMTE4NDAw"; self.downloader = [[VideoDownloader alloc]initWithM3U8List:handler.playlist]; [self.downloader addObserver:self forKeyPath:@"currentProgress" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:nil]; // 設置觀察者用來得到當前下載的進度 [self.downloader addObserver:self forKeyPath:@"clearCaches" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:nil]; // 判斷是否清理緩存 self.downloader.delegate = self; [self.downloader startDownloadVideo]; // 開始下載 }
#pragma mark - 通過觀察者監控下載進度顯示/緩存清理
-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { if ([keyPath isEqualToString:@"clearCaches"]) { self.downloadButton.enabled = YES; [self.downloadButton setTitle:@"下載" forState:UIControlStateNormal]; self.clearButton.enabled = NO; [[NSUserDefaults standardUserDefaults] setObject:[NSNumber numberWithBool:NO] forKey:@"isDownload"]; [[NSUserDefaults standardUserDefaults] synchronize]; self.progressView.progress = 0.0; self.progressLabel.text = [NSString stringWithFormat:@"%.2f%%", 0.0]; }else{ self.progressLabel.text = [NSString stringWithFormat:@"%.2f%%", 100 * [[change objectForKey:@"new"] floatValue]]; self.progressView.progress = [[change objectForKey:@"new"] floatValue]; if (self.progressView.progress == 1) { [[NSUserDefaults standardUserDefaults] setObject:[NSNumber numberWithBool:YES] forKey:@"isDownload"]; [self.downloadButton setTitle:@"已完成" forState:UIControlStateNormal]; [[NSUserDefaults standardUserDefaults] synchronize]; self.clearButton.enabled = YES; self.downloadButton.enabled = NO; } } }
#pragma mark - 視頻解析失敗 -(void)praseM3U8Failed:(M3U8Handler*)handler { NSLog(@"視頻解析失敗-failed -- %@",handler); UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"噢,NO~