申明:這篇只是自己的一些研究逢倍,請(qǐng)不要用于商業(yè)用途捧颅。如果影響到了你的利益,請(qǐng)你聯(lián)系我较雕。
? ? ?獲取三方的視頻碉哑,因?yàn)槿降囊曨l地址可能幾分鐘就會(huì)失效亮蒋。那么視頻有可能播放不了幾分鐘就會(huì)播發(fā)不了扣典。所以就需要在本地開啟一個(gè)播放代理。(目前這個(gè)需要服務(wù)器配合,客戶端不能夠自己去三方的網(wǎng)站去解析視頻)慎玖,以下所有的操作都是建立在自己的服務(wù)器上進(jìn)行贮尖。接下來(lái)會(huì)研究不需要依賴服務(wù)器,直接在本地解析優(yōu)酷真正的地址.
用到三方庫(kù):GCDWebServer(攔截視頻url請(qǐng)求)趁怔,AFNetworking
原理:
? ? ? ? ? ?1):本地開啟HttpServer服務(wù)湿硝,去捕獲到所有的視頻播發(fā)請(qǐng)求薪前。
? ? ? ? ? ?2):獲取到返回來(lái)視頻的視頻文件描述文件,將該描述文件存起來(lái)关斜,并且解析出來(lái)所有視頻片段的ts的url示括,保存所有的url。
? ? ? ? ? ?3):將返回來(lái)的描述文件中真實(shí)的播放地址替換成本地的地址痢畜,將修改后的描述文件直接扔給播放器垛膝。目的:利用重定向,可以獲取到所有經(jīng)過(guò)本地服務(wù)器的請(qǐng)求的url地址丁稀。
? ? ? ? ? ?4):因?yàn)椴シ牌魅フ?qǐng)求每一個(gè)視頻片段吼拥,本地就能捕獲到這些地址。再用這些本地的地址去匹配我們保存的真實(shí)地址线衫。映射出真實(shí)地址凿可。
? ? ? ? ? ?5):判斷真實(shí)的地址是否是可用的。不可用授账,那么重復(fù)第一步(目的是更新本地真實(shí)的地址)矿酵,可用,那么直接就扔給播放器去播放視頻
上原理圖:
直接上重要的代碼:
一)解析出視頻的每一個(gè)片段的的地址
? ? ? ? 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)根據(jù)視頻地址解析出視頻的描述文件
????????????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":@"獲取數(shù)據(jù)內(nèi)容失敗"}];
????????????????????[self.delegate M3U8Handler:self praseError:formatError];
????????????}
? ? ? ? 4)判斷解析出來(lái)的文件格式是否是正確的
????????????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":@"數(shù)據(jù)格式錯(cuò)誤"}];
????????????????????[self.delegate M3U8Handler:self praseError:formatError];
? ? ? ? ? ? ? ? ? ?}
????????????????return;
????????????????}
? ? ? ? 5)解析出mei一個(gè)片段的真正的地址
????????????- (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);
????????????????}
????????}
二)在本地播放器捕獲到所有的播放請(qǐng)求,利用重定向獲取到真實(shí)的播放地址
? ??????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];
????????????????????????????/ /驗(yàn)證播發(fā)地址是否過(guò)期
????????????????????????????if([strongSelf playUrleffective:urlStr])//過(guò)期重新去請(qǐng)求數(shù)據(jù){
????????????????????????????????????NSLog(@"播發(fā)片段的地址已經(jīng)過(guò)期了,正在重新獲取播放地址");
????????????????????????????????????if(strongSelf.playFlag!=0){
????????????????????????????????????????????????strongSelf.playFlag++;
????????????????????????????????????????????????//過(guò)期需要重新去獲取數(shù)據(jù)
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?[strongSelf getPlayUrl];
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?urlStr = [strongSelf.m3u8PlayUrlDic objectForKey:m3u8LocalUrl];
????????????????????????????????????????????????NSURL * url = [[NSURL alloc] initWithString:urlStr];;
????????????????????????????????????????????????return [GCDWebServerDataResponse responseWithRedirect:url permanent:NO];
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?}
????????????????????????????????????????else//播發(fā)不了需要跳轉(zhuǎn)到網(wǎng)頁(yè){
????????????????????????????????????????????????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];
}
申明:以上只是自己的一些愛好研究敞咧,請(qǐng)不要用于商業(yè)棘捣。