关于流媒体(m3u8)的播放与下载
前一段时间做了一个视频播放下载应用,抓取的是优酷的视频,虽然优酷有自己的开发平台http://open.youku.com/,但未真正的实现。所以只能靠抓取视频源,Youku的视频采取了加密+动态的获取方式,视频地址需要访问网站动态获取,而结果则还需经过解密等操作。我们的目的只解析到网站视频的m3u8地址,好在在ios 的web可以实现:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 // 初始化webView 2 3 UIWebView *webView = [[UIWebView alloc] initWithFrame:CGRectMake(0, 0, 280, 390)]; 4 5 [webViewsetDelegate:self]; 6 7 //加载网址 8 9 NSURLRequest *request =[NSURLRequestrequestWithURL:url 10 11 cachePolicy:NSURLRequestReloadIgnoringLocalCacheData 12 13 timeoutInterval:5.0]; 14 15 [webView loadRequest:request]; 16 17 //在UIWebViewDelegate方法中解析 18 19 #pragma mark - UIWebViewDelegate Methods 20 21 - (void)webViewDidStartLoad:(UIWebView *)aWebView 22 { 23 [indicatorView startAnimating]; 24 } 25 26 - (void)webViewDidFinishLoad:(UIWebView *)aWebView 27 { 28 [indicatorView stopAnimating]; 29 /* 30 NSString *lJs = @"document.documentElement.innerHTML"; 31 NSString *lHtml1 = [webView stringByEvaluatingJavaScriptFromString:lJs]; 32 NSLog(@"html内容:%@",lHtml1); 33 34 // NSString *lJs2 = @"(document.getElementsByTagName(\"video\")[0]).getElementsByTagName(\"source\")[0].src"; //qiyi 35 NSString *lJs2 = @"(document.getElementsByTagName(\"video\")[0]).src";// youku,tudou,ku6 ,souhu 36 NSString *lm3u8 = [webView stringByEvaluatingJavaScriptFromString:lJs2]; 37 NSLog(@"video source:%@",lm3u8); 38 */ 39 40 NSString *str = nil; 41 BOOL isYoukuTudouSource = [self isYoukuTudouSource]; 42 if (isYoukuTudouSource){ 43 44 str = [webView stringByEvaluatingJavaScriptFromString:@"document.getElementsByTagName('video')[0].getAttribute('src');"]; 45 if (![str isEqualToString:@""]){ 46 47 NSString *tempStr =str; 48 //NSLog(@"======%@",tempStr); 49 NSRange range = [ str rangeOfString:@"http://"]; 50 if (range.length==0){ 51 range = [videolinkStr rangeOfString:@"youku.com"]; 52 if (range.length>0){ 53 str = [@"http://v.youku.com" stringByAppendingString:tempStr]; 54 } 55 } 56 } 57 }else{ 58 BOOL sourcemode = [self isSourceMode]; 59 if (sourcemode){ 60 str = [webView stringByEvaluatingJavaScriptFromString:@"document.getElementsByTagName('source')[0].getAttribute('src')"]; 61 } 62 } 63 if (IfPass == NO) {//设置BOOL类型的IfPass主要目的避免重复加载获取。 64 65 if (str && ![str isEqualToString:@""]) { 66 [webView stopLoading]; 67 if ([delegate respondsToSelector:@selector(authorizeWebView:didReceiveAuthorizeCode:)]) 68 { 69 NSLog(@"地址信息=======%@",str); 70 IfPass = YES; 71 [delegate authorizeWebView:self didReceiveAuthorizeCode:str]; 72 } 73 } 74 }else if (IfPass == YES){ 75 [webView stopLoading]; 76 //NSLog(@"地址信息=======%@",str); 77 } 78 //NSLog(@"Web获取视频地址信息=======%@",str); 79 } 80 81 - (void)webView:(UIWebView *)aWebView didFailLoadWithError:(NSError *)error 82 { 83 [indicatorView stopAnimating]; 84 } 85 86 - (BOOL)webView:(UIWebView *)aWebView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType 87 { 88 89 return YES; 90 } 91 92 93 -(BOOL) isSourceMode { 94 95 NSRange yinyuetai = [videolinkStr rangeOfString:@"yinyuetai.com"]; 96 NSRange iqiyi = [videolinkStr rangeOfString:@"iqiyi.com"]; 97 NSRange qq = [videolinkStr rangeOfString:@"qq.com"]; 98 return yinyuetai.length>4||iqiyi.length>4||qq.length>4; 99 } 100 101 - (BOOL) isYoukuTudouSource { 102 103 NSRange youku = [videolinkStr rangeOfString:@"youku.com"]; 104 NSRange tudou = [videolinkStr rangeOfString:@"tudou.com"]; 105 //NSLog(@"youku length====%d===tudou length===%d",youku.length,tudou.length); 106 return youku.length>4||tudou.length>4; 107 }
客户端,webView加载解析会有延迟,所以这种方式不推荐。其实后台可以通过技术手段得到m3u8.接下来就是调用苹果自己的播放器播放。我单独创建了一个MyMoviePlayViewController类继承自
MPMoviePlayerViewController类,以方便我应对ios6和ios6之前版本的屏幕旋转问题。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
self.myVideoUrlStr =@“视频m3u8地址”; //这里我需要查询下视频总时间 NSURL *movieURL = [NSURL URLWithString:self.myVideoUrlStr]; NSDictionary *opts = [NSDictionary dictionaryWithObject:[NSNumber numberWithBool:NO]forKey:AVURLAssetPreferPreciseDurationAndTimingKey]; AVURLAsset *urlAsset = [AVURLAsset URLAssetWithURL:movieURL options:opts]; // 初始化视频媒体文件 //int minute = 0; int second = 0; second = urlAsset.duration.value / urlAsset.duration.timescale; // 获取视频总时长,单位秒 //设置播放器监听事件 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(movieFinishedCallback:) name:MPMoviePlayerPlaybackDidFinishNotification object:self.moviePlayer]; //初始化视频媒体文件 MyMoviePlayViewController *moviePalyViewController = [[MyMoviePlayViewController alloc]initWithContentURL:[NSURL URLWithString:@""]]; [[moviePalyViewController view]setFrame:CGRectMake(0, 0, 320, self.view.frame.size.height*216/460)]; moviePalyViewController.moviePlayer.view.frame =CGRectMake(0, 0, 320, 216); //moviePalyViewController.view.frame = CGRectMake(0, 0, 320, self.view.frame.size.height*216/460); [moviePalyViewController getVideoWithURL:[NSURL URLWithString:self.myVideoUrlStr]]; //视频监听调用方法。主要用来记录用户所观看的时间 - (void)movieFinishedCallback:(NSNotification*) notification { //NSLog(@"111111111视频播放完毕"); MPMoviePlayerController *player = [notification object]; CGFloat videotimedurationWatched; NSArray *events = player.accessLog.events; int count = events.count; //NSLog(@"events count = %d", count); for (int i = 0; i < count; ++i) { MPMovieAccessLogEvent *currenEvent = [events objectAtIndex:i]; // double byts = currenEvent.indicatedBitrate ; //NSLog(@"5555555==视频播放当前时间======%f",currenEvent.durationWatched); videotimedurationWatched = currenEvent.durationWatched; } switch (player.playbackState) { case MPMoviePlaybackStateStopped:{ } break; case MPMoviePlaybackStatePlaying:{ //NSLog(@"Playing"); } break; case MPMoviePlaybackStatePaused:{ AccountModel *tempmodel = [AccountModel shareInstance]; tempmodel.videotimeWatched = videotimedurationWatched; //NSLog(@"Paused");//播放暂停,播放完毕调用 [self dismissModalViewControllerAnimated:YES]; return; } break; case MPMoviePlaybackStateInterrupted:{ //NSLog(@"Interrupted"); } break; case MPMoviePlaybackStateSeekingForward:{ //NSLog(@"Forward"); } break; case MPMoviePlaybackStateSeekingBackward:{ //NSLog(@"Backward"); } break; default: break; } [[NSNotificationCenter defaultCenter] removeObserver:self name:MPMoviePlayerPlaybackDidFinishNotification object:player]; }
有了在线播放功能还不够,还需要下载到本地播放,实现步骤如下:
(1) 在线解析m3u8文件内容,把里面的ts对应连接的资源下载本地的Document文件下。
(2) 把下载下来的资源使用本地路径重新拼接成一个新的本地m3u8文件。
(3) 然后在开启一个http服务端,把m3u8共享成连接地址,让播放器播放。
具体实现请查看这位仁兄给的Demo,开启一个http服务端使用到了http服务端,就用CocoaHTTPServer可以把工作做好 CocoaHTTPServer小,重量轻,可嵌入HTTP服务器的Mac OS X或iOS应用程序。
有时,开发人员在他们的应用程序需要一个嵌入式HTTP服务器。也许它是一个服务器应用程序的远程监控。也许这是一个桌面应用程序使用HTTP后端的通信。它提供了:
•内置支持卓悦广播
•IPv4和IPv6支持
·异步网络,使用GCD和标准插座
•密码保护支持
•SSL/ TLS加密支持
•非常快速和高效的内存
•可扩展性(完全建立在GCD)
•重注释掉的代码
•很容易扩展
•支持WebDAV!
下面 介绍有关视频格式的:
在线视频一般都是基于flash和flv来实现的,而众所周知,iOS的safari不支持网页中的flash,但支持html5的video标记。FLV视频格式:许多在线视频网站都采用此视频格式。如搜狐视频、新浪播客、六间房、56、优酷、酷6、土豆,youtube等。FLV已经成为当前视频文件的主流格式。
优酷开发了“优酷通用播放器”,您可以将视频嵌入到任意页面和设备中,这样视频就可以被各种终端用户观看,包括PC浏览器,iPad和iPhone,较高版本Android平板和手机。优酷播放器现在只有在线播放能力,不提供播放离线文件的功能。
iOS 设备上启用 HTTP Live Streaming 非常简单,也是苹果官方推荐的方式。Adobe 的 Flash 流媒体服务器的新版本也支持这个技术的。
有关HLS可以参考HLS直播分析与实现 ,搭建HTTP Live Streaming直播系统
posted on 2013-06-25 17:13 一梦浮生2012 阅读(15368) 评论(0) 编辑 收藏 举报