ios开启本地播发代理,解析三方服务器上的视频,防止视频地址失效

申明:这篇只是自己的一些研究,请不要用于商业用途。如果影响到了你的利益,请你联系我。

     获取三方的视频,因为三方的视频地址可能几分钟就会失效。那么视频有可能播放不了几分钟就会播发不了。所以就需要在本地开启一个播放代理。(目前这个需要服务器配合,客户端不能够自己去三方的网站去解析视频),以下所有的操作都是建立在自己的服务器上进行。接下来会研究不需要依赖服务器,直接在本地解析优酷真正的地址.

用到三方库: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];

}

申明:以上只是自己的一些爱好研究,请不要用于商业。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,324评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,356评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,328评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,147评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,160评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,115评论 1 296
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,025评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,867评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,307评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,528评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,688评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,409评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,001评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,657评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,811评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,685评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,573评论 2 353

推荐阅读更多精彩内容