CocoaHTTPServer 和 视频边下边播

CocoaHTTPServer的原理:搞过服务器的应该了解,这就是在手机本地架设一个本地服务器,然后通过HTTP去访问本地服务器中得文件,或者视频,不了解也没关系,把appDelgate 中的内容copy ,引入相应的文件,本地服务器搭建完成。

搭建手机本地服务器。

ps:如果知道如何将服务器搭建在sandbox 的tmp 文件夹下,此部分可以略过。</br>
1.首先通过通过连接去下载CocoaHTTPServer

2.查看下载的文件中得Samples文件,里面有几个Demo,有一个叫iPhoneHTTPServer的,是iphone上的项目,在XCode 7 直接运行Crash ,你可以把这个问题fix了,搞不定无所谓,这里主要看里面的代码。_iPhoneHTTPServerAppDelegate.m 这个文件主要是建立服务器的代码,注释很详细。</br>
3.建立一个Single工程(XCode 7),引入CoreVendor两个所有文件文件,拷贝_iPhoneHTTPServerAppDelegate.m中相应的代码。

#import "AppDelegate.h"
#import "HTTPServer.h"
#import "DDLog.h"
#import "DDTTYLogger.h"
@interface AppDelegate ()
{
    HTTPServer *httpServer;
}
@end
//主要和DDlog 有关,可以忽略
static const int ddLogLevel = LOG_LEVEL_VERBOSE;
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    
    [DDLog addLogger:[DDTTYLogger sharedInstance]];
    httpServer = [[HTTPServer alloc] init];
    [httpServer setType:@"_http._tcp."];
    [httpServer setPort:12345];

    NSString *webPath = NSTemporaryDirectory();
    NSLog(@"Setting document root: %@", webPath);
    [httpServer setDocumentRoot:webPath];
    [self startServer];
    [self clearFileAtTmp];
    [self createIndexHelloWordFileAtTmp];
    return YES;
}
- (void)startServer
{
    // Start the server (and check for problems)
    
    NSError *error;
    if([httpServer start:&error])
    {
        DDLogInfo(@"Started HTTP Server on port %hu", [httpServer listeningPort]);
    }
    else
    {
        DDLogError(@"Error starting HTTP Server: %@", error);
    }
}
- (void)clearFileAtTmp{
    
    NSArray *fileAry  = [[NSFileManager defaultManager] subpathsOfDirectoryAtPath:NSTemporaryDirectory() error:nil];
    for (NSString *fileName in fileAry) {
        NSString *filePath = [NSTemporaryDirectory() stringByAppendingPathComponent:fileName];
        [[NSFileManager defaultManager] removeItemAtPath:filePath error:nil];
    }
    NSLog(@"clear over");
}
- (void)createIndexHelloWordFileAtTmp {
    NSString *filePath = [NSTemporaryDirectory() stringByAppendingPathComponent:@"index.html"];
    NSString *hello = @"helloWord";
    FILE *fp = fopen([filePath UTF8String], "a+");
    if (fp ) {
        fwrite([[hello dataUsingEncoding:NSUTF8StringEncoding] bytes], [hello dataUsingEncoding:NSUTF8StringEncoding].length, 1, fp);
        fflush(fp);
    }
    fclose(fp);
    
    
    NSLog(@"file:%d",[[NSFileManager defaultManager]fileExistsAtPath:filePath]);
}
@end

<p>
主要是两个方法,(最好点进去看看,都有注释)</br>

[httpServer setDocumentRoot:webPath]这是设置服务器的根路径,我把服务器的根路径设置为tmp 文件夹</br>

[httpServer setPort:12345];服务器开放的端口,
</p>

ok,可以将你的项目run 起来,然后通过电脑的浏览器去访问你刚才搭建的服务器。访问的url:http://127.0.0.1:12345/index.hml

下载视频,通过播放器访问该视频文件

CocoaHTTPServer 有一个地方需要修改下:
1.HTTPConnection.m 文件

/**
 * This method is called to get a response for a request.
 * You may return any object that adopts the HTTPResponse protocol.
 * The HTTPServer comes with two such classes: HTTPFileResponse and HTTPDataResponse.
 * HTTPFileResponse is a wrapper for an NSFileHandle object, and is the preferred way to send a file response.
 * HTTPDataResponse is a wrapper for an NSData object, and may be used to send a custom response.
**/
- (NSObject<HTTPResponse> *)httpResponseForMethod:(NSString *)method URI:(NSString *)path
{
    HTTPLogTrace();
    
    // Override me to provide custom responses.
    
    NSString *filePath = [self filePathForURI:path allowDirectory:NO];
    
    BOOL isDir = NO;
    
    if (filePath && [[NSFileManager defaultManager] fileExistsAtPath:filePath isDirectory:&isDir] && !isDir)
    {
        //return [[HTTPFileResponse alloc] initWithFilePath:filePath forConnection:self];
        /*下面的注释是这是为什么改为HTTPAsyncFileResponse 原因,能看懂吧*/
        // Use me instead for asynchronous file IO.
        // Generally better for larger files.
        
        return [[HTTPAsyncFileResponse alloc] initWithFilePath:filePath forConnection:self];
    }
    
    return nil;
}

2.将HTTPAsyncFileResponse.m 的 62行

