123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537 |
- //
- // SVGAParser.m
- // SVGAPlayer
- //
- // Created by 崔明辉 on 16/6/17.
- // Copyright © 2016年 UED Center. All rights reserved.
- //
- #import "SVGAParser.h"
- #import "SVGAVideoEntity.h"
- #import "Svga.pbobjc.h"
- #import <zlib.h>
- #import <SSZipArchive/SSZipArchive.h>
- #import <CommonCrypto/CommonDigest.h>
- #define ZIP_MAGIC_NUMBER "PK"
- @interface SVGAParser ()
- @end
- @implementation SVGAParser
- static NSOperationQueue *parseQueue;
- static NSOperationQueue *unzipQueue;
- + (void)load {
- parseQueue = [NSOperationQueue new];
- parseQueue.maxConcurrentOperationCount = 8;
- unzipQueue = [NSOperationQueue new];
- unzipQueue.maxConcurrentOperationCount = 1;
- }
- - (void)parseWithURL:(nonnull NSURL *)URL
- completionBlock:(void ( ^ _Nonnull )(SVGAVideoEntity * _Nullable videoItem))completionBlock
- failureBlock:(void ( ^ _Nullable)(NSError * _Nullable error))failureBlock {
- [self parseWithURLRequest:[NSURLRequest requestWithURL:URL cachePolicy:NSURLRequestReturnCacheDataElseLoad timeoutInterval:20.0]
- completionBlock:completionBlock
- failureBlock:failureBlock];
- }
- - (void)parseWithURLRequest:(NSURLRequest *)URLRequest completionBlock:(void (^)(SVGAVideoEntity * _Nullable))completionBlock failureBlock:(void (^)(NSError * _Nullable))failureBlock {
- if (URLRequest.URL == nil) {
- if (failureBlock) {
- [[NSOperationQueue mainQueue] addOperationWithBlock:^{
- failureBlock([NSError errorWithDomain:@"SVGAParser" code:411 userInfo:@{NSLocalizedDescriptionKey: @"URL cannot be nil."}]);
- }];
- }
- return;
- }
-
- NSString *cacheKeyMD5 = [self cacheKey:URLRequest.URL];
- if ([[NSFileManager defaultManager] fileExistsAtPath:[self cacheDirectory:cacheKeyMD5]]) {
- [self parseWithCacheKey:cacheKeyMD5 completionBlock:^(SVGAVideoEntity * _Nonnull videoItem) {
- if (completionBlock) {
- [[NSOperationQueue mainQueue] addOperationWithBlock:^{
- completionBlock(videoItem);
- }];
- }
- } failureBlock:^(NSError * _Nonnull error) {
- [self clearCache:cacheKeyMD5];
- if (failureBlock) {
- [[NSOperationQueue mainQueue] addOperationWithBlock:^{
- failureBlock(error);
- }];
- }
- }];
- return;
- }
-
- if ([[NSFileManager defaultManager] fileExistsAtPath:[self SVGADataCacheFilePath:cacheKeyMD5]]) {
- [self parseLocalSVGADataWithCacheKey:cacheKeyMD5 completionBlock:^(SVGAVideoEntity * _Nonnull videoItem) {
- if (completionBlock) {
- [[NSOperationQueue mainQueue] addOperationWithBlock:^{
- completionBlock(videoItem);
- }];
- }
- } failureBlock:^(NSError * _Nonnull error) {
- [self clearLocalSVGADataCache:cacheKeyMD5];
- if (failureBlock) {
- [[NSOperationQueue mainQueue] addOperationWithBlock:^{
- failureBlock(error);
- }];
- }
- }];
- return;
- }
-
- // 网络请求SVGAData
- __weak typeof(self) weakSelf = self;
- [[[NSURLSession sharedSession] dataTaskWithRequest:URLRequest completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
- if (error == nil && data != nil) {
- __strong typeof(weakSelf) strongSelf = weakSelf;
- [strongSelf parseWithSVGAData:data cacheKey:cacheKeyMD5 completionBlock:completionBlock failureBlock:failureBlock];
- [strongSelf saveToLocalWithSVGAData:data cacheKey:cacheKeyMD5];
- }
- else {
- if (failureBlock) {
- [[NSOperationQueue mainQueue] addOperationWithBlock:^{
- failureBlock(error);
- }];
- }
- }
- }] resume];
- }
- - (void)parseWithSVGAData:(NSData *)data cacheKey:(NSString *)cacheKey completionBlock:(void (^)(SVGAVideoEntity * _Nullable))completionBlock failureBlock:(void (^)(NSError * _Nullable))failureBlock {
- [self parseWithData:data cacheKey:cacheKey completionBlock:^(SVGAVideoEntity * _Nonnull videoItem) {
- if (completionBlock) {
- [[NSOperationQueue mainQueue] addOperationWithBlock:^{
- completionBlock(videoItem);
- }];
- }
- } failureBlock:^(NSError * _Nonnull error) {
- [self clearCache:cacheKey];
- [self clearLocalSVGADataCache:cacheKey];
- if (failureBlock) {
- [[NSOperationQueue mainQueue] addOperationWithBlock:^{
- failureBlock(error);
- }];
- }
- }];
- }
- - (void)parseWithNamed:(NSString *)named
- inBundle:(NSBundle *)inBundle
- completionBlock:(void (^)(SVGAVideoEntity * _Nonnull))completionBlock
- failureBlock:(void (^)(NSError * _Nonnull))failureBlock {
- NSString *filePath = [(inBundle ?: [NSBundle mainBundle]) pathForResource:named ofType:@"svga"];
- if (filePath == nil) {
- if (failureBlock) {
- [[NSOperationQueue mainQueue] addOperationWithBlock:^{
- failureBlock([NSError errorWithDomain:@"SVGAParser" code:404 userInfo:@{NSLocalizedDescriptionKey: @"File not exist."}]);
- }];
- }
- return;
- }
- [self parseWithData:[NSData dataWithContentsOfFile:filePath]
- cacheKey:[self cacheKey:[NSURL fileURLWithPath:filePath]]
- completionBlock:completionBlock
- failureBlock:failureBlock];
- }
- - (void)parseWithCacheKey:(nonnull NSString *)cacheKey
- completionBlock:(void ( ^ _Nullable)(SVGAVideoEntity * _Nonnull videoItem))completionBlock
- failureBlock:(void ( ^ _Nullable)(NSError * _Nonnull error))failureBlock {
- [parseQueue addOperationWithBlock:^{
- SVGAVideoEntity *cacheItem = [SVGAVideoEntity readCache:cacheKey];
- if (cacheItem != nil) {
- if (completionBlock) {
- [[NSOperationQueue mainQueue] addOperationWithBlock:^{
- completionBlock(cacheItem);
- }];
- }
- return;
- }
- NSString *cacheDir = [self cacheDirectory:cacheKey];
- if ([[NSFileManager defaultManager] fileExistsAtPath:[cacheDir stringByAppendingString:@"/movie.binary"]]) {
- NSError *err;
- NSData *protoData = [NSData dataWithContentsOfFile:[cacheDir stringByAppendingString:@"/movie.binary"]];
- SVGAProtoMovieEntity *protoObject = [SVGAProtoMovieEntity parseFromData:protoData error:&err];
- if (!err && [protoObject isKindOfClass:[SVGAProtoMovieEntity class]]) {
- SVGAVideoEntity *videoItem = [[SVGAVideoEntity alloc] initWithProtoObject:protoObject cacheDir:cacheDir];
- [videoItem resetImagesWithProtoObject:protoObject];
- [videoItem resetSpritesWithProtoObject:protoObject];
- [videoItem resetAudiosWithProtoObject:protoObject];
- if (self.enabledMemoryCache) {
- [videoItem saveCache:cacheKey];
- } else {
- [videoItem saveWeakCache:cacheKey];
- }
- if (completionBlock) {
- [[NSOperationQueue mainQueue] addOperationWithBlock:^{
- completionBlock(videoItem);
- }];
- }
- }
- else {
- if (failureBlock) {
- [[NSOperationQueue mainQueue] addOperationWithBlock:^{
- failureBlock([NSError errorWithDomain:NSFilePathErrorKey code:-1 userInfo:nil]);
- }];
- }
- }
- }
- else {
- NSError *err;
- NSData *JSONData = [NSData dataWithContentsOfFile:[cacheDir stringByAppendingString:@"/movie.spec"]];
- if (JSONData != nil) {
- NSDictionary *JSONObject = [NSJSONSerialization JSONObjectWithData:JSONData options:kNilOptions error:&err];
- if ([JSONObject isKindOfClass:[NSDictionary class]]) {
- SVGAVideoEntity *videoItem = [[SVGAVideoEntity alloc] initWithJSONObject:JSONObject cacheDir:cacheDir];
- [videoItem resetImagesWithJSONObject:JSONObject];
- [videoItem resetSpritesWithJSONObject:JSONObject];
- if (self.enabledMemoryCache) {
- [videoItem saveCache:cacheKey];
- } else {
- [videoItem saveWeakCache:cacheKey];
- }
- if (completionBlock) {
- [[NSOperationQueue mainQueue] addOperationWithBlock:^{
- completionBlock(videoItem);
- }];
- }
- }
- }
- else {
- if (failureBlock) {
- [[NSOperationQueue mainQueue] addOperationWithBlock:^{
- failureBlock([NSError errorWithDomain:NSFilePathErrorKey code:-1 userInfo:nil]);
- }];
- }
- }
- }
- }];
- }
- - (void)clearCache:(nonnull NSString *)cacheKey {
- NSString *cacheDir = [self cacheDirectory:cacheKey];
- [[NSFileManager defaultManager] removeItemAtPath:cacheDir error:NULL];
- }
- + (BOOL)isZIPData:(NSData *)data {
- BOOL result = NO;
- if (!strncmp([data bytes], ZIP_MAGIC_NUMBER, strlen(ZIP_MAGIC_NUMBER))) {
- result = YES;
- }
- return result;
- }
- - (void)parseWithData:(nonnull NSData *)data
- cacheKey:(nonnull NSString *)cacheKey
- completionBlock:(void ( ^ _Nullable)(SVGAVideoEntity * _Nonnull videoItem))completionBlock
- failureBlock:(void ( ^ _Nullable)(NSError * _Nonnull error))failureBlock {
- SVGAVideoEntity *cacheItem = [SVGAVideoEntity readCache:cacheKey];
- if (cacheItem != nil) {
- if (completionBlock) {
- [[NSOperationQueue mainQueue] addOperationWithBlock:^{
- completionBlock(cacheItem);
- }];
- }
- return;
- }
- if (!data || data.length < 4) {
- return;
- }
- if (![SVGAParser isZIPData:data]) {
- // Maybe is SVGA 2.0.0
- [parseQueue addOperationWithBlock:^{
- NSData *inflateData = [self zlibInflate:data];
- NSError *err;
- SVGAProtoMovieEntity *protoObject = [SVGAProtoMovieEntity parseFromData:inflateData error:&err];
- if (!err && [protoObject isKindOfClass:[SVGAProtoMovieEntity class]]) {
- SVGAVideoEntity *videoItem = [[SVGAVideoEntity alloc] initWithProtoObject:protoObject cacheDir:@""];
- [videoItem resetImagesWithProtoObject:protoObject];
- [videoItem resetSpritesWithProtoObject:protoObject];
- [videoItem resetAudiosWithProtoObject:protoObject];
- if (self.enabledMemoryCache) {
- [videoItem saveCache:cacheKey];
- } else {
- [videoItem saveWeakCache:cacheKey];
- }
- if (completionBlock) {
- [[NSOperationQueue mainQueue] addOperationWithBlock:^{
- completionBlock(videoItem);
- }];
- }
- }
- }];
- return ;
- }
- [unzipQueue addOperationWithBlock:^{
- if ([[NSFileManager defaultManager] fileExistsAtPath:[self cacheDirectory:cacheKey]]) {
- [self parseWithCacheKey:cacheKey completionBlock:^(SVGAVideoEntity * _Nonnull videoItem) {
- if (completionBlock) {
- [[NSOperationQueue mainQueue] addOperationWithBlock:^{
- completionBlock(videoItem);
- }];
- }
- } failureBlock:^(NSError * _Nonnull error) {
- [self clearCache:cacheKey];
- [self clearLocalSVGADataCache:cacheKey];
- if (failureBlock) {
- [[NSOperationQueue mainQueue] addOperationWithBlock:^{
- failureBlock(error);
- }];
- }
- }];
- return;
- }
- NSString *tmpPath = [NSTemporaryDirectory() stringByAppendingFormat:@"%u.svga", arc4random()];
- if (data != nil) {
- [data writeToFile:tmpPath atomically:YES];
- NSString *cacheDir = [self cacheDirectory:cacheKey];
- if ([cacheDir isKindOfClass:[NSString class]]) {
- [[NSFileManager defaultManager] createDirectoryAtPath:cacheDir withIntermediateDirectories:NO attributes:nil error:nil];
- [SSZipArchive unzipFileAtPath:tmpPath toDestination:[self cacheDirectory:cacheKey] progressHandler:^(NSString * _Nonnull entry, unz_file_info zipInfo, long entryNumber, long total) {
-
- } completionHandler:^(NSString *path, BOOL succeeded, NSError *error) {
- if (error != nil) {
- if (failureBlock) {
- [[NSOperationQueue mainQueue] addOperationWithBlock:^{
- failureBlock(error);
- }];
- }
- }
- else {
- if ([[NSFileManager defaultManager] fileExistsAtPath:[cacheDir stringByAppendingString:@"/movie.binary"]]) {
- NSError *err;
- NSData *protoData = [NSData dataWithContentsOfFile:[cacheDir stringByAppendingString:@"/movie.binary"]];
- SVGAProtoMovieEntity *protoObject = [SVGAProtoMovieEntity parseFromData:protoData error:&err];
- if (!err) {
- SVGAVideoEntity *videoItem = [[SVGAVideoEntity alloc] initWithProtoObject:protoObject cacheDir:cacheDir];
- [videoItem resetImagesWithProtoObject:protoObject];
- [videoItem resetSpritesWithProtoObject:protoObject];
- if (self.enabledMemoryCache) {
- [videoItem saveCache:cacheKey];
- } else {
- [videoItem saveWeakCache:cacheKey];
- }
- if (completionBlock) {
- [[NSOperationQueue mainQueue] addOperationWithBlock:^{
- completionBlock(videoItem);
- }];
- }
- }
- else {
- if (failureBlock) {
- [[NSOperationQueue mainQueue] addOperationWithBlock:^{
- failureBlock([NSError errorWithDomain:NSFilePathErrorKey code:-1 userInfo:nil]);
- }];
- }
- }
- }
- else {
- NSError *err;
- NSData *JSONData = [NSData dataWithContentsOfFile:[cacheDir stringByAppendingString:@"/movie.spec"]];
- if (JSONData != nil) {
- NSDictionary *JSONObject = [NSJSONSerialization JSONObjectWithData:JSONData options:kNilOptions error:&err];
- if ([JSONObject isKindOfClass:[NSDictionary class]]) {
- SVGAVideoEntity *videoItem = [[SVGAVideoEntity alloc] initWithJSONObject:JSONObject cacheDir:cacheDir];
- [videoItem resetImagesWithJSONObject:JSONObject];
- [videoItem resetSpritesWithJSONObject:JSONObject];
- if (self.enabledMemoryCache) {
- [videoItem saveCache:cacheKey];
- } else {
- [videoItem saveWeakCache:cacheKey];
- }
- if (completionBlock) {
- [[NSOperationQueue mainQueue] addOperationWithBlock:^{
- completionBlock(videoItem);
- }];
- }
- }
- }
- else {
- if (failureBlock) {
- [[NSOperationQueue mainQueue] addOperationWithBlock:^{
- failureBlock([NSError errorWithDomain:NSFilePathErrorKey code:-1 userInfo:nil]);
- }];
- }
- }
- }
- }
- }];
- }
- else {
- if (failureBlock) {
- [[NSOperationQueue mainQueue] addOperationWithBlock:^{
- failureBlock([NSError errorWithDomain:NSFilePathErrorKey code:-1 userInfo:nil]);
- }];
- }
- }
- }
- else {
- if (failureBlock) {
- [[NSOperationQueue mainQueue] addOperationWithBlock:^{
- failureBlock([NSError errorWithDomain:@"Data Error" code:-1 userInfo:nil]);
- }];
- }
- }
- }];
- }
- - (nonnull NSString *)cacheKey:(NSURL *)URL {
- return [self MD5String:URL.absoluteString];
- }
- - (nullable NSString *)cacheDirectory:(NSString *)cacheKey {
- NSString *cacheDir = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) firstObject];
- return [cacheDir stringByAppendingFormat:@"/%@", cacheKey];
- }
- - (NSString *)MD5String:(NSString *)str {
- const char *cstr = [str UTF8String];
- unsigned char result[16];
- CC_MD5(cstr, (CC_LONG)strlen(cstr), result);
- return [NSString stringWithFormat:
- @"%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X",
- result[0], result[1], result[2], result[3],
- result[4], result[5], result[6], result[7],
- result[8], result[9], result[10], result[11],
- result[12], result[13], result[14], result[15]
- ];
- }
- - (NSData *)zlibInflate:(NSData *)data
- {
- if ([data length] == 0) return data;
-
- unsigned full_length = (unsigned)[data length];
- unsigned half_length = (unsigned)[data length] / 2;
-
- NSMutableData *decompressed = [NSMutableData dataWithLength: full_length + half_length];
- BOOL done = NO;
- int status;
-
- z_stream strm;
- strm.next_in = (Bytef *)[data bytes];
- strm.avail_in = (unsigned)[data length];
- strm.total_out = 0;
- strm.zalloc = Z_NULL;
- strm.zfree = Z_NULL;
-
- if (inflateInit (&strm) != Z_OK) return nil;
-
- while (!done)
- {
- // Make sure we have enough room and reset the lengths.
- if (strm.total_out >= [decompressed length])
- [decompressed increaseLengthBy: half_length];
- strm.next_out = [decompressed mutableBytes] + strm.total_out;
- strm.avail_out = (uInt)([decompressed length] - strm.total_out);
-
- // Inflate another chunk.
- status = inflate (&strm, Z_SYNC_FLUSH);
- if (status == Z_STREAM_END) done = YES;
- else if (status != Z_OK) break;
- }
- if (inflateEnd (&strm) != Z_OK) return nil;
-
- // Set real length.
- if (done)
- {
- [decompressed setLength: strm.total_out];
- return [NSData dataWithData: decompressed];
- }
- else return nil;
- }
- #pragma mark - Customer 方法
- - (void)parseLocalSVGADataWithCacheKey:(nonnull NSString *)cacheKey
- completionBlock:(void ( ^ _Nullable)(SVGAVideoEntity * _Nonnull videoItem))completionBlock
- failureBlock:(void ( ^ _Nullable)(NSError * _Nonnull error))failureBlock {
- SVGAVideoEntity *cacheItem = [SVGAVideoEntity readCache:cacheKey];
- if (cacheItem != nil) {
- if (completionBlock) {
- [[NSOperationQueue mainQueue] addOperationWithBlock:^{
- completionBlock(cacheItem);
- }];
- }
- return;
- }
- // 读取本地SVGAData缓存
- NSData *data = [self readLocalSVGADataWithCacheKey:cacheKey];
- if (data != nil) {
- [self parseWithSVGAData:data cacheKey:cacheKey completionBlock:completionBlock failureBlock:failureBlock];
- return;
- }
- }
- - (nullable NSString *)SVGADataCacheFilePath:(NSString *)cacheKey {
- NSString *SVGADataCacheDir = [self SVGADataCacheDirectory];
- if (![[NSFileManager defaultManager] fileExistsAtPath: SVGADataCacheDir]) {
- [[NSFileManager defaultManager] createDirectoryAtPath:SVGADataCacheDir withIntermediateDirectories:NO attributes:nil error:nil];
- }
- return [SVGADataCacheDir stringByAppendingFormat:@"/%@", cacheKey];
- }
- - (nullable NSString *)SVGADataCacheDirectory {
- return [[self diskCacheDirectory] stringByAppendingFormat:@"/SVGA_data_cache"];
- }
- - (nullable NSString *)diskCacheDirectory {
- return [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) firstObject];;
- }
- /** 保存二进制SVGAData到本地
- *
- */
- - (void)saveToLocalWithSVGAData:(NSData *)data cacheKey:(NSString *)cacheKey {
- if (![[NSFileManager defaultManager] fileExistsAtPath:[self SVGADataCacheFilePath:cacheKey]]) {
- [data writeToFile:[self SVGADataCacheFilePath:cacheKey] atomically:true];
- }
- }
- /** 读取本地二进制 SVGAData
- *
- */
- - (NSData *)readLocalSVGADataWithCacheKey:(NSString *)cacheKey{
- if ([[NSFileManager defaultManager] fileExistsAtPath:[self SVGADataCacheFilePath:cacheKey]]) {
- return [NSData dataWithContentsOfFile:[self SVGADataCacheFilePath:cacheKey]];
- }
- return nil;
- }
- - (void)clearLocalSVGADataCache:(nonnull NSString *)cacheKey {
- NSString *cacheDir = [self SVGADataCacheFilePath:cacheKey];
- [[NSFileManager defaultManager] removeItemAtPath:cacheDir error:NULL];
- }
- - (void)clearLocalSVGADataCacheWithURLString:(nonnull NSString *)URLString {
- NSString *cacheDir = [self SVGADataCacheFilePath:[self MD5String:URLString]];
- NSError *error = nil;
- [[NSFileManager defaultManager] removeItemAtPath:cacheDir error:&error];
- }
- /** 下载SVGAData
- *
- */
- - (void)downloadSVGADataWithURLString:(NSString *)URLString {
- [parseQueue addOperationWithBlock:^{
- NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:URLString]];
- NSString *cacheKeyMD5 = [self MD5String:URLString];
- // 读取本地SVGAData缓存
- NSData *data = [self readLocalSVGADataWithCacheKey:cacheKeyMD5];
- if (data == nil) {
- [[[NSURLSession sharedSession] dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
- if (error == nil && data != nil) {
- [self saveToLocalWithSVGAData:data cacheKey:cacheKeyMD5];
- }
- }] resume];
- }
- }];
- }
- @end
|