CocoaHTTPServer 和 視頻邊下邊播

CocoaHTTPServer的原理:搞過(guò)服務(wù)器的應(yīng)該了解还棱,這就是在手機(jī)本地架設(shè)一個(gè)本地服務(wù)器载慈,然后通過(guò)HTTP去訪問(wèn)本地服務(wù)器中得文件,或者視頻,不了解也沒(méi)關(guān)系珍手,把a(bǔ)ppDelgate 中的內(nèi)容copy 办铡,引入相應(yīng)的文件,本地服務(wù)器搭建完成琳要。

搭建手機(jī)本地服務(wù)器寡具。

ps:如果知道如何將服務(wù)器搭建在sandbox 的tmp 文件夾下,此部分可以略過(guò)稚补。</br>
1.首先通過(guò)通過(guò)連接去下載CocoaHTTPServer

2.查看下載的文件中得Samples文件童叠,里面有幾個(gè)Demo,有一個(gè)叫iPhoneHTTPServer的课幕,是iphone上的項(xiàng)目厦坛,在XCode 7 直接運(yùn)行Crash ,你可以把這個(gè)問(wèn)題fix了乍惊,搞不定無(wú)所謂杜秸,這里主要看里面的代碼。_iPhoneHTTPServerAppDelegate.m 這個(gè)文件主要是建立服務(wù)器的代碼润绎,注釋很詳細(xì)撬碟。</br>
3.建立一個(gè)Single工程(XCode 7),引入CoreVendor兩個(gè)所有文件文件凡橱,拷貝_iPhoneHTTPServerAppDelegate.m中相應(yīng)的代碼小作。

#import "AppDelegate.h"
#import "HTTPServer.h"
#import "DDLog.h"
#import "DDTTYLogger.h"
@interface AppDelegate ()
{
    HTTPServer *httpServer;
}
@end
//主要和DDlog 有關(guān),可以忽略
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>
主要是兩個(gè)方法稼钩,(最好點(diǎn)進(jìn)去看看顾稀,都有注釋)</br>

[httpServer setDocumentRoot:webPath]這是設(shè)置服務(wù)器的根路徑,我把服務(wù)器的根路徑設(shè)置為tmp 文件夾</br>

[httpServer setPort:12345];服務(wù)器開(kāi)放的端口,
</p>

ok,可以將你的項(xiàng)目run 起來(lái),然后通過(guò)電腦的瀏覽器去訪問(wèn)你剛才搭建的服務(wù)器阻肿。訪問(wèn)的url:http://127.0.0.1:12345/index.hml

下載視頻素跺,通過(guò)播放器訪問(wèn)該視頻文件

CocoaHTTPServer 有一個(gè)地方需要修改下:
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 中獲取文件長(zhǎng)度,在connection的代理里面有獲取視頻長(zhǎng)度的代碼崭参。唇跨。粱挡。殊橙。辐宾。,如果你不改或播放不成功膨蛮。:)or 視頻播放幾秒以后沒(méi)有聲音(當(dāng)年我就遇到這個(gè)問(wèn)題叠纹,搞了將近一個(gè)禮拜,然后2行代碼解決敞葛,說(shuō)多了都是淚)
別忘了改App Transport Security Settings
</br>

算了上代碼吧:

#import "ViewController.h"
#import "VideoView.h"http://上一篇有代碼

