CocoaHTTPServer的原理:搞過服務(wù)器的應(yīng)該了解,這就是在手機(jī)本地架設(shè)一個(gè)本地服務(wù)器,然后通過HTTP去訪問本地服務(wù)器中得文件,或者視頻,不了解也沒關(guān)系,把a(bǔ)ppDelgate 中的內(nèi)容copy ,引入相應(yīng)的文件,本地服務(wù)器搭建完成。
搭建手機(jī)本地服務(wù)器。
ps:如果知道如何將服務(wù)器搭建在sandbox 的tmp 文件夾下,此部分可以略過。</br>
1.首先通過通過連接去下載CocoaHTTPServer
2.查看下載的文件中得Samples文件,里面有幾個(gè)Demo,有一個(gè)叫iPhoneHTTPServer的,是iphone上的項(xiàng)目,在XCode 7 直接運(yùn)行Crash ,你可以把這個(gè)問題fix了,搞不定無所謂,這里主要看里面的代碼。_iPhoneHTTPServerAppDelegate.m 這個(gè)文件主要是建立服務(wù)器的代碼,注釋很詳細(xì)。</br>
3.建立一個(gè)Single工程(XCode 7),引入Core和Vendor兩個(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ù)器開放的端口,
</p>
ok,可以將你的項(xiàng)目run 起來,然后通過電腦的瀏覽器去訪問你剛才搭建的服務(wù)器。訪問的url:http://127.0.0.1:12345/index.hml
下載視頻,通過播放器訪問該視頻文件
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 中獲取文件長度,在connection的代理里面有獲取視頻長度的代碼。。。。。,如果你不改或播放不成功。:)or 視頻播放幾秒以后沒有聲音(當(dāng)年我就遇到這個(gè)問題,搞了將近一個(gè)禮拜,然后2行代碼解決,說多了都是淚)
別忘了改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(@"開始下載");
[[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.有同事的同事說第一次播放不成功過,第二次播放成功了,那應(yīng)該是你開啟播器的速度太快,,也就是說,你下載的文件還沒有達(dá)到可播放的程度,就開啟播放器播放,肯定會(huì)失?。ㄕf的可能詞不達(dá)意,自己理解吧,上面我不是寫了fileSize > 10000的時(shí)候才能播放啊,你試試10000 改小一點(diǎn),小到一定程度,肯定播放不成功)。</br>
3.播放一開始有聲音,然后播放了一段時(shí)間沒有聲音了。。。這個(gè)問題我也遇到過,到底是什么造成我也不是太清楚,我猜測是網(wǎng)絡(luò)太卡的原因,,,,,,建議你手動(dòng)控制AVPlayer 的duration,當(dāng)視頻卡頓的時(shí)候根據(jù)duration 的大小控制是否播放,我當(dāng)時(shí)就是這樣做的,沒聲音的概率小了點(diǎn),使用KVO監(jiān)聽,VideoView.m中有這個(gè)監(jiān)聽,當(dāng)收到bufferEmpty 的通知的時(shí)候暫停播放,等到duration 大于5s,,, 的時(shí)候在開啟播放,這個(gè)時(shí)間長度的大小可以自己控制,不過系統(tǒng)的也就8~10s左右,我做過實(shí)驗(yàn),大于這個(gè)值的時(shí)候就收不到duration 改變的KVO通知了。
視頻播放由 Socket 轉(zhuǎn) HTTP
這個(gè)部分我不知道怎么更好的描述,
這個(gè)問題我只能提供一些思路,這個(gè)需要你去自定義個(gè)HTTPResponse實(shí)現(xiàn)HTTPResponse 協(xié)議 ,CocoaHTTPServer有好幾個(gè)Response 你可以參考下。</br>
思路:播放器發(fā)送請求的時(shí)候會(huì)帶有請求的offset 和 length,當(dāng)你獲取到offset 的時(shí)候開啟socket下載,數(shù)據(jù)分包返回的地方需要將數(shù)據(jù)有序的拼接起來,然后在<code>- (NSData *)readDataOfLength:(NSUInteger)length;</code>中將數(shù)據(jù)返回。通過socket 下載的數(shù)據(jù)你可以使用一個(gè)第三方庫,功能類似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í)候說明這個(gè)鏈接connection 已經(jīng)die ,需要發(fā)送鏈接,新的offset 和新的length。還有其他required的方法,注釋寫的也很清楚</br>
最后一個(gè)部分詞不達(dá)意,,,,,看不懂就算了,歡迎指正,多謝。