参见网上示例大概清楚了在线缓存播放原理
1,本地实现httpserver,MPMoviePlayerController通过访问本地资源来实现视频播放如(http://127.0.0.1/test.mp4)
2,自己实现下目标视频下载,并在返回文件头时,将content-length字段取出,并存入沙盒
2,文件下载中不断写入已下载资源
原来MPMoviePlayerController访问网络视频时会自动不断请求这个资源地址,我们通过不断往这个地址写入数据来实现本地缓存
具体代码如下
#import <Foundation/Foundation.h>
typedef enum enWordResType
{
RES_TYPE_JPG,
RES_TYPE_MP3,
RES_TYPE_FLV,
RES_TYPE_UNKNOW,
}enWordResType;
//
// http下载任务委托
//
@class HttpDownloadUnit;
@protocol HttpDownloadUnitDelegate <NSObject>
-(void) HttpDownloadUnitProgress:(HttpDownloadUnit *)object percent:(int)nPercent;
-(void) HttpDownloadUnitCompleted:(HttpDownloadUnit *)object data:(NSData *)data;
-(void) HttpDownloadUnitError:(HttpDownloadUnit *)object error:(NSError *)error;
@end
//
//
//
@interface HttpDownloadUnit : NSObject<NSURLConnectionDelegate,NSURLConnectionDataDelegate>
{
NSURLConnection *_conn;
NSMutableData *_buffer;
int64_t _bytesTotal;
}
//属性定义
@property(nonatomic,retain) NSString *url;
@property(nonatomic,retain) id<HttpDownloadUnitDelegate> delegate;
//方法定义
//根据一个完整url返回一个不包含xxx.xxx.com的相对路径
+(NSString *)relativePath:(NSString *)strURL;
//根据文件名创建对应的本地路径
+(void)CreateFolderByPath:(NSString *)strFileName;
//取消
-(void)cancel;
-(void)startCache:(NSString *)strURL;
-(id)initWithURL:(NSString *)url;
@end
//
// WordResCache.m
// LearningEnglish
//
// Created by w on 14-6-10.
// Copyright (c) 2014年 xxx.com. All rights reserved.
//
#import "HttpDownloadUnit.h"
#import "LocalDataMgr.h"
@interface HttpDownloadUnit()
@end
@implementation HttpDownloadUnit
//
+(NSString *)relativePath:(NSString *)strURL;
{
//http://www.xxx.com/xx/xxx.xxx
NSRange sr;
sr.location = 7; // "http://"
sr.length = strURL.length - 7;
NSRange range1 = [strURL rangeOfString:@"/" options:NSCaseInsensitiveSearch range:sr];
if (range1.length != 0)
{
return [strURL substringFromIndex:range1.location + 1];
}
return nil;
}
//
-(void)cancel
{
[_conn cancel];
_conn = nil;
}
//
-(void)startCache:(NSString *)strURL
{
self.url = strURL;
if (_conn)
{
[_conn cancel];
_conn = nil;
}
[_buffer setLength:0];
NSURL *url = [[NSURL alloc] initWithString:strURL];
NSMutableURLRequest *urlReq = [[NSMutableURLRequest alloc] initWithURL:url];
urlReq.timeoutInterval = 10.0;
urlReq.HTTPMethod = @"GET";
_conn = [NSURLConnection connectionWithRequest:urlReq delegate:self];
}
//
-(id)initWithURL:(NSString *)url
{
self = [super init];
if (self)
{
self.url = url;
_buffer = [[NSMutableData alloc] init];
[self startCache:url];
}
return self;
}
#pragma mark NSURLConnection Delegate
-(void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
if (self.delegate && [self.delegate respondsToSelector:@selector(HttpDownloadUnitError:error:)])
[self.delegate HttpDownloadUnitError:self error:error];
NSLog(@"connection fail.");
}
//
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response{
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;
if(httpResponse && [httpResponse respondsToSelector:@selector(allHeaderFields)]){
NSDictionary *httpResponseHeaderFields = [httpResponse allHeaderFields];
_bytesTotal = [[httpResponseHeaderFields objectForKey:@"Content-Length"] longLongValue];
//NSLog(@"%lld", total_);
}
}
//
-(void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
[_buffer appendData:data];
int nPercent = (((float)_buffer.length / (float)_bytesTotal) * 100);
if (self.delegate && [self.delegate respondsToSelector:@selector(HttpDownloadUnitProgress:percent:)])
[self.delegate HttpDownloadUnitProgress:self percent:nPercent];
}
//根据文件名创建对应的本地路径
+(void)CreateFolderByPath:(NSString *)strFileName
{
NSRange range = [strFileName rangeOfString:@"/" options:NSBackwardsSearch];
if (range.length > 0)
{
NSFileManager *fileMgr = [NSFileManager defaultManager];
NSString *strFolder = [strFileName substringToIndex:range.location];
[fileMgr createDirectoryAtPath:strFolder withIntermediateDirectories:YES attributes:nil error:nil];
}
}
//
-(void)connectionDidFinishLoading:(NSURLConnection *)connection
{
NSString *strRelativePath = [HttpDownloadUnit relativePath:self.url];
NSString *strLocalFile = [LocalDataMgr getLocalResFileName:strRelativePath];
//根据文件名创建对应的目录结构
[HttpDownloadUnit CreateFolderByPath:strLocalFile];
NSLog(@"保存本地文件:%@",strLocalFile);
[_buffer writeToFile:strLocalFile atomically:YES];
if (self.delegate && [self.delegate respondsToSelector:@selector(HttpDownloadUnitCompleted:data:)])
[self.delegate HttpDownloadUnitCompleted:self data:_buffer];
}
@end
//
// httpVideoController.h
// LearningEnglish
//
// Created by w on 14-7-21.
// Copyright (c) 2014年 xxx.com. All rights reserved.
//
#import <UIKit/UIKit.h>
#import "VideoCacheUnit.h"
@interface httpVideoController : UIViewController<VideoCacheUnitDelegate>
{
VideoCacheUnit *_videoCacheUnit;
}
@end
//
// httpVideoController.m
// LearningEnglish
//
// Created by w on 14-7-21.
// Copyright (c) 2014年 xxx.com. All rights reserved.
//
#import "httpVideoController.h"
#import "VideoCacheUnit.h"
#import <MediaPlayer/MediaPlayer.h>
@interface httpVideoController ()
{
}
@end
@implementation httpVideoController
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
-(void)willStartVideCache
{
NSURL *url = [[NSURL alloc] initWithString:[NSString stringWithFormat:@"http://127.0.0.1:%d/test.mp4",HTTP_SERVER_PORT]];
player = [[MPMoviePlayerController alloc] initWithContentURL:url];
// 设置播放器的大小(16:9)
[player.view setFrame:CGRectMake(0, 30, 320, 180)];
//设置样式,让播放器影藏工具条
[player setControlStyle:MPMovieControlStyleEmbedded];
// 将播放器视图添加到根视图
[self.view addSubview:player.view];
[player play];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onReadyPlay:) name:MPMoviePlayerReadyForDisplayDidChangeNotification object:nil];
}
MPMoviePlayerController *player ;
- (void)viewDidLoad
{
[super viewDidLoad];
self.view.backgroundColor = [UIColor whiteColor];
//NSArray *directoryPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
//NSString *documentDirectory = [directoryPaths objectAtIndex:0];
// DBNAME 是要查找的文件名字,文件全名
//NSString *filePath = [documentDirectory stringByAppendingPathComponent:@"test.mp4"];
//
// filePath = [[NSBundle mainBundle] pathForResource:@"q1" ofType:@"mp4"];
// NSLog(@"PATH=%@",filePath);
// 用这个方法来判断当前的文件是否存在,如果不存在,就创建一个文件
//NSURL *url = [[NSBundle mainBundle]URLForResource:@"q1" withExtension:@"mp4"];
//NSURL *url = [[NSURL alloc] initFileURLWithPath:filePath];
//NSURL *url = [NSURL URLWithString:@"http://static.tripbe.com/videofiles/20121214/9533522808.f4v.mp4"];
_videoCacheUnit = [[VideoCacheUnit alloc] initWithURL:@"http://static.tripbe.com/videofiles/20121214/9533522808.f4v.mp4"];
_videoCacheUnit.delegate = self;
// Do any additional setup after loading the view.
}
-(void)onReadyPlay:(NSNotification *)notify
{
[player play];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
/*
#pragma mark - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
// Get the new view controller using [segue destinationViewController].
// Pass the selected object to the new view controller.
}
*/
@end
初始化httpserver
-(void)startServer
{
NSError *error;
if([httpServer start:&error])
{
NSLog(@"Started HTTP Server on port %hu", [httpServer listeningPort]);
}
else
{
NSLog(@"Error starting HTTP Server: %@", error);
}
}
-(void)initHttpServer
{
httpServer = [[HTTPServer alloc] init];
[httpServer setType:@"_http._tcp."];
[httpServer setPort:HTTP_SERVER_PORT];
// Serve files from our embedded Web folder
NSString *webPath = [NSHomeDirectory() stringByAppendingPathComponent:@"WebRoot"];
webPath = NSTemporaryDirectory();
NSLog(@"webRoot=%@",webPath);
NSFileManager *fileManager=[NSFileManager defaultManager];
if(![fileManager fileExistsAtPath:webPath])
{
[fileManager createDirectoryAtPath:webPath withIntermediateDirectories:YES attributes:nil error:nil];
}
[httpServer setDocumentRoot:webPath];
[self startServer];
}
修改httpserver中HTTPFileResponse.m
@implementation HTTPFileResponse
- (id)initWithFilePath:(NSString *)fpath forConnection:(HTTPConnection *)parent
{
// NSLog(@"filePath = %@",fpath);
if((self = [super init]))
{
HTTPLogTrace();
connection = parent; // Parents retain children, children do NOT retain parents
fileFD = NULL_FD;
filePath = [[fpath copy] stringByResolvingSymlinksInPath];
if (filePath == nil)
{
//HTTPLogWarn(@"%@: Init failed - Nil filePath", THIS_FILE);
return nil;
}
//NSLog(@"filePath = %@",filePath);
NSDictionary *fileAttributes = [[NSFileManager defaultManager] attributesOfItemAtPath:filePath error:nil];
if (fileAttributes == nil)
{
//HTTPLogWarn(@"%@: Init failed - Unable to get file attributes. filePath: %@", THIS_FILE, filePath);
return nil;
}
//fileLength = (UInt64)[[fileAttributes objectForKey:NSFileSize] unsignedLongLongValue];
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
fileLength = (UInt64)[userDefaults doubleForKey:cache_video_file_size];
//fileLength = 20955211;
fileOffset = 0;
//NSLog(@"fileAttributes = %@",fileAttributes);
//NSLog(@"fileLength = %llu",fileLength);
aborted = NO;
// We don't bother opening the file here.
// If this is a HEAD request we only need to know the fileLength.
}
return self;
}
部分视频出现如下错误
_itemFailedToPlayToEnd: {
kind = 1;
new = 2;
old = 0;
}
解决参见 http://blog.csdn.net/zttjhm/article/details/8581189