利用NSURLProtocol和本地代理实现在线视频边播放边缓存
2015-08-28 17:34
573 查看
Wrote By Fanxiushu 2015-08-28,引用或转载请注明原始作者
接上文,
因为真机上,好像是iOS7以后的系统,AVPlayer和MPMovie等iOS自带的视频播放控件,
虽然表面上还是使用 NSURL的概念,但是在内部并不是通过 URL Loading System,
应该是直接通过更底层的socket来通讯获取服务端视频数据,并不清楚苹果这么做的理由,估计是为了效率吧。
我们要实现一边播放在线视频,一边还得缓存视频数据;就得想方设法在播放视频过程中获取视频数据,然后缓存起来。
因为AVPlayer或MPMovie等iOS自带的多媒体播放控件并没有提供获取正在播放视频数据的接口,
所以得从网络通信这个地方,想法获取视频文件数据。
既然不能直接从URL Loading System截获到数据,那么就绕一个圈。
实现一个本地代理服务,让AVPlayer对视频的数据的请求都发送到代理,
代理再把请求发到真正的服务端,
然后一边从真正服务端拉取视频数据,一边缓存视频数据,一边再发给AVPlayer等控件。
如果我们在代理服务中, 发送请求到真正视频服务端的时候,使用 NSURLConnection接口,
那么很显然,网络通信数据流再次进入到 URL Loading System,
从而通过 子类化的 NSURLProtocol可以拦截和缓存到这个视频数据流。
这样前一篇中实现的 NSURLProtocol 就再次发挥了作用。
当然 AVPlayer等播放控件是使用断点续传(也就是206请求)方式获取视频数据的。
我们也可以直接在代理服务中获取视频数据,然后自己缓存和处理,反正效果都差不多。
但是我更喜欢使用NSURLProtocol,因为它掌控着全局,能缓存程序的所有NSURL网络请求,只要你愿意缓存的话。
这里重点说说这个代理服务的开发。其实不要把他想象的多复杂,
只要具备基本的 socket编程基础和基本的HTTP协议概念,就能自己实现这个代理服务。
当然你也可以使用网络上现有的代理开源代码,比如 HTTPServer等。但这不是我的习惯,
因为发现自己实现一个简单代理,远比使用别人一堆的代码,还得研究如何使用,还得担心BUG来的更加简洁和方便。
使用c开发,objc的好处就是能在其中嵌入大家都熟悉的c代码,而不会出现编译错误。
由于是个简单的代理服务端,所以采用多线程模式就可以了,一个连接一个线程。
int fd = socket(AF_INET,SOCK_STREAM,0);
bind(fd,....); ///// 绑定到 127.0.0.1的随意一个端口,比如 端口 12345 。
listen(fd,5);
while(1){
int s = accept(fd,0,0);
/////开启新线程处理新到来的连接。
dispatch_async(dispatch_get_global_queue(0,0), ^{
////
do_client(s); /////处理每个到来的连接,在这里负责把请求转发到真正的视频服务端,
同时负责把从服务端获得的数据转发给 s。
/////
});
///////////
}
以上就是简单的代理服务框架。 重点是在 do_client函数的处理。
为了简单,把真正要请求的URL直接作为本地代理请求的参数。比如如下:
一个真实的URL: http://v.sohu.com/video/a.mp4,
给 AVPlayer或MPMovie等控件的参数就是 http://127.0.0.1:12345/http://v.sohu.com/video/a.mp4
void do_client(int s)
{
///////这里首先接收AVPlayer或MPMovie等控件发来的视频请求头,其实就是HTTP请求头,
这里只考虑 GET请求,毫无疑问,几乎都是GET请求。
char buf[4096];
int pos = 0;
while(pos<4096){
int r = (int)recv( s, (buf + pos), (size_t)(4096-pos), 0);
if(r<=0){
NSLog(@"**** proxyServer: Recv Header Error.");
close(s);
return ;
}
pos += r;
buf[pos] = 0;
char* ptr = strstr(buf,"\r\n\r\n");
if( ptr ){
*(ptr+2)=0;
break;
}
}
////////////////////////解析HTTP协议头,从中分析出真正请求的URL,同时如果请求是 断点续传请求的话,还得分析出断点的信息。
char* hdr = buf;
char* range = NULL;
const char* t="Range: "; int ll = (int)strlen(t);
while(hdr){
char* e = strstr( hdr, "\r\n"); if(!e)break;
*e = 0;
///
// NSLog(@"[%s]", hdr);
if(strncasecmp(hdr, t, ll ) ==0 ){
range = hdr + ll;
}
///
hdr = e+2; ////
}
////////
char* uri = strchr( buf,' '); if(!uri) {close(s);return;}
uri++;
char* end = strchr( uri, ' '); if(!end){close(s);return;}
*end = 0;
//////////////////////////////////然后就是发起真正的网络请求,这里使用 NSURLConnection来发起网络请求,
这样所有网络数据都会经过 NSURLProtocol 协议。
NSString* strurl = [NSString stringWithUTF8String:(uri+1)]; ////
NSMutableURLRequest* request = [[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:strurl]
cachePolicy:NSURLRequestReloadIgnoringLocalCacheData
timeoutInterval:10.0];
if(range){///如果是断点续传请求,还必须标志这个断点
[request setValue:[NSString stringWithUTF8String:range] forHTTPHeaderField:@"Range"];
//////
}
ProxyPerClient* proxy = [[ProxyPerClient alloc]init]; ////这个是代理类,在这个代理类无非就是把接收到数据发送给AVPlayer等控件。
proxy.sock = s;
//////
NSURLConnection* conn = [[NSURLConnection alloc] initWithRequest:request delegate:proxy startImmediately:NO];
proxy.conn = conn;
[conn setDelegateQueue:[[NSOperationQueue alloc]init]]; /// enter Queue for execute
////////
。。。。。。。。。
}
至于 ProxyPerClient如何实现,我就不贴代码了,稍后可以从CSDN上下载这个代理服务的代码,
代码如果要实现边播放边缓存功能,需要配合 NSURLProtocol协议的代码。
至此 ,基本就完成了一个简单的代理服务的开发工作。
对应资源
http://download.csdn.net/detail/fanxiushu/9059053
接上文,
因为真机上,好像是iOS7以后的系统,AVPlayer和MPMovie等iOS自带的视频播放控件,
虽然表面上还是使用 NSURL的概念,但是在内部并不是通过 URL Loading System,
应该是直接通过更底层的socket来通讯获取服务端视频数据,并不清楚苹果这么做的理由,估计是为了效率吧。
我们要实现一边播放在线视频,一边还得缓存视频数据;就得想方设法在播放视频过程中获取视频数据,然后缓存起来。
因为AVPlayer或MPMovie等iOS自带的多媒体播放控件并没有提供获取正在播放视频数据的接口,
所以得从网络通信这个地方,想法获取视频文件数据。
既然不能直接从URL Loading System截获到数据,那么就绕一个圈。
实现一个本地代理服务,让AVPlayer对视频的数据的请求都发送到代理,
代理再把请求发到真正的服务端,
然后一边从真正服务端拉取视频数据,一边缓存视频数据,一边再发给AVPlayer等控件。
如果我们在代理服务中, 发送请求到真正视频服务端的时候,使用 NSURLConnection接口,
那么很显然,网络通信数据流再次进入到 URL Loading System,
从而通过 子类化的 NSURLProtocol可以拦截和缓存到这个视频数据流。
这样前一篇中实现的 NSURLProtocol 就再次发挥了作用。
当然 AVPlayer等播放控件是使用断点续传(也就是206请求)方式获取视频数据的。
我们也可以直接在代理服务中获取视频数据,然后自己缓存和处理,反正效果都差不多。
但是我更喜欢使用NSURLProtocol,因为它掌控着全局,能缓存程序的所有NSURL网络请求,只要你愿意缓存的话。
这里重点说说这个代理服务的开发。其实不要把他想象的多复杂,
只要具备基本的 socket编程基础和基本的HTTP协议概念,就能自己实现这个代理服务。
当然你也可以使用网络上现有的代理开源代码,比如 HTTPServer等。但这不是我的习惯,
因为发现自己实现一个简单代理,远比使用别人一堆的代码,还得研究如何使用,还得担心BUG来的更加简洁和方便。
使用c开发,objc的好处就是能在其中嵌入大家都熟悉的c代码,而不会出现编译错误。
由于是个简单的代理服务端,所以采用多线程模式就可以了,一个连接一个线程。
int fd = socket(AF_INET,SOCK_STREAM,0);
bind(fd,....); ///// 绑定到 127.0.0.1的随意一个端口,比如 端口 12345 。
listen(fd,5);
while(1){
int s = accept(fd,0,0);
/////开启新线程处理新到来的连接。
dispatch_async(dispatch_get_global_queue(0,0), ^{
////
do_client(s); /////处理每个到来的连接,在这里负责把请求转发到真正的视频服务端,
同时负责把从服务端获得的数据转发给 s。
/////
});
///////////
}
以上就是简单的代理服务框架。 重点是在 do_client函数的处理。
为了简单,把真正要请求的URL直接作为本地代理请求的参数。比如如下:
一个真实的URL: http://v.sohu.com/video/a.mp4,
给 AVPlayer或MPMovie等控件的参数就是 http://127.0.0.1:12345/http://v.sohu.com/video/a.mp4
void do_client(int s)
{
///////这里首先接收AVPlayer或MPMovie等控件发来的视频请求头,其实就是HTTP请求头,
这里只考虑 GET请求,毫无疑问,几乎都是GET请求。
char buf[4096];
int pos = 0;
while(pos<4096){
int r = (int)recv( s, (buf + pos), (size_t)(4096-pos), 0);
if(r<=0){
NSLog(@"**** proxyServer: Recv Header Error.");
close(s);
return ;
}
pos += r;
buf[pos] = 0;
char* ptr = strstr(buf,"\r\n\r\n");
if( ptr ){
*(ptr+2)=0;
break;
}
}
////////////////////////解析HTTP协议头,从中分析出真正请求的URL,同时如果请求是 断点续传请求的话,还得分析出断点的信息。
char* hdr = buf;
char* range = NULL;
const char* t="Range: "; int ll = (int)strlen(t);
while(hdr){
char* e = strstr( hdr, "\r\n"); if(!e)break;
*e = 0;
///
// NSLog(@"[%s]", hdr);
if(strncasecmp(hdr, t, ll ) ==0 ){
range = hdr + ll;
}
///
hdr = e+2; ////
}
////////
char* uri = strchr( buf,' '); if(!uri) {close(s);return;}
uri++;
char* end = strchr( uri, ' '); if(!end){close(s);return;}
*end = 0;
//////////////////////////////////然后就是发起真正的网络请求,这里使用 NSURLConnection来发起网络请求,
这样所有网络数据都会经过 NSURLProtocol 协议。
NSString* strurl = [NSString stringWithUTF8String:(uri+1)]; ////
NSMutableURLRequest* request = [[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:strurl]
cachePolicy:NSURLRequestReloadIgnoringLocalCacheData
timeoutInterval:10.0];
if(range){///如果是断点续传请求,还必须标志这个断点
[request setValue:[NSString stringWithUTF8String:range] forHTTPHeaderField:@"Range"];
//////
}
ProxyPerClient* proxy = [[ProxyPerClient alloc]init]; ////这个是代理类,在这个代理类无非就是把接收到数据发送给AVPlayer等控件。
proxy.sock = s;
//////
NSURLConnection* conn = [[NSURLConnection alloc] initWithRequest:request delegate:proxy startImmediately:NO];
proxy.conn = conn;
[conn setDelegateQueue:[[NSOperationQueue alloc]init]]; /// enter Queue for execute
////////
。。。。。。。。。
}
至于 ProxyPerClient如何实现,我就不贴代码了,稍后可以从CSDN上下载这个代理服务的代码,
代码如果要实现边播放边缓存功能,需要配合 NSURLProtocol协议的代码。
至此 ,基本就完成了一个简单的代理服务的开发工作。
对应资源
http://download.csdn.net/detail/fanxiushu/9059053
相关文章推荐
- 29、Jquery Ajax
- UISwitch
- 自定义Window 服务
- python 实现 RPC 通信
- django 1.8 官方文档翻译: 3-1-4 视图装饰器
- 三维立体数据库系统
- iOS开发——创建你自己的Framework
- oracle 正则表达式 ( 前端传入连续字符串分析..)
- Mac Cornerstone无法上传.a文件
- UITextField
- UITextView
- 用OC语⾔言完成简易通讯录(实现增删改查)功 能. (注:使用MRC)
- LINUX之旅
- nginx基本介绍
- Linux系统安装apache
- HDU 1257 最少拦截系统
- HDU 4465 Candy(概率)
- iOS中绘制圆形的函数方法
- Java中的匿名对象
- UIView的layoutSubviews和drawRect方法何时调用