0
背景:我在MPMoviePlayer不支持的服務器端有自定義的身份驗證機制。所以,我決定有一個本地環回HTTP服務器,它將接受播放器的初始請求並提供HLS清單文件。通過iOS上的本地HTTP服務器回放HLS
我處於播放器向本地HTTP服務器發起請求的位置,然後我的本地HTTP服務器從服務器獲取清單文件並將其作爲http響應寫回播放器。但之後MPMoviePlayer不再播放視頻。
有人可以幫助我實現這一目標嗎?
#import "QumuMediaPlayerProxy.h"
#import "GZIP.h"
#define WELCOME_MSG 0
#define ECHO_MSG 1
#define WARNING_MSG 2
#define READ_TIMEOUT 15.0
#define READ_TIMEOUT_EXTENSION 10.0
@interface QumuMediaPlayerProxy()
@property NSURL *contentURL;
@end
@implementation QumuMediaPlayerProxy
+(NSURL*)getProxyURL{
return [NSURL URLWithString:[NSString stringWithFormat:@"http://192.168.2.11:%d%@", SERVER_PORT, @"/nkm.do"]];
}
- (id)initWithURL:(NSURL*)contentURL
{
if((self = [super init]))
{
socketQueue = dispatch_queue_create("socketQueue", NULL);
listenSocket = [[GCDAsyncSocket alloc] initWithDelegate:self delegateQueue:socketQueue];
connectedSockets = [[NSMutableArray alloc] initWithCapacity:1];
isRunning = NO;
self.contentURL = contentURL;
}
return self;
}
- (void)startOnPort:(int)port
{
if(!isRunning)
{
if (port < 0 || port > 65535)
{
port = 0;
}
NSError *error = nil;
if(![listenSocket acceptOnPort:port error:&error])
{
NSLog(@"Error starting QumuMediaPlayerProxy: %@", error.debugDescription);
return;
}
NSLog(@"QumuMediaPlayerProxy started on port %hu", [listenSocket localPort]);
isRunning = YES;
}
}
-(void)stop
{
if(isRunning)
{
// Stop accepting connections
[listenSocket disconnect];
// Stop any client connections
@synchronized(connectedSockets)
{
NSUInteger i;
for (i = 0; i < [connectedSockets count]; i++)
{
// Call disconnect on the socket,
// which will invoke the socketDidDisconnect: method,
// which will remove the socket from the list.
[[connectedSockets objectAtIndex:i] disconnect];
}
}
NSLog(@"Stopped QumuMediaPlayerProxy");
isRunning = false;
}
}
- (void)socket:(GCDAsyncSocket *)sock didAcceptNewSocket:(GCDAsyncSocket *)newSocket
{
// This method is executed on the socketQueue (not the main thread)
@synchronized(connectedSockets)
{
[connectedSockets addObject:newSocket];
NSLog(@"==Accepted client==");
}
[newSocket readDataWithTimeout:-1 tag:0];
}
- (void)socket:(GCDAsyncSocket *)sock didWriteDataWithTag:(long)tag
{
// This method is executed on the socketQueue (not the main thread)
if (tag == ECHO_MSG)
{
[sock readDataToData:[GCDAsyncSocket CRLFData] withTimeout:READ_TIMEOUT tag:0];
}
}
- (void)socket:(GCDAsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag
{
// This method is executed on the socketQueue (not the main thread)
dispatch_async(dispatch_get_main_queue(), ^{
@autoreleasepool {
NSData *strData = [data subdataWithRange:NSMakeRange(0, [data length] - 2)];
NSString *msg = [[NSString alloc] initWithData:strData encoding:NSUTF8StringEncoding];
if (msg)
{
NSLog(@"msg===>%@",msg);
NSLog(@"contentURL===>%@",self.contentURL.absoluteString);
NSString *getStr = [msg componentsSeparatedByString:@"\n"][0];
NSString *requestedURL = [getStr substringWithRange:NSMakeRange(4, getStr.length-9)];
//NSString *host = @"http://127.0.0.1:6910/";
NSString *host = @"http://192.168.2.11:6910/";
NSURL *requestURL = self.contentURL;
if(![requestedURL containsString:@"nkm.do"]){
NSString *actualHost = [self.contentURL.absoluteString stringByReplacingOccurrencesOfString:self.contentURL.lastPathComponent withString:@""];
requestURL = [NSURL URLWithString:[actualHost stringByAppendingString:requestedURL]];
}
NSData *manifestData = [[QumuJSONHelper getInstance] fetchM3U8Playlist:requestURL];
NSString *manifestStr = [[NSString alloc] initWithData:manifestData encoding:NSUTF8StringEncoding];
NSLog(@"manifestStr===>%@",manifestStr);
/* NSArray *manifestArray = [manifestStr componentsSeparatedByString:@"\n"];
NSString *modifiedManifest = @"";
for(int i=0;i<manifestArray.count;i++){
NSString *token = manifestArray[i];
if([token containsString:@"#EXT-X-STREAM-INF"]){
NSLog(@"== Found tag EXT-X-STREAM-INF ==");
modifiedManifest = [modifiedManifest stringByAppendingString:token];
modifiedManifest = [modifiedManifest stringByAppendingString:@"\n"];
token = manifestArray[++i];
// token = [@"https://devimages.apple.com.edgekey.net/streaming/examples/bipbop_16x9/" stringByAppendingString:token];
token = [host stringByAppendingString:token];
NSLog(@"Modified URL===>%@",token);
}
modifiedManifest = [modifiedManifest stringByAppendingString:token];
modifiedManifest = [modifiedManifest stringByAppendingString:@"\n"];
}
modifiedManifest = [modifiedManifest stringByReplacingOccurrencesOfString:@"URI=\"" withString:[NSString stringWithFormat:@"URI=\"%@",host]];
NSLog(@"modifiedManifest===>%@",modifiedManifest);*/
NSString *response = @"HTTP/1.1 200 OK";
response = [response stringByAppendingString:@"\r\nContent-Type: application/vnd.apple.mpegurl;charset=UTF-8"];
response = [response stringByAppendingFormat:@"\r\nContent-Length: %ld", (unsigned long)manifestData.length];
response = [response stringByAppendingString:@"\r\nConnection: keep-alive"];
response = [response stringByAppendingString:@"\r\n\r\n"];
NSLog(@"response header ===>%@",response);
NSData *responseData = [response dataUsingEncoding:NSUTF8StringEncoding];
[sock writeData:responseData withTimeout:-1 tag:0];
[sock writeData:manifestData withTimeout:-1 tag:0];
}
else
{
NSLog(@"Error converting received data into UTF-8 String");
}
}
});
// Echo message back to client
[sock writeData:data withTimeout:-1 tag:ECHO_MSG];
}
/**
* This method is called if a read has timed out.
* It allows us to optionally extend the timeout.
* We use this method to issue a warning to the user prior to disconnecting them.
**/
- (NSTimeInterval)socket:(GCDAsyncSocket *)sock shouldTimeoutReadWithTag:(long)tag
elapsed:(NSTimeInterval)elapsed
bytesDone:(NSUInteger)length
{
if (elapsed <= READ_TIMEOUT)
{
NSString *warningMsg = @"Are you still there?\r\n";
NSData *warningData = [warningMsg dataUsingEncoding:NSUTF8StringEncoding];
[sock writeData:warningData withTimeout:-1 tag:WARNING_MSG];
return READ_TIMEOUT_EXTENSION;
}
return 0.0;
}
- (void)socketDidDisconnect:(GCDAsyncSocket *)sock withError:(NSError *)err
{
if (sock != listenSocket)
{
dispatch_async(dispatch_get_main_queue(), ^{
@autoreleasepool {
NSLog(@"Client Disconnected");
}
});
@synchronized(connectedSockets)
{
[connectedSockets removeObject:sock];
}
}
}
-(void)dealloc{
// [self stop];
}
@end
在此先感謝。
我知道這不是很有幫助,但我不確定HLS協議是否允許從本地主機播放。幾個月前,我正在嘗試一種類似的技術,並且在Apple媒體開發論壇的某個地方,我發現這是不可能的。我想離線播放,希望能夠下載.ts文件來使用配置爲流式傳輸的播放器。因爲這是不可能的,我最終只是下載了一個mp4文件並更換了我的播放器來支持它。 – timgcarlson 2014-11-24 18:18:38
假設您可以訪問Apple開發論壇,請查看此帖... https://devforums.apple.com/message/1031399#1031399(baleighsdad是Apple HLS團隊的工程師)。 – timgcarlson 2014-11-24 18:19:17
感謝您的鏈接timgcarlson。我目前正在使用MPMoviePlayerController。我正在離線使用MPMoviePlayerController成功傳輸MP4文件。我想用HLS視頻做同樣的事情。在我的場景中,我不必在本地流式傳輸整個視頻。我只是希望能夠提供HLS清單,因爲這是受客戶端證書身份驗證機制保護的,並且MPMoviePlayerController無法處理身份驗證。我應該改用AVPlayer嗎? – Madala 2014-11-26 09:56:49