SVGAVideoEntity.m 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280
  1. //
  2. // SVGAVideoEntity.m
  3. // SVGAPlayer
  4. //
  5. // Created by 崔明辉 on 16/6/17.
  6. // Copyright © 2016年 UED Center. All rights reserved.
  7. //
  8. #import <AVFoundation/AVFoundation.h>
  9. #import "SVGAVideoEntity.h"
  10. #import "SVGABezierPath.h"
  11. #import "SVGAVideoSpriteEntity.h"
  12. #import "SVGAAudioEntity.h"
  13. #import "Svga.pbobjc.h"
  14. #import "UIImage+Svga.h"
  15. #define MP3_MAGIC_NUMBER "ID3"
  16. @interface SVGAVideoEntity ()
  17. @property (nonatomic, assign) CGSize videoSize;
  18. @property (nonatomic, assign) int FPS;
  19. @property (nonatomic, assign) int frames;
  20. @property (nonatomic, copy) NSDictionary<NSString *, UIImage *> *images;
  21. @property (nonatomic, copy) NSDictionary<NSString *, NSData *> *audiosData;
  22. @property (nonatomic, copy) NSArray<SVGAVideoSpriteEntity *> *sprites;
  23. @property (nonatomic, copy) NSArray<SVGAAudioEntity *> *audios;
  24. @property (nonatomic, copy) NSString *cacheDir;
  25. @end
  26. @implementation SVGAVideoEntity
  27. static NSCache *videoCache;
  28. static NSMapTable * weakCache;
  29. static dispatch_semaphore_t videoSemaphore;
  30. + (void)load {
  31. static dispatch_once_t onceToken;
  32. dispatch_once(&onceToken, ^{
  33. videoCache = [[NSCache alloc] init];
  34. weakCache = [[NSMapTable alloc] initWithKeyOptions:NSPointerFunctionsStrongMemory
  35. valueOptions:NSPointerFunctionsWeakMemory
  36. capacity:64];
  37. videoSemaphore = dispatch_semaphore_create(1);
  38. });
  39. }
  40. - (instancetype)initWithJSONObject:(NSDictionary *)JSONObject cacheDir:(NSString *)cacheDir {
  41. self = [super init];
  42. if (self) {
  43. _videoSize = CGSizeMake(100, 100);
  44. _FPS = 20;
  45. _images = @{};
  46. _cacheDir = cacheDir;
  47. [self resetMovieWithJSONObject:JSONObject];
  48. }
  49. return self;
  50. }
  51. - (void)resetMovieWithJSONObject:(NSDictionary *)JSONObject {
  52. if ([JSONObject isKindOfClass:[NSDictionary class]]) {
  53. NSDictionary *movieObject = JSONObject[@"movie"];
  54. if ([movieObject isKindOfClass:[NSDictionary class]]) {
  55. NSDictionary *viewBox = movieObject[@"viewBox"];
  56. if ([viewBox isKindOfClass:[NSDictionary class]]) {
  57. NSNumber *width = viewBox[@"width"];
  58. NSNumber *height = viewBox[@"height"];
  59. if ([width isKindOfClass:[NSNumber class]] && [height isKindOfClass:[NSNumber class]]) {
  60. _videoSize = CGSizeMake(width.floatValue, height.floatValue);
  61. }
  62. }
  63. NSNumber *FPS = movieObject[@"fps"];
  64. if ([FPS isKindOfClass:[NSNumber class]]) {
  65. _FPS = [FPS intValue];
  66. }
  67. NSNumber *frames = movieObject[@"frames"];
  68. if ([frames isKindOfClass:[NSNumber class]]) {
  69. _frames = [frames intValue];
  70. }
  71. }
  72. }
  73. }
  74. - (void)resetImagesWithJSONObject:(NSDictionary *)JSONObject {
  75. if ([JSONObject isKindOfClass:[NSDictionary class]]) {
  76. NSMutableDictionary<NSString *, UIImage *> *images = [[NSMutableDictionary alloc] init];
  77. NSDictionary<NSString *, NSString *> *JSONImages = JSONObject[@"images"];
  78. if ([JSONImages isKindOfClass:[NSDictionary class]]) {
  79. [JSONImages enumerateKeysAndObjectsUsingBlock:^(NSString * _Nonnull key, NSString * _Nonnull obj, BOOL * _Nonnull stop) {
  80. if ([obj isKindOfClass:[NSString class]]) {
  81. NSString *filePath = [self.cacheDir stringByAppendingFormat:@"/%@.png", obj];
  82. // NSData *imageData = [NSData dataWithContentsOfFile:filePath];
  83. NSData *imageData = [NSData dataWithContentsOfFile:filePath options:NSDataReadingMappedIfSafe error:NULL];
  84. if (imageData != nil) {
  85. UIImage *image = [[UIImage alloc] initWithData:imageData scale:2.0];
  86. if (image != nil) {
  87. [images setObject:image forKey:[key stringByDeletingPathExtension]];
  88. }
  89. }
  90. }
  91. }];
  92. }
  93. self.images = images;
  94. }
  95. }
  96. - (void)resetSpritesWithJSONObject:(NSDictionary *)JSONObject {
  97. if ([JSONObject isKindOfClass:[NSDictionary class]]) {
  98. NSMutableArray<SVGAVideoSpriteEntity *> *sprites = [[NSMutableArray alloc] init];
  99. NSArray<NSDictionary *> *JSONSprites = JSONObject[@"sprites"];
  100. if ([JSONSprites isKindOfClass:[NSArray class]]) {
  101. [JSONSprites enumerateObjectsUsingBlock:^(NSDictionary * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
  102. if ([obj isKindOfClass:[NSDictionary class]]) {
  103. SVGAVideoSpriteEntity *spriteItem = [[SVGAVideoSpriteEntity alloc] initWithJSONObject:obj];
  104. [sprites addObject:spriteItem];
  105. }
  106. }];
  107. }
  108. self.sprites = sprites;
  109. }
  110. }
  111. - (instancetype)initWithProtoObject:(SVGAProtoMovieEntity *)protoObject cacheDir:(NSString *)cacheDir {
  112. self = [super init];
  113. if (self) {
  114. _videoSize = CGSizeMake(100, 100);
  115. _FPS = 20;
  116. _images = @{};
  117. _cacheDir = cacheDir;
  118. [self resetMovieWithProtoObject:protoObject];
  119. }
  120. return self;
  121. }
  122. - (void)resetMovieWithProtoObject:(SVGAProtoMovieEntity *)protoObject {
  123. if (protoObject.hasParams) {
  124. self.videoSize = CGSizeMake((CGFloat)protoObject.params.viewBoxWidth, (CGFloat)protoObject.params.viewBoxHeight);
  125. self.FPS = (int)protoObject.params.fps;
  126. self.frames = (int)protoObject.params.frames;
  127. }
  128. }
  129. + (BOOL)isMP3Data:(NSData *)data {
  130. BOOL result = NO;
  131. if (!strncmp([data bytes], MP3_MAGIC_NUMBER, strlen(MP3_MAGIC_NUMBER))) {
  132. result = YES;
  133. }
  134. return result;
  135. }
  136. - (void)resetImagesWithProtoObject:(SVGAProtoMovieEntity *)protoObject {
  137. NSMutableDictionary<NSString *, UIImage *> *images = [[NSMutableDictionary alloc] init];
  138. NSMutableDictionary<NSString *, NSData *> *audiosData = [[NSMutableDictionary alloc] init];
  139. NSDictionary *protoImages = [protoObject.images copy];
  140. for (NSString *key in protoImages) {
  141. // 确保 key 是有效的字符串,避免使用 nil 或空字符串
  142. if (![key isKindOfClass:[NSString class]] || key.length == 0) {
  143. continue;
  144. }
  145. id protoData = protoImages[key];
  146. if (!protoData) {
  147. continue; // 防止 protoData 为空
  148. }
  149. NSString *fileName = [[NSString alloc] initWithData:protoImages[key] encoding:NSUTF8StringEncoding];
  150. if (fileName != nil) {
  151. NSString *filePath = [self.cacheDir stringByAppendingFormat:@"/%@.png", fileName];
  152. if (![[NSFileManager defaultManager] fileExistsAtPath:filePath]) {
  153. filePath = [self.cacheDir stringByAppendingFormat:@"/%@", fileName];
  154. }
  155. if ([[NSFileManager defaultManager] fileExistsAtPath:filePath]) {
  156. // NSData *imageData = [NSData dataWithContentsOfFile:filePath];
  157. NSData *imageData = [NSData dataWithContentsOfFile:filePath options:NSDataReadingMappedIfSafe error:NULL];
  158. if (imageData != nil) {
  159. UIImage *image = [[UIImage alloc] initWithData:imageData scale:2.0];
  160. if (image != nil) {
  161. [images setObject:image forKey:key];
  162. }
  163. }
  164. }
  165. }
  166. else if ([protoImages[key] isKindOfClass:[NSData class]]) {
  167. if ([SVGAVideoEntity isMP3Data:protoImages[key]]) {
  168. // mp3
  169. [audiosData setObject:protoImages[key] forKey:key];
  170. } else {
  171. UIImage *image = [[UIImage alloc] initWithData:protoImages[key] scale:2.0];
  172. if (image != nil) {
  173. image = [image imageByResizeToSize:image.size];
  174. if(image == nil) {
  175. continue;
  176. }
  177. [images setObject:image forKey:key];
  178. }
  179. }
  180. }
  181. }
  182. self.images = images;
  183. self.audiosData = audiosData;
  184. }
  185. - (void)resetSpritesWithProtoObject:(SVGAProtoMovieEntity *)protoObject {
  186. NSMutableArray<SVGAVideoSpriteEntity *> *sprites = [[NSMutableArray alloc] init];
  187. NSArray *protoSprites = [protoObject.spritesArray copy];
  188. [protoSprites enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
  189. if ([obj isKindOfClass:[SVGAProtoSpriteEntity class]]) {
  190. SVGAVideoSpriteEntity *spriteItem = [[SVGAVideoSpriteEntity alloc] initWithProtoObject:obj];
  191. [sprites addObject:spriteItem];
  192. }
  193. }];
  194. self.sprites = sprites;
  195. }
  196. - (void)resetAudiosWithProtoObject:(SVGAProtoMovieEntity *)protoObject {
  197. NSMutableArray<SVGAAudioEntity *> *audios = [[NSMutableArray alloc] init];
  198. NSArray *protoAudios = [protoObject.audiosArray copy];
  199. [protoAudios enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
  200. if ([obj isKindOfClass:[SVGAProtoAudioEntity class]]) {
  201. SVGAAudioEntity *audioItem = [[SVGAAudioEntity alloc] initWithProtoObject:obj];
  202. [audios addObject:audioItem];
  203. }
  204. }];
  205. self.audios = audios;
  206. }
  207. + (SVGAVideoEntity *)readCache:(NSString *)cacheKey {
  208. dispatch_semaphore_wait(videoSemaphore, DISPATCH_TIME_FOREVER);
  209. SVGAVideoEntity * object = [videoCache objectForKey:cacheKey];
  210. if (!object) {
  211. object = [weakCache objectForKey:cacheKey];
  212. }
  213. dispatch_semaphore_signal(videoSemaphore);
  214. return object;
  215. }
  216. - (void)saveCache:(NSString *)cacheKey {
  217. dispatch_semaphore_wait(videoSemaphore, DISPATCH_TIME_FOREVER);
  218. [videoCache setObject:self forKey:cacheKey];
  219. dispatch_semaphore_signal(videoSemaphore);
  220. }
  221. - (void)saveWeakCache:(NSString *)cacheKey {
  222. dispatch_semaphore_wait(videoSemaphore, DISPATCH_TIME_FOREVER);
  223. [weakCache setObject:self forKey:cacheKey];
  224. dispatch_semaphore_signal(videoSemaphore);
  225. }
  226. - (void)clearCache:(NSString *)cacheKey {
  227. dispatch_semaphore_wait(videoSemaphore, DISPATCH_TIME_FOREVER);
  228. [videoCache removeObjectForKey:cacheKey];
  229. [weakCache removeObjectForKey:cacheKey];
  230. dispatch_semaphore_signal(videoSemaphore);
  231. }
  232. + (void)clearCache:(NSString *)cacheKey {
  233. dispatch_semaphore_wait(videoSemaphore, DISPATCH_TIME_FOREVER);
  234. [videoCache removeObjectForKey:cacheKey];
  235. [weakCache removeObjectForKey:cacheKey];
  236. dispatch_semaphore_signal(videoSemaphore);
  237. }
  238. //- (void)dealloc {
  239. // NSLog(@"===== SVGAVideoEntity =====");
  240. //}
  241. @end
  242. @interface SVGAVideoSpriteEntity()
  243. @property (nonatomic, copy) NSString *imageKey;
  244. @property (nonatomic, copy) NSArray<SVGAVideoSpriteFrameEntity *> *frames;
  245. @property (nonatomic, copy) NSString *matteKey;
  246. @end