申明:这篇只是自己的一些研究,请不要用于商业用途。如果影响到了你的利益,请你联系我。
获取三方的视频,因为三方的视频地址可能几分钟就会失效。那么视频有可能播放不了几分钟就会播发不了。所以就需要在本地开启一个播放代理。(目前这个需要服务器配合,客户端不能够自己去三方的网站去解析视频),以下所有的操作都是建立在自己的服务器上进行。接下来会研究不需要依赖服务器,直接在本地解析优酷真正的地址.
用到三方库:GCDWebServer(拦截视频url请求),AFNetworking
原理:
1):本地开启HttpServer服务,去捕获到所有的视频播发请求。
2):获取到返回来视频的视频文件描述文件,将该描述文件存起来,并且解析出来所有视频片段的ts的url,保存所有的url。
3):将返回来的描述文件中真实的播放地址替换成本地的地址,将修改后的描述文件直接扔给播放器。目的:利用重定向,可以获取到所有经过本地服务器的请求的url地址。
4):因为播放器去请求每一个视频片段,本地就能捕获到这些地址。再用这些本地的地址去匹配我们保存的真实地址。映射出真实地址。
5):判断真实的地址是否是可用的。不可用,那么重复第一步(目的是更新本地真实的地址),可用,那么直接就扔给播放器去播放视频
上原理图:
直接上重要的代码:
一)解析出视频的每一个片段的的地址
1)首先开启本地HttpServer
self.webServer = [[GCDWebServer alloc] init];
[self.webServer addDefaultHandlerForMethod:@"GET" requestClass:[GCDWebServerRequest class] processBlock:^GCDWebServerResponse *(__kindof GCDWebServerRequest *request) {
}
return [GCDWebServerDataResponse responseWithRedirect:request.URL permanent:YES];
}];
[self.webServer startWithPort:[self.port integerValue] bonjourName:nil];
2)根据视频地址解析出视频的描述文件
NSError *error;
NSStringEncoding encoding;
NSString *dataString = [[NSString alloc] initWithContentsOfURL:url usedEncoding:&encoding error:&error];
3)判断是否解析成功
if(dataString.length<=0)//获取切片失败 {
NSError *formatError = [[NSError alloc] initWithDomain:@"NSDataFormateError" code:8889 userInfo:@{@"info":@"获取数据内容失败"}];
[self.delegate M3U8Handler:self praseError:formatError];
}
4)判断解析出来的文件格式是否是正确的
NSRange range = [dataString rangeOfString:@"#EXTINF"];
if (range.location == NSNotFound) {
if ([self.delegate respondsToSelector:@selector(M3U8Handler:praseError:)]) {
NSError *formatError = [[NSError alloc] initWithDomain:@"NSDataFormateError" code:8888 userInfo:@{@"info":@"数据格式错误"}];
[self.delegate M3U8Handler:self praseError:formatError];
}
return;
}
5)解析出mei一个片段的真正的地址
- (void)tsInfoWithM3U8String:(NSString *)m3u8Str baseUrlStr:(NSString *)baseUrlStr{
NSArray *components = nil;
if([m3u8Str componentsSeparatedByString:@"\r\n"].count>1) {
components = [m3u8Str componentsSeparatedByString:@"\r\n"];
}
else {
components = [m3u8Str componentsSeparatedByString:@"\n"];
}
NSMutableArray *durations = [NSMutableArray array];
NSMutableArray *urlArray = [NSMutableArray array];
for (NSString *infoString in components) {
NSRange durationRange = [infoString rangeOfString:@"#EXTINF:"];
NSRange tsRange = [infoString rangeOfString:@".ts"];
if (durationRange.location != NSNotFound){
NSString *durationStr = [infoString substringFromIndex:durationRange.length];
[durations addObject:durationStr];
}
else if (tsRange.location != NSNotFound){
[urlArray addObject:infoString];
}
}
for (int i = 0; i<durations.count; i++){
NSString *durationStr = durations[i];
NSString *tsURL = urlArray[i];
if(![tsURL hasPrefix:@"http://"])
tsURL = [baseUrlStr stringByAppendingString:tsURL];
SegmentInfo *tsInfo = [SegmentInfo infoWith:[durationStr doubleValue] tsURL:tsURL];
[self.segments addObject:tsInfo];
}
}
6)伪造视频描述文件,将伪造的文件给播放器播放
- (void)praseM3U8InfoFinish:(M3U8Handler *)handler{
if(handler.segments.count<=0) return;
NSMutableArray * localUrl = [[NSMutableArray alloc] init];
for (int i=0; i<handler.segments.count; i++){
SegmentInfo * mediaUrl = handler.segments[i];
NSString * indexStr = [NSString stringWithFormat:@"%d",(int)i];
NSString * extension = @"ts";
[self.m3u8PlayUrlDic setObject:mediaUrl.tsURL forKey:[[self getLocalUrlWithAddExtension:NO] stringByAppendingString: [self getlocalWithIndex:indexStr extension:extension]]];
[localUrl addObject:[self getlocalWithIndex:indexStr extension:extension]];
}
NSMutableString * urlStr = [NSMutableString string];
NSArray *components = [handler.dataStr componentsSeparatedByString:@"\n"];
int i = 0;
for (NSString * item in components){
if(item.length<=0) continue;
if([item hasPrefix:@"#"]) {
[urlStr appendString:[NSString stringWithFormat:@"%@\n",item]];
}
else {
NSString * url = [localUrl objectAtIndex:i];
[urlStr appendString:[NSString stringWithFormat:@"%@\n",url]];
i++;
}
}
[self.localM3u8Dic setObject:urlStr forKey:[self getLocalUrlWithAddExtension:YES]];
[localUrl removeAllObjects];
//解析完成 通知播放器播放
if(self.openServer){
if(!self.isChangePlayeUrl){
self.isChangePlayeUrl = YES;
self.openServer(YES,0);
}
}
二)在本地播放器捕获到所有的播放请求,利用重定向获取到真实的播放地址
self.webServer = [[GCDWebServer alloc] init];
__weak typeof(self) weakSelf = self;
__strong typeof(self) strongSelf = weakSelf;
[self.webServer addDefaultHandlerForMethod:@"GET" requestClass:[GCDWebServerRequest class] processBlock:^GCDWebServerResponse *(__kindof GCDWebServerRequest *request) {
strongSelf.isChangePlayeUrl = YES;
NSString * m3u8LocalUrl = request.URL.absoluteString;
if(m3u8LocalUrl.length>3){
NSString * flag = [m3u8LocalUrl substringFromIndex:m3u8LocalUrl.length-3];
if([flag hasPrefix:@"---"]){
m3u8LocalUrl = [m3u8LocalUrl substringToIndex:m3u8LocalUrl.length-3];
}
}
if([[strongSelf.localM3u8Dic allKeys] containsObject:m3u8LocalUrl]){
NSString * content = [strongSelf.localM3u8Dic objectForKey:m3u8LocalUrl];
return [GCDWebServerDataResponse responseWithText:content];
}
else if([[strongSelf.m3u8PlayUrlDic allKeys] containsObject:m3u8LocalUrl]){
NSString * urlStr = [strongSelf.m3u8PlayUrlDic objectForKey:m3u8LocalUrl];
/ /验证播发地址是否过期
if([strongSelf playUrleffective:urlStr])//过期重新去请求数据{
NSLog(@"播发片段的地址已经过期了,正在重新获取播放地址");
if(strongSelf.playFlag!=0){
strongSelf.playFlag++;
//过期需要重新去获取数据
[strongSelf getPlayUrl];
urlStr = [strongSelf.m3u8PlayUrlDic objectForKey:m3u8LocalUrl];
NSURL * url = [[NSURL alloc] initWithString:urlStr];;
return [GCDWebServerDataResponse responseWithRedirect:url permanent:NO];
}
else//播发不了需要跳转到网页{
if(strongSelf.playFlag++==0) {
strongSelf.openServer(NO,1004);
}
return [GCDWebServerDataResponse responseWithRedirect:nil permanent:NO];
}
}
else {
strongSelf.playFlag++;
NSURL * url = [[NSURL alloc] initWithString:urlStr];;
return [GCDWebServerDataResponse responseWithRedirect:url permanent:NO];
}
}
return [GCDWebServerDataResponse responseWithRedirect:request.URL permanent:YES];
}];
[self.webServer startWithPort:[self.port integerValue] bonjourName:nil];
}
申明:以上只是自己的一些爱好研究,请不要用于商业。