SVGAParser.m 18 KB

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