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~