@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(@"開(kāi)始下載");
    [[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

我遇到的問(wèn)題誉察,都是淚啊

1.視頻播放到1~20s沒(méi)有聲音,那是你沒(méi)有獲取視頻文件的長(zhǎng)度惹谐,我在上面有介紹</br>
2.有同事的同事說(shuō)第一次播放不成功過(guò)持偏,第二次播放成功了,那應(yīng)該是你開(kāi)啟播器的速度太快氨肌,鸿秆,也就是說(shuō),你下載的文件還沒(méi)有達(dá)到可播放的程度儒飒,就開(kāi)啟播放器播放谬莹,肯定會(huì)失旈莸臁(說(shuō)的可能詞不達(dá)意桩了,自己理解吧,上面我不是寫了fileSize > 10000的時(shí)候才能播放啊埠戳,你試試10000 改小一點(diǎn)井誉,小到一定程度,肯定播放不成功)整胃。</br>
3.播放一開(kāi)始有聲音颗圣,然后播放了一段時(shí)間沒(méi)有聲音了。屁使。在岂。這個(gè)問(wèn)題我也遇到過(guò),到底是什么造成我也不是太清楚蛮寂,我猜測(cè)是網(wǎng)絡(luò)太卡的原因蔽午,,酬蹋,及老,抽莱,,建議你手動(dòng)控制AVPlayer 的duration骄恶,當(dāng)視頻卡頓的時(shí)候根據(jù)duration 的大小控制是否播放食铐,我當(dāng)時(shí)就是這樣做的,沒(méi)聲音的概率小了點(diǎn)僧鲁,使用KVO監(jiān)聽(tīng)虐呻,VideoView.m中有這個(gè)監(jiān)聽(tīng),當(dāng)收到bufferEmpty 的通知的時(shí)候暫停播放寞秃,等到duration 大于5s铃慷,,蜕该, 的時(shí)候在開(kāi)啟播放犁柜,這個(gè)時(shí)間長(zhǎng)度的大小可以自己控制,不過(guò)系統(tǒng)的也就8~10s左右堂淡,我做過(guò)實(shí)驗(yàn)馋缅,大于這個(gè)值的時(shí)候就收不到duration 改變的KVO通知了。

視頻播放由 Socket 轉(zhuǎn) HTTP

這個(gè)部分我不知道怎么更好的描述绢淀,
這個(gè)問(wèn)題我只能提供一些思路萤悴,這個(gè)需要你去自定義個(gè)HTTPResponse實(shí)現(xiàn)HTTPResponse 協(xié)議 ,CocoaHTTPServer有好幾個(gè)Response 你可以參考下皆的。</br>
思路:播放器發(fā)送請(qǐng)求的時(shí)候會(huì)帶有請(qǐng)求的offset 和 length覆履,當(dāng)你獲取到offset 的時(shí)候開(kāi)啟socket下載,數(shù)據(jù)分包返回的地方需要將數(shù)據(jù)有序的拼接起來(lái),然后在<code>- (NSData *)readDataOfLength:(NSUInteger)length;</code>中將數(shù)據(jù)返回费薄。通過(guò)socket 下載的數(shù)據(jù)你可以使用一個(gè)第三方庫(kù)硝全,功能類似Java,不懂的話楞抡,搜索一下伟众。 BlockQueue下載地址

有一個(gè)option方法,你最好實(shí)現(xiàn)召廷,也許能幫你省很多事:

/**
 * 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;

在這個(gè)方法里面凳厢,將socket下載任務(wù)取消掉,調(diào)用這個(gè)方法的時(shí)候說(shuō)明這個(gè)鏈接connection 已經(jīng)die 竞慢,需要發(fā)送鏈接先紫,新的offset 和新的length。還有其他required的方法筹煮,注釋寫的也很清楚</br>
最后一個(gè)部分詞不達(dá)意遮精,,寺谤,仑鸥,吮播,看不懂就算了,歡迎指正眼俊,多謝意狠。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市疮胖,隨后出現(xiàn)的幾起案子环戈,更是在濱河造成了極大的恐慌,老刑警劉巖澎灸,帶你破解...
    沈念sama閱讀 206,482評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件院塞,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡性昭,警方通過(guò)查閱死者的電腦和手機(jī)拦止,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,377評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)糜颠,“玉大人汹族,你說(shuō)我怎么就攤上這事∑湫耍” “怎么了顶瞒?”我有些...
    開(kāi)封第一講書人閱讀 152,762評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)元旬。 經(jīng)常有香客問(wèn)我榴徐,道長(zhǎng),這世上最難降的妖魔是什么匀归? 我笑而不...
    開(kāi)封第一講書人閱讀 55,273評(píng)論 1 279
  • 正文 為了忘掉前任坑资,我火速辦了婚禮,結(jié)果婚禮上朋譬,老公的妹妹穿的比我還像新娘盐茎。我一直安慰自己兴垦,他們只是感情好徙赢,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,289評(píng)論 5 373
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著探越,像睡著了一般狡赐。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上钦幔,一...
    開(kāi)封第一講書人閱讀 49,046評(píng)論 1 285
  • 那天枕屉,我揣著相機(jī)與錄音,去河邊找鬼鲤氢。 笑死搀擂,一個(gè)胖子當(dāng)著我的面吹牛西潘,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播哨颂,決...
    沈念sama閱讀 38,351評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼喷市,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了威恼?” 一聲冷哼從身側(cè)響起品姓,我...
    開(kāi)封第一講書人閱讀 36,988評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎箫措,沒(méi)想到半個(gè)月后腹备,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,476評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡斤蔓,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,948評(píng)論 2 324
  • 正文 我和宋清朗相戀三年植酥,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片弦牡。...
    茶點(diǎn)故事閱讀 38,064評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡惧互,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出喇伯,到底是詐尸還是另有隱情喊儡,我是刑警寧澤,帶...
    沈念sama閱讀 33,712評(píng)論 4 323
  • 正文 年R本政府宣布稻据,位于F島的核電站艾猜,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏捻悯。R本人自食惡果不足惜匆赃,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,261評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望今缚。 院中可真熱鬧算柳,春花似錦、人聲如沸姓言。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 30,264評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)何荚。三九已至囱淋,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間餐塘,已是汗流浹背妥衣。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 31,486評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人税手。 一個(gè)月前我還...
    沈念sama閱讀 45,511評(píng)論 2 354
  • 正文 我出身青樓蜂筹,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親芦倒。 傳聞我的和親對(duì)象是個(gè)殘疾皇子狂票,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,802評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容