SVGAParser.m 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328
  1. //
  2. // SVGAParser.m
  3. // SVGAPlayer
  4. //
  5. // Created by 崔明辉 on 16/6/17.
  6. // Copyright © 2016年 UED Center. All rights reserved.
  7. //
  8. #import "SVGAParser.h"
  9. #import "SVGAVideoEntity.h"
  10. #import "Svga.pbobjc.h"
  11. #import <zlib.h>
  12. #import "SSZipArchive.h"
  13. #import <CommonCrypto/CommonDigest.h>
  14. @interface SVGAParser ()
  15. @end
  16. @implementation SVGAParser
  17. static NSOperationQueue *parseQueue;
  18. static NSOperationQueue *unzipQueue;
  19. + (void)load {
  20. parseQueue = [NSOperationQueue new];
  21. parseQueue.maxConcurrentOperationCount = 8;
  22. unzipQueue = [NSOperationQueue new];
  23. unzipQueue.maxConcurrentOperationCount = 1;
  24. }
  25. - (void)parseWithURL:(nonnull NSURL *)URL
  26. completionBlock:(void ( ^ _Nonnull )(SVGAVideoEntity * _Nullable videoItem))completionBlock
  27. failureBlock:(void ( ^ _Nullable)(NSError * _Nullable error))failureBlock {
  28. if ([[NSFileManager defaultManager] fileExistsAtPath:[self cacheDirectory:[self cacheKey:URL]]]) {
  29. [self parseWithCacheKey:[self cacheKey:URL] completionBlock:^(SVGAVideoEntity * _Nonnull videoItem) {
  30. if (completionBlock) {
  31. completionBlock(videoItem);
  32. }
  33. } failureBlock:^(NSError * _Nonnull error) {
  34. [self clearCache:[self cacheKey:URL]];
  35. if (failureBlock) {
  36. failureBlock(error);
  37. }
  38. }];
  39. return;
  40. }
  41. [[[NSURLSession sharedSession] dataTaskWithURL:URL completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
  42. if (error == nil && data != nil) {
  43. [self parseWithData:data cacheKey:[self cacheKey:URL] completionBlock:^(SVGAVideoEntity * _Nonnull videoItem) {
  44. if (completionBlock) {
  45. completionBlock(videoItem);
  46. }
  47. } failureBlock:^(NSError * _Nonnull error) {
  48. [self clearCache:[self cacheKey:URL]];
  49. if (failureBlock) {
  50. failureBlock(error);
  51. }
  52. }];
  53. }
  54. else {
  55. if (failureBlock) {
  56. failureBlock(error);
  57. }
  58. }
  59. }] resume];
  60. }
  61. - (void)parseWithNamed:(NSString *)named
  62. inBundle:(NSBundle *)inBundle
  63. completionBlock:(void (^)(SVGAVideoEntity * _Nonnull))completionBlock
  64. failureBlock:(void (^)(NSError * _Nonnull))failureBlock {
  65. NSString *filePath = [(inBundle ?: [NSBundle mainBundle]) pathForResource:named ofType:@"svga"];
  66. if (filePath != nil) {
  67. NSString *cacheKey = [self cacheKey:[NSURL fileURLWithPath:filePath]];
  68. [self parseWithData:[NSData dataWithContentsOfFile:filePath]
  69. cacheKey:cacheKey
  70. completionBlock:completionBlock
  71. failureBlock:failureBlock];
  72. }
  73. else {
  74. failureBlock([NSError errorWithDomain:@"SVGAParser" code:404 userInfo:@{NSLocalizedDescriptionKey: @"File not exist."}]);
  75. }
  76. }
  77. - (void)parseWithCacheKey:(nonnull NSString *)cacheKey
  78. completionBlock:(void ( ^ _Nullable)(SVGAVideoEntity * _Nonnull videoItem))completionBlock
  79. failureBlock:(void ( ^ _Nullable)(NSError * _Nonnull error))failureBlock {
  80. [parseQueue addOperationWithBlock:^{
  81. SVGAVideoEntity *cacheItem = [SVGAVideoEntity readCache:cacheKey];
  82. if (cacheItem != nil) {
  83. if (completionBlock) {
  84. completionBlock(cacheItem);
  85. }
  86. return;
  87. }
  88. NSString *cacheDir = [self cacheDirectory:cacheKey];
  89. if ([[NSFileManager defaultManager] fileExistsAtPath:[cacheDir stringByAppendingString:@"/movie.binary"]]) {
  90. NSError *err;
  91. NSData *protoData = [NSData dataWithContentsOfFile:[cacheDir stringByAppendingString:@"/movie.binary"]];
  92. SVGAProtoMovieEntity *protoObject = [SVGAProtoMovieEntity parseFromData:protoData error:&err];
  93. if (!err) {
  94. SVGAVideoEntity *videoItem = [[SVGAVideoEntity alloc] initWithProtoObject:protoObject cacheDir:cacheDir];
  95. [videoItem resetImagesWithProtoObject:protoObject];
  96. [videoItem resetSpritesWithProtoObject:protoObject];
  97. [videoItem saveCache:cacheKey];
  98. if (completionBlock) {
  99. completionBlock(videoItem);
  100. }
  101. }
  102. else {
  103. if (failureBlock) {
  104. failureBlock([NSError errorWithDomain:NSFilePathErrorKey code:-1 userInfo:nil]);
  105. }
  106. }
  107. }
  108. else {
  109. NSError *err;
  110. NSData *JSONData = [NSData dataWithContentsOfFile:[cacheDir stringByAppendingString:@"/movie.spec"]];
  111. if (JSONData != nil) {
  112. NSDictionary *JSONObject = [NSJSONSerialization JSONObjectWithData:JSONData options:kNilOptions error:&err];
  113. if ([JSONObject isKindOfClass:[NSDictionary class]]) {
  114. SVGAVideoEntity *videoItem = [[SVGAVideoEntity alloc] initWithJSONObject:JSONObject cacheDir:cacheDir];
  115. [videoItem resetImagesWithJSONObject:JSONObject];
  116. [videoItem resetSpritesWithJSONObject:JSONObject];
  117. [videoItem saveCache:cacheKey];
  118. if (completionBlock) {
  119. completionBlock(videoItem);
  120. }
  121. }
  122. }
  123. else {
  124. if (failureBlock) {
  125. failureBlock([NSError errorWithDomain:NSFilePathErrorKey code:-1 userInfo:nil]);
  126. }
  127. }
  128. }
  129. }];
  130. }
  131. - (void)clearCache:(nonnull NSString *)cacheKey {
  132. NSString *cacheDir = [self cacheDirectory:cacheKey];
  133. [[NSFileManager defaultManager] removeItemAtPath:cacheDir error:NULL];
  134. }
  135. - (void)parseWithData:(nonnull NSData *)data
  136. cacheKey:(nonnull NSString *)cacheKey
  137. completionBlock:(void ( ^ _Nullable)(SVGAVideoEntity * _Nonnull videoItem))completionBlock
  138. failureBlock:(void ( ^ _Nullable)(NSError * _Nonnull error))failureBlock {
  139. SVGAVideoEntity *cacheItem = [SVGAVideoEntity readCache:cacheKey];
  140. if (cacheItem != nil) {
  141. if (completionBlock) {
  142. completionBlock(cacheItem);
  143. }
  144. return;
  145. }
  146. NSData *tag = [data subdataWithRange:NSMakeRange(0, 4)];
  147. if (![[tag description] isEqualToString:@"<504b0304>"]) {
  148. // Maybe is SVGA 2.0.0
  149. [parseQueue addOperationWithBlock:^{
  150. NSData *inflateData = [self zlibInflate:data];
  151. NSError *err;
  152. SVGAProtoMovieEntity *protoObject = [SVGAProtoMovieEntity parseFromData:inflateData error:&err];
  153. if (!err) {
  154. SVGAVideoEntity *videoItem = [[SVGAVideoEntity alloc] initWithProtoObject:protoObject cacheDir:@""];
  155. [videoItem resetImagesWithProtoObject:protoObject];
  156. [videoItem resetSpritesWithProtoObject:protoObject];
  157. [videoItem saveCache:cacheKey];
  158. if (completionBlock) {
  159. completionBlock(videoItem);
  160. }
  161. }
  162. }];
  163. return ;
  164. }
  165. [unzipQueue addOperationWithBlock:^{
  166. if ([[NSFileManager defaultManager] fileExistsAtPath:[self cacheDirectory:cacheKey]]) {
  167. [self parseWithCacheKey:cacheKey completionBlock:^(SVGAVideoEntity * _Nonnull videoItem) {
  168. if (completionBlock) {
  169. completionBlock(videoItem);
  170. }
  171. } failureBlock:^(NSError * _Nonnull error) {
  172. [self clearCache:cacheKey];
  173. if (failureBlock) {
  174. failureBlock(error);
  175. }
  176. }];
  177. return;
  178. }
  179. NSString *tmpPath = [NSTemporaryDirectory() stringByAppendingFormat:@"%u.svga", arc4random()];
  180. if (data != nil) {
  181. [data writeToFile:tmpPath atomically:YES];
  182. NSString *cacheDir = [self cacheDirectory:cacheKey];
  183. if ([cacheDir isKindOfClass:[NSString class]]) {
  184. [[NSFileManager defaultManager] createDirectoryAtPath:cacheDir withIntermediateDirectories:NO attributes:nil error:nil];
  185. [SSZipArchive unzipFileAtPath:tmpPath toDestination:[self cacheDirectory:cacheKey] progressHandler:^(NSString * _Nonnull entry, unz_file_info zipInfo, long entryNumber, long total) {
  186. } completionHandler:^(NSString *path, BOOL succeeded, NSError *error) {
  187. if (error != nil) {
  188. if (failureBlock) {
  189. failureBlock(error);
  190. }
  191. }
  192. else {
  193. if ([[NSFileManager defaultManager] fileExistsAtPath:[cacheDir stringByAppendingString:@"/movie.binary"]]) {
  194. NSError *err;
  195. NSData *protoData = [NSData dataWithContentsOfFile:[cacheDir stringByAppendingString:@"/movie.binary"]];
  196. SVGAProtoMovieEntity *protoObject = [SVGAProtoMovieEntity parseFromData:protoData error:&err];
  197. if (!err) {
  198. SVGAVideoEntity *videoItem = [[SVGAVideoEntity alloc] initWithProtoObject:protoObject cacheDir:cacheDir];
  199. [videoItem resetImagesWithProtoObject:protoObject];
  200. [videoItem resetSpritesWithProtoObject:protoObject];
  201. [videoItem saveCache:cacheKey];
  202. if (completionBlock) {
  203. completionBlock(videoItem);
  204. }
  205. }
  206. else {
  207. if (failureBlock) {
  208. failureBlock([NSError errorWithDomain:NSFilePathErrorKey code:-1 userInfo:nil]);
  209. }
  210. }
  211. }
  212. else {
  213. NSError *err;
  214. NSData *JSONData = [NSData dataWithContentsOfFile:[cacheDir stringByAppendingString:@"/movie.spec"]];
  215. if (JSONData != nil) {
  216. NSDictionary *JSONObject = [NSJSONSerialization JSONObjectWithData:JSONData options:kNilOptions error:&err];
  217. if ([JSONObject isKindOfClass:[NSDictionary class]]) {
  218. SVGAVideoEntity *videoItem = [[SVGAVideoEntity alloc] initWithJSONObject:JSONObject cacheDir:cacheDir];
  219. [videoItem resetImagesWithJSONObject:JSONObject];
  220. [videoItem resetSpritesWithJSONObject:JSONObject];
  221. [videoItem saveCache:cacheKey];
  222. if (completionBlock) {
  223. completionBlock(videoItem);
  224. }
  225. }
  226. }
  227. else {
  228. if (failureBlock) {
  229. failureBlock([NSError errorWithDomain:NSFilePathErrorKey code:-1 userInfo:nil]);
  230. }
  231. }
  232. }
  233. }
  234. }];
  235. }
  236. else {
  237. if (failureBlock) {
  238. failureBlock([NSError errorWithDomain:NSFilePathErrorKey code:-1 userInfo:nil]);
  239. }
  240. }
  241. }
  242. else {
  243. if (failureBlock) {
  244. failureBlock([NSError errorWithDomain:@"Data Error" code:-1 userInfo:nil]);
  245. }
  246. }
  247. }];
  248. }
  249. - (nonnull NSString *)cacheKey:(NSURL *)URL {
  250. return [self MD5String:URL.absoluteString];
  251. }
  252. - (nullable NSString *)cacheDirectory:(NSString *)cacheKey {
  253. NSString *cacheDir = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) firstObject];
  254. return [cacheDir stringByAppendingFormat:@"/%@", cacheKey];
  255. }
  256. - (NSString *)MD5String:(NSString *)str {
  257. const char *cstr = [str UTF8String];
  258. unsigned char result[16];
  259. CC_MD5(cstr, (CC_LONG)strlen(cstr), result);
  260. return [NSString stringWithFormat:
  261. @"%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X",
  262. result[0], result[1], result[2], result[3],
  263. result[4], result[5], result[6], result[7],
  264. result[8], result[9], result[10], result[11],
  265. result[12], result[13], result[14], result[15]
  266. ];
  267. }
  268. - (NSData *)zlibInflate:(NSData *)data
  269. {
  270. if ([data length] == 0) return data;
  271. unsigned full_length = (unsigned)[data length];
  272. unsigned half_length = (unsigned)[data length] / 2;
  273. NSMutableData *decompressed = [NSMutableData dataWithLength: full_length + half_length];
  274. BOOL done = NO;
  275. int status;
  276. z_stream strm;
  277. strm.next_in = (Bytef *)[data bytes];
  278. strm.avail_in = (unsigned)[data length];
  279. strm.total_out = 0;
  280. strm.zalloc = Z_NULL;
  281. strm.zfree = Z_NULL;
  282. if (inflateInit (&strm) != Z_OK) return nil;
  283. while (!done)
  284. {
  285. // Make sure we have enough room and reset the lengths.
  286. if (strm.total_out >= [decompressed length])
  287. [decompressed increaseLengthBy: half_length];
  288. strm.next_out = [decompressed mutableBytes] + strm.total_out;
  289. strm.avail_out = (uInt)([decompressed length] - strm.total_out);
  290. // Inflate another chunk.
  291. status = inflate (&strm, Z_SYNC_FLUSH);
  292. if (status == Z_STREAM_END) done = YES;
  293. else if (status != Z_OK) break;
  294. }
  295. if (inflateEnd (&strm) != Z_OK) return nil;
  296. // Set real length.
  297. if (done)
  298. {
  299. [decompressed setLength: strm.total_out];
  300. return [NSData dataWithData: decompressed];
  301. }
  302. else return nil;
  303. }
  304. @end