SVGAParser.m 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420
  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/SSZipArchive.h>
  13. #import <CommonCrypto/CommonDigest.h>
  14. #define ZIP_MAGIC_NUMBER "PK"
  15. @interface SVGAParser ()
  16. @end
  17. @implementation SVGAParser
  18. static NSOperationQueue *parseQueue;
  19. static NSOperationQueue *unzipQueue;
  20. + (void)load {
  21. parseQueue = [NSOperationQueue new];
  22. parseQueue.maxConcurrentOperationCount = 8;
  23. unzipQueue = [NSOperationQueue new];
  24. unzipQueue.maxConcurrentOperationCount = 1;
  25. }
  26. - (void)parseWithURL:(nonnull NSURL *)URL
  27. completionBlock:(void ( ^ _Nonnull )(SVGAVideoEntity * _Nullable videoItem))completionBlock
  28. failureBlock:(void ( ^ _Nullable)(NSError * _Nullable error))failureBlock {
  29. [self parseWithURLRequest:[NSURLRequest requestWithURL:URL cachePolicy:NSURLRequestReturnCacheDataElseLoad timeoutInterval:20.0]
  30. completionBlock:completionBlock
  31. failureBlock:failureBlock];
  32. }
  33. - (void)parseWithURLRequest:(NSURLRequest *)URLRequest completionBlock:(void (^)(SVGAVideoEntity * _Nullable))completionBlock failureBlock:(void (^)(NSError * _Nullable))failureBlock {
  34. if (URLRequest.URL == nil) {
  35. if (failureBlock) {
  36. [[NSOperationQueue mainQueue] addOperationWithBlock:^{
  37. failureBlock([NSError errorWithDomain:@"SVGAParser" code:411 userInfo:@{NSLocalizedDescriptionKey: @"URL cannot be nil."}]);
  38. }];
  39. }
  40. return;
  41. }
  42. if ([[NSFileManager defaultManager] fileExistsAtPath:[self cacheDirectory:[self cacheKey:URLRequest.URL]]]) {
  43. [self parseWithCacheKey:[self cacheKey:URLRequest.URL] completionBlock:^(SVGAVideoEntity * _Nonnull videoItem) {
  44. if (completionBlock) {
  45. [[NSOperationQueue mainQueue] addOperationWithBlock:^{
  46. completionBlock(videoItem);
  47. }];
  48. }
  49. } failureBlock:^(NSError * _Nonnull error) {
  50. [self clearCache:[self cacheKey:URLRequest.URL]];
  51. if (failureBlock) {
  52. [[NSOperationQueue mainQueue] addOperationWithBlock:^{
  53. failureBlock(error);
  54. }];
  55. }
  56. }];
  57. return;
  58. }
  59. [[[NSURLSession sharedSession] dataTaskWithRequest:URLRequest completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
  60. if (error == nil && data != nil) {
  61. [self parseWithData:data cacheKey:[self cacheKey:URLRequest.URL] completionBlock:^(SVGAVideoEntity * _Nonnull videoItem) {
  62. if (completionBlock) {
  63. [[NSOperationQueue mainQueue] addOperationWithBlock:^{
  64. completionBlock(videoItem);
  65. }];
  66. }
  67. } failureBlock:^(NSError * _Nonnull error) {
  68. [self clearCache:[self cacheKey:URLRequest.URL]];
  69. if (failureBlock) {
  70. [[NSOperationQueue mainQueue] addOperationWithBlock:^{
  71. failureBlock(error);
  72. }];
  73. }
  74. }];
  75. }
  76. else {
  77. if (failureBlock) {
  78. [[NSOperationQueue mainQueue] addOperationWithBlock:^{
  79. failureBlock(error);
  80. }];
  81. }
  82. }
  83. }] resume];
  84. }
  85. - (void)parseWithNamed:(NSString *)named
  86. inBundle:(NSBundle *)inBundle
  87. completionBlock:(void (^)(SVGAVideoEntity * _Nonnull))completionBlock
  88. failureBlock:(void (^)(NSError * _Nonnull))failureBlock {
  89. NSString *filePath = [(inBundle ?: [NSBundle mainBundle]) pathForResource:named ofType:@"svga"];
  90. if (filePath == nil) {
  91. if (failureBlock) {
  92. [[NSOperationQueue mainQueue] addOperationWithBlock:^{
  93. failureBlock([NSError errorWithDomain:@"SVGAParser" code:404 userInfo:@{NSLocalizedDescriptionKey: @"File not exist."}]);
  94. }];
  95. }
  96. return;
  97. }
  98. [self parseWithData:[NSData dataWithContentsOfFile:filePath]
  99. cacheKey:[self cacheKey:[NSURL fileURLWithPath:filePath]]
  100. completionBlock:completionBlock
  101. failureBlock:failureBlock];
  102. }
  103. - (void)parseWithCacheKey:(nonnull NSString *)cacheKey
  104. completionBlock:(void ( ^ _Nullable)(SVGAVideoEntity * _Nonnull videoItem))completionBlock
  105. failureBlock:(void ( ^ _Nullable)(NSError * _Nonnull error))failureBlock {
  106. [parseQueue addOperationWithBlock:^{
  107. SVGAVideoEntity *cacheItem = [SVGAVideoEntity readCache:cacheKey];
  108. if (cacheItem != nil) {
  109. if (completionBlock) {
  110. [[NSOperationQueue mainQueue] addOperationWithBlock:^{
  111. completionBlock(cacheItem);
  112. }];
  113. }
  114. return;
  115. }
  116. NSString *cacheDir = [self cacheDirectory:cacheKey];
  117. if ([[NSFileManager defaultManager] fileExistsAtPath:[cacheDir stringByAppendingString:@"/movie.binary"]]) {
  118. NSError *err;
  119. NSData *protoData = [NSData dataWithContentsOfFile:[cacheDir stringByAppendingString:@"/movie.binary"]];
  120. SVGAProtoMovieEntity *protoObject = [SVGAProtoMovieEntity parseFromData:protoData error:&err];
  121. if (!err && [protoObject isKindOfClass:[SVGAProtoMovieEntity class]]) {
  122. SVGAVideoEntity *videoItem = [[SVGAVideoEntity alloc] initWithProtoObject:protoObject cacheDir:cacheDir];
  123. [videoItem resetImagesWithProtoObject:protoObject];
  124. [videoItem resetSpritesWithProtoObject:protoObject];
  125. [videoItem resetAudiosWithProtoObject:protoObject];
  126. if (self.enabledMemoryCache) {
  127. [videoItem saveCache:cacheKey];
  128. } else {
  129. [videoItem saveWeakCache:cacheKey];
  130. }
  131. if (completionBlock) {
  132. [[NSOperationQueue mainQueue] addOperationWithBlock:^{
  133. completionBlock(videoItem);
  134. }];
  135. }
  136. }
  137. else {
  138. if (failureBlock) {
  139. [[NSOperationQueue mainQueue] addOperationWithBlock:^{
  140. failureBlock([NSError errorWithDomain:NSFilePathErrorKey code:-1 userInfo:nil]);
  141. }];
  142. }
  143. }
  144. }
  145. else {
  146. NSError *err;
  147. NSData *JSONData = [NSData dataWithContentsOfFile:[cacheDir stringByAppendingString:@"/movie.spec"]];
  148. if (JSONData != nil) {
  149. NSDictionary *JSONObject = [NSJSONSerialization JSONObjectWithData:JSONData options:kNilOptions error:&err];
  150. if ([JSONObject isKindOfClass:[NSDictionary class]]) {
  151. SVGAVideoEntity *videoItem = [[SVGAVideoEntity alloc] initWithJSONObject:JSONObject cacheDir:cacheDir];
  152. [videoItem resetImagesWithJSONObject:JSONObject];
  153. [videoItem resetSpritesWithJSONObject:JSONObject];
  154. if (self.enabledMemoryCache) {
  155. [videoItem saveCache:cacheKey];
  156. } else {
  157. [videoItem saveWeakCache:cacheKey];
  158. }
  159. if (completionBlock) {
  160. [[NSOperationQueue mainQueue] addOperationWithBlock:^{
  161. completionBlock(videoItem);
  162. }];
  163. }
  164. }
  165. }
  166. else {
  167. if (failureBlock) {
  168. [[NSOperationQueue mainQueue] addOperationWithBlock:^{
  169. failureBlock([NSError errorWithDomain:NSFilePathErrorKey code:-1 userInfo:nil]);
  170. }];
  171. }
  172. }
  173. }
  174. }];
  175. }
  176. - (void)clearCache:(nonnull NSString *)cacheKey {
  177. NSString *cacheDir = [self cacheDirectory:cacheKey];
  178. [[NSFileManager defaultManager] removeItemAtPath:cacheDir error:NULL];
  179. }
  180. + (BOOL)isZIPData:(NSData *)data {
  181. BOOL result = NO;
  182. if (!strncmp([data bytes], ZIP_MAGIC_NUMBER, strlen(ZIP_MAGIC_NUMBER))) {
  183. result = YES;
  184. }
  185. return result;
  186. }
  187. - (void)parseWithData:(nonnull NSData *)data
  188. cacheKey:(nonnull NSString *)cacheKey
  189. completionBlock:(void ( ^ _Nullable)(SVGAVideoEntity * _Nonnull videoItem))completionBlock
  190. failureBlock:(void ( ^ _Nullable)(NSError * _Nonnull error))failureBlock {
  191. SVGAVideoEntity *cacheItem = [SVGAVideoEntity readCache:cacheKey];
  192. if (cacheItem != nil) {
  193. if (completionBlock) {
  194. [[NSOperationQueue mainQueue] addOperationWithBlock:^{
  195. completionBlock(cacheItem);
  196. }];
  197. }
  198. return;
  199. }
  200. if (!data || data.length < 4) {
  201. return;
  202. }
  203. if (![SVGAParser isZIPData:data]) {
  204. // Maybe is SVGA 2.0.0
  205. [parseQueue addOperationWithBlock:^{
  206. NSData *inflateData = [self zlibInflate:data];
  207. NSError *err;
  208. SVGAProtoMovieEntity *protoObject = [SVGAProtoMovieEntity parseFromData:inflateData error:&err];
  209. if (!err && [protoObject isKindOfClass:[SVGAProtoMovieEntity class]]) {
  210. SVGAVideoEntity *videoItem = [[SVGAVideoEntity alloc] initWithProtoObject:protoObject cacheDir:@""];
  211. [videoItem resetImagesWithProtoObject:protoObject];
  212. [videoItem resetSpritesWithProtoObject:protoObject];
  213. [videoItem resetAudiosWithProtoObject:protoObject];
  214. if (self.enabledMemoryCache) {
  215. [videoItem saveCache:cacheKey];
  216. } else {
  217. [videoItem saveWeakCache:cacheKey];
  218. }
  219. if (completionBlock) {
  220. [[NSOperationQueue mainQueue] addOperationWithBlock:^{
  221. completionBlock(videoItem);
  222. }];
  223. }
  224. }
  225. }];
  226. return ;
  227. }
  228. [unzipQueue addOperationWithBlock:^{
  229. if ([[NSFileManager defaultManager] fileExistsAtPath:[self cacheDirectory:cacheKey]]) {
  230. [self parseWithCacheKey:cacheKey completionBlock:^(SVGAVideoEntity * _Nonnull videoItem) {
  231. if (completionBlock) {
  232. [[NSOperationQueue mainQueue] addOperationWithBlock:^{
  233. completionBlock(videoItem);
  234. }];
  235. }
  236. } failureBlock:^(NSError * _Nonnull error) {
  237. [self clearCache:cacheKey];
  238. if (failureBlock) {
  239. [[NSOperationQueue mainQueue] addOperationWithBlock:^{
  240. failureBlock(error);
  241. }];
  242. }
  243. }];
  244. return;
  245. }
  246. NSString *tmpPath = [NSTemporaryDirectory() stringByAppendingFormat:@"%u.svga", arc4random()];
  247. if (data != nil) {
  248. [data writeToFile:tmpPath atomically:YES];
  249. NSString *cacheDir = [self cacheDirectory:cacheKey];
  250. if ([cacheDir isKindOfClass:[NSString class]]) {
  251. [[NSFileManager defaultManager] createDirectoryAtPath:cacheDir withIntermediateDirectories:NO attributes:nil error:nil];
  252. [SSZipArchive unzipFileAtPath:tmpPath toDestination:[self cacheDirectory:cacheKey] progressHandler:^(NSString * _Nonnull entry, unz_file_info zipInfo, long entryNumber, long total) {
  253. } completionHandler:^(NSString *path, BOOL succeeded, NSError *error) {
  254. if (error != nil) {
  255. if (failureBlock) {
  256. [[NSOperationQueue mainQueue] addOperationWithBlock:^{
  257. failureBlock(error);
  258. }];
  259. }
  260. }
  261. else {
  262. if ([[NSFileManager defaultManager] fileExistsAtPath:[cacheDir stringByAppendingString:@"/movie.binary"]]) {
  263. NSError *err;
  264. NSData *protoData = [NSData dataWithContentsOfFile:[cacheDir stringByAppendingString:@"/movie.binary"]];
  265. SVGAProtoMovieEntity *protoObject = [SVGAProtoMovieEntity parseFromData:protoData error:&err];
  266. if (!err) {
  267. SVGAVideoEntity *videoItem = [[SVGAVideoEntity alloc] initWithProtoObject:protoObject cacheDir:cacheDir];
  268. [videoItem resetImagesWithProtoObject:protoObject];
  269. [videoItem resetSpritesWithProtoObject:protoObject];
  270. if (self.enabledMemoryCache) {
  271. [videoItem saveCache:cacheKey];
  272. } else {
  273. [videoItem saveWeakCache:cacheKey];
  274. }
  275. if (completionBlock) {
  276. [[NSOperationQueue mainQueue] addOperationWithBlock:^{
  277. completionBlock(videoItem);
  278. }];
  279. }
  280. }
  281. else {
  282. if (failureBlock) {
  283. [[NSOperationQueue mainQueue] addOperationWithBlock:^{
  284. failureBlock([NSError errorWithDomain:NSFilePathErrorKey code:-1 userInfo:nil]);
  285. }];
  286. }
  287. }
  288. }
  289. else {
  290. NSError *err;
  291. NSData *JSONData = [NSData dataWithContentsOfFile:[cacheDir stringByAppendingString:@"/movie.spec"]];
  292. if (JSONData != nil) {
  293. NSDictionary *JSONObject = [NSJSONSerialization JSONObjectWithData:JSONData options:kNilOptions error:&err];
  294. if ([JSONObject isKindOfClass:[NSDictionary class]]) {
  295. SVGAVideoEntity *videoItem = [[SVGAVideoEntity alloc] initWithJSONObject:JSONObject cacheDir:cacheDir];
  296. [videoItem resetImagesWithJSONObject:JSONObject];
  297. [videoItem resetSpritesWithJSONObject:JSONObject];
  298. if (self.enabledMemoryCache) {
  299. [videoItem saveCache:cacheKey];
  300. } else {
  301. [videoItem saveWeakCache:cacheKey];
  302. }
  303. if (completionBlock) {
  304. [[NSOperationQueue mainQueue] addOperationWithBlock:^{
  305. completionBlock(videoItem);
  306. }];
  307. }
  308. }
  309. }
  310. else {
  311. if (failureBlock) {
  312. [[NSOperationQueue mainQueue] addOperationWithBlock:^{
  313. failureBlock([NSError errorWithDomain:NSFilePathErrorKey code:-1 userInfo:nil]);
  314. }];
  315. }
  316. }
  317. }
  318. }
  319. }];
  320. }
  321. else {
  322. if (failureBlock) {
  323. [[NSOperationQueue mainQueue] addOperationWithBlock:^{
  324. failureBlock([NSError errorWithDomain:NSFilePathErrorKey code:-1 userInfo:nil]);
  325. }];
  326. }
  327. }
  328. }
  329. else {
  330. if (failureBlock) {
  331. [[NSOperationQueue mainQueue] addOperationWithBlock:^{
  332. failureBlock([NSError errorWithDomain:@"Data Error" code:-1 userInfo:nil]);
  333. }];
  334. }
  335. }
  336. }];
  337. }
  338. - (nonnull NSString *)cacheKey:(NSURL *)URL {
  339. return [self MD5String:URL.absoluteString];
  340. }
  341. - (nullable NSString *)cacheDirectory:(NSString *)cacheKey {
  342. NSString *cacheDir = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) firstObject];
  343. return [cacheDir stringByAppendingFormat:@"/%@", cacheKey];
  344. }
  345. - (NSString *)MD5String:(NSString *)str {
  346. const char *cstr = [str UTF8String];
  347. unsigned char result[16];
  348. CC_MD5(cstr, (CC_LONG)strlen(cstr), result);
  349. return [NSString stringWithFormat:
  350. @"%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X",
  351. result[0], result[1], result[2], result[3],
  352. result[4], result[5], result[6], result[7],
  353. result[8], result[9], result[10], result[11],
  354. result[12], result[13], result[14], result[15]
  355. ];
  356. }
  357. - (NSData *)zlibInflate:(NSData *)data
  358. {
  359. if ([data length] == 0) return data;
  360. unsigned full_length = (unsigned)[data length];
  361. unsigned half_length = (unsigned)[data length] / 2;
  362. NSMutableData *decompressed = [NSMutableData dataWithLength: full_length + half_length];
  363. BOOL done = NO;
  364. int status;
  365. z_stream strm;
  366. strm.next_in = (Bytef *)[data bytes];
  367. strm.avail_in = (unsigned)[data length];
  368. strm.total_out = 0;
  369. strm.zalloc = Z_NULL;
  370. strm.zfree = Z_NULL;
  371. if (inflateInit (&strm) != Z_OK) return nil;
  372. while (!done)
  373. {
  374. // Make sure we have enough room and reset the lengths.
  375. if (strm.total_out >= [decompressed length])
  376. [decompressed increaseLengthBy: half_length];
  377. strm.next_out = [decompressed mutableBytes] + strm.total_out;
  378. strm.avail_out = (uInt)([decompressed length] - strm.total_out);
  379. // Inflate another chunk.
  380. status = inflate (&strm, Z_SYNC_FLUSH);
  381. if (status == Z_STREAM_END) done = YES;
  382. else if (status != Z_OK) break;
  383. }
  384. if (inflateEnd (&strm) != Z_OK) return nil;
  385. // Set real length.
  386. if (done)
  387. {
  388. [decompressed setLength: strm.total_out];
  389. return [NSData dataWithData: decompressed];
  390. }
  391. else return nil;
  392. }
  393. @end