ios - 在 iOS 上通过本地 HTTP 服务器进行 HLS 播放

标签 ios local httpserver loopback http-live-streaming

背景:我在服务器端有一个 MPMoviePlayer 不支持的自定义身份验证机制。因此,我决定拥有一个本地环回 HTTP 服务器,它将接收播放器的初始请求并提供 HLS list 文件。

我处于播放器向我的本地 HTTP 服务器发起请求的位置,之后我的本地 HTTP 服务器从服务器获取 list 文件并将其作为 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

提前致谢。

最佳答案

我正在向服务器写一条额外的回显消息

  • (void)soc​​ket:(GCDAsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag

一旦我删除它,一切都会按预期开始工作。只是提一下,以防有人想使用上面发布的代码。

关于ios - 在 iOS 上通过本地 HTTP 服务器进行 HLS 播放,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27111274/

相关文章:

ios - 获取从数组中随机选择的对象类型?

ios - 谷歌 VR + Unity + iOS

Java 和本地数据库

javascript - node.js 使 HTTP 服务器的请求和响应全局化?

java - 直接从 HTTP 服务器上的 JAR 本身运行 java applet

ios - Swift 如何在推送后保留导航栏按钮项目

ios - 获取请求后的冗余 TableView

r - 环境如何记住它的存在?

python - 何时以及为何使用 AF_UNIX,为什么使用同一台机器

python - 通过 HTTP 服务日志