fileLength = (UInt64)[[fileAttributes objectForKey:NSFileSize] unsignedLongLongValue];

改为

NSNumber *number = [[NSUserDefaults standardUserDefaults] objectForKey:@"fileSize"];
fileLength = [number longLongValue];

这是从NSUserDefaults 中获取文件长度,在connection的代理里面有获取视频长度的代码。。。。。,如果你不改或播放不成功。:)or 视频播放几秒以后没有声音(当年我就遇到这个问题,搞了将近一个礼拜,然后2行代码解决,说多了都是泪)
别忘了改App Transport Security Settings
</br>

算了上代码吧:

#import "ViewController.h"
#import "VideoView.h"//上一篇有代码

@interface ViewController ()
@property (nonatomic ,strong) NSString *url;
@property (nonatomic ,strong) VideoView *videoView;
@property (nonatomic ,strong) NSURLConnection  *connection;
@property (nonatomic ,strong) NSString *filePath;

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.

    [self initFilePath];
    [self.connection start];
}
- (void)initVideoView {
    NSString *string = @"http://127.0.0.1:12345/video.mp4";
    _videoView = [[VideoView alloc] initWithUrl:string delegate:nil];
    _videoView.frame = CGRectMake(30, 200, 260, 180);
    [self.view addSubview:_videoView];
}
- (void)initFilePath {
    _filePath = [NSTemporaryDirectory() stringByAppendingPathComponent:@"video.mp4"];
}
- (NSString *)url {
    if (_url == nil) {
        _url = @"http://static.tripbe.com/videofiles/20121214/9533522808.f4v.mp4";
    }
    return _url;
}
- (NSURLConnection *)connection {
    if (_connection == nil) {
        _connection = [NSURLConnection connectionWithRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:self.url]] delegate:self];
    }
    return  _connection;
}

#pragma  mark - DataDelegate
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
    NSLog(@"开始下载");
    [[NSUserDefaults standardUserDefaults ] setValue:[NSNumber numberWithLongLong:response.expectedContentLength] forKey:@"fileSize"];
}

- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
    long long fileSize = [[[NSFileManager defaultManager] attributesOfItemAtPath:_filePath error:nil] fileSize];
    if (fileSize < 10000) {
        NSLog(@"waite file buffer");
    } else if (fileSize > 10000 && _videoView == nil){
        [self initVideoView];
        NSLog(@"start play");
    }
    [self writeFile:data];
}
- (void)writeFile:(NSData *)data {
    FILE *fp = fopen([_filePath UTF8String], "a+");
    if (fp ) {
        fwrite([data bytes], data.length, 1, fp);
        fflush(fp);
    }
    fclose(fp);
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
        NSLog(@"%s",__FUNCTION__);
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}
@end

我遇到的问题,都是泪啊

1.视频播放到1~20s没有声音,那是你没有获取视频文件的长度,我在上面有介绍</br>
2.有同事的同事说第一次播放不成功过,第二次播放成功了,那应该是你开启播器的速度太快,,也就是说,你下载的文件还没有达到可播放的程度,就开启播放器播放,肯定会失败(说的可能词不达意,自己理解吧,上面我不是写了fileSize > 10000的时候才能播放啊,你试试10000 改小一点,小到一定程度,肯定播放不成功)。</br>
3.播放一开始有声音,然后播放了一段时间没有声音了。。。这个问题我也遇到过,到底是什么造成我也不是太清楚,我猜测是网络太卡的原因,,,,,,建议你手动控制AVPlayer 的duration,当视频卡顿的时候根据duration 的大小控制是否播放,我当时就是这样做的,没声音的概率小了点,使用KVO监听,VideoView.m中有这个监听,当收到bufferEmpty 的通知的时候暂停播放,等到duration 大于5s,,, 的时候在开启播放,这个时间长度的大小可以自己控制,不过系统的也就8~10s左右,我做过实验,大于这个值的时候就收不到duration 改变的KVO通知了。

视频播放由 Socket 转 HTTP

这个部分我不知道怎么更好的描述,
这个问题我只能提供一些思路,这个需要你去自定义个HTTPResponse实现HTTPResponse 协议 ,CocoaHTTPServer有好几个Response 你可以参考下。</br>
思路:播放器发送请求的时候会带有请求的offset 和 length,当你获取到offset 的时候开启socket下载,数据分包返回的地方需要将数据有序的拼接起来,然后在<code>- (NSData *)readDataOfLength:(NSUInteger)length;</code>中将数据返回。通过socket 下载的数据你可以使用一个第三方库,功能类似Java,不懂的话,搜索一下。 BlockQueue下载地址

有一个option方法,你最好实现,也许能帮你省很多事:

/**
 * This method is called from the HTTPConnection class when the connection is closed,
 * or when the connection is finished with the response.
 * If your response is asynchronous, you should implement this method so you know not to
 * invoke any methods on the HTTPConnection after this method is called (as the connection may be deallocated).
**/
- (void)connectionDidClose;

在这个方法里面,将socket下载任务取消掉,调用这个方法的时候说明这个链接connection 已经die ,需要发送链接,新的offset 和新的length。还有其他required的方法,注释写的也很清楚</br>
最后一个部分词不达意,,,,,看不懂就算了,欢迎指正,多谢。

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

推荐阅读更多精彩内容