CoolFlutterIJK.m 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228
  1. //
  2. // Created by Caijinglong on 2019-03-08.
  3. //
  4. #import "CoolFlutterIJK.h"
  5. #import "CoolVideoInfo.h"
  6. #import "CoolIjkNotifyChannel.h"
  7. #import <IJKMediaFramework/IJKMediaFramework.h>
  8. #import <IJKMediaFramework/IJKMediaPlayer.h>
  9. #import <AVFoundation/AVFoundation.h>
  10. #import <libkern/OSAtomic.h>
  11. @interface CoolFlutterIJK () <FlutterTexture, KKIjkNotifyDelegate>
  12. @end
  13. @implementation CoolFlutterIJK {
  14. int64_t textureId;
  15. CADisplayLink *displayLink;
  16. NSObject <FlutterTextureRegistry> *textures;
  17. IJKFFMoviePlayerController *controller;
  18. CVPixelBufferRef latestPixelBuffer;
  19. FlutterMethodChannel *channel;
  20. CoolIjkNotifyChannel *notifyChannel;
  21. }
  22. - (instancetype)initWithRegistrar:(NSObject <FlutterPluginRegistrar> *)registrar {
  23. self = [super init];
  24. if (self) {
  25. self.registrar = registrar;
  26. textures = [self.registrar textures];
  27. textureId = [textures registerTexture:self];
  28. NSString *channelName = [NSString stringWithFormat:@"top.kikt/ijkplayer/%lli", textureId];
  29. channel = [FlutterMethodChannel methodChannelWithName:channelName binaryMessenger:[registrar messenger]];
  30. [channel setMethodCallHandler:^(FlutterMethodCall *call, FlutterResult result) {
  31. [self handleMethodCall:call result:result];
  32. }];
  33. }
  34. return self;
  35. }
  36. - (void)dispose {
  37. [notifyChannel dispose];
  38. [[self.registrar textures] unregisterTexture:self.id];
  39. [controller stop];
  40. [controller shutdown];
  41. controller = nil;
  42. displayLink.paused = YES;
  43. [displayLink invalidate];
  44. }
  45. - (void)handleMethodCall:(FlutterMethodCall *)call result:(FlutterResult)result {
  46. if ([@"play" isEqualToString:call.method]) {
  47. [self play];
  48. result(@(YES));
  49. } else if ([@"pause" isEqualToString:call.method]) {
  50. [self pause];
  51. result(@(YES));
  52. } else if ([@"stop" isEqualToString:call.method]) {
  53. [self stop];
  54. result(@(YES));
  55. } else if ([@"setNetworkDataSource" isEqualToString:call.method]) {
  56. @try {
  57. NSDictionary *params = call.arguments;
  58. NSString *uri = params[@"uri"];
  59. [self setDataSourceWithUri:uri];
  60. result(@(YES));
  61. }
  62. @catch (NSException *exception) {
  63. NSLog(@"Exception occurred: %@, %@", exception, [exception userInfo]);
  64. result([FlutterError errorWithCode:@"1" message:@"设置失败" details:nil]);
  65. }
  66. } else if ([@"setAssetDataSource" isEqualToString:call.method]) {
  67. @try {
  68. NSDictionary *params = [call arguments];
  69. NSString *name = params[@"name"];
  70. NSString *pkg = params[@"package"];
  71. IJKFFMoviePlayerController *playerController = [self createControllerWithAssetName:name pkg:pkg];
  72. [self setDataSourceWithController:playerController];
  73. result(@(YES));
  74. }
  75. @catch (NSException *exception) {
  76. NSLog(@"Exception occurred: %@, %@", exception, [exception userInfo]);
  77. result([FlutterError errorWithCode:@"1" message:@"设置失败" details:nil]);
  78. }
  79. } else if ([@"setFileDataSource" isEqualToString:call.method]) {
  80. NSDictionary *params = call.arguments;
  81. NSString *path = params[@"path"];
  82. IJKFFMoviePlayerController *playerController = [self createControllerWithPath:path];
  83. [self setDataSourceWithController:playerController];
  84. result(@(YES));
  85. } else if ([@"seekTo" isEqualToString:call.method]) {
  86. NSDictionary *params = call.arguments;
  87. double target = [params[@"target"] doubleValue];
  88. [self seekTo:target];
  89. result(@(YES));
  90. } else if ([@"getInfo" isEqualToString:call.method]) {
  91. CoolVideoInfo *info = [self getInfo];
  92. result([info toMap]);
  93. } else if ([@"setVolume" isEqualToString:call.method]) {
  94. NSDictionary *params = [self params:call];
  95. float v = [params[@"volume"] floatValue] / 100;
  96. controller.playbackVolume = v;
  97. result(@(YES));
  98. } else {
  99. result(FlutterMethodNotImplemented);
  100. }
  101. }
  102. - (NSDictionary *)params:(FlutterMethodCall *)call {
  103. }
  104. + (instancetype)ijkWithRegistrar:(NSObject <FlutterPluginRegistrar> *)registrar {
  105. return [[self alloc] initWithRegistrar:registrar];
  106. }
  107. - (int64_t)id {
  108. return textureId;
  109. }
  110. - (void)play {
  111. [controller play];
  112. }
  113. - (void)pause {
  114. [controller pause];
  115. }
  116. - (void)stop {
  117. [controller stop];
  118. }
  119. - (void)setDataSourceWithController:(IJKFFMoviePlayerController *)ctl {
  120. if (ctl) {
  121. controller = ctl;
  122. [self prepare];
  123. }
  124. }
  125. - (IJKFFOptions *)createOption {
  126. IJKFFOptions *options = [IJKFFOptions optionsByDefault];
  127. return options;
  128. }
  129. - (void)setDataSourceWithUri:(NSString *)uri {
  130. IJKFFOptions *options = [self createOption];
  131. controller = [[IJKFFMoviePlayerController alloc] initWithContentURLString:uri withOptions:options];
  132. [self prepare];
  133. }
  134. - (void)prepare {
  135. [controller prepareToPlay];
  136. if (displayLink) {
  137. displayLink.paused = YES;
  138. [displayLink invalidate];
  139. displayLink = nil;
  140. }
  141. displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(onDisplayLink:)];
  142. [displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
  143. displayLink.paused = YES;
  144. notifyChannel = [CoolIjkNotifyChannel channelWithController:controller textureId:textureId registrar:self.registrar];
  145. notifyChannel.infoDelegate = self;
  146. }
  147. - (IJKFFMoviePlayerController *)createControllerWithAssetName:(NSString *)assetName pkg:(NSString *)pkg {
  148. NSString *asset;
  149. if (!pkg) {
  150. asset = [self.registrar lookupKeyForAsset:assetName];
  151. } else {
  152. asset = [self.registrar lookupKeyForAsset:assetName fromPackage:pkg];
  153. }
  154. NSString *path = [[NSBundle mainBundle] pathForResource:asset ofType:nil];
  155. NSURL *url = [NSURL fileURLWithPath:path];
  156. IJKFFOptions *options = [self createOption];
  157. return [[IJKFFMoviePlayerController alloc] initWithContentURL:url withOptions:options];
  158. }
  159. - (IJKFFMoviePlayerController *)createControllerWithPath:(NSString *)path {
  160. NSURL *url = [NSURL fileURLWithPath:path];
  161. IJKFFOptions *options = [self createOption];
  162. return [[IJKFFMoviePlayerController alloc] initWithContentURL:url withOptions:options];
  163. }
  164. - (void)seekTo:(double)target {
  165. [controller setCurrentPlaybackTime:target];
  166. }
  167. - (void)onDisplayLink:(CADisplayLink *)link {
  168. [textures textureFrameAvailable:textureId];
  169. }
  170. - (CVPixelBufferRef _Nullable)copyPixelBuffer {
  171. CVPixelBufferRef newBuffer = [controller framePixelbuffer];
  172. if (newBuffer) {
  173. CFRetain(newBuffer);
  174. CVPixelBufferRef pixelBuffer = latestPixelBuffer;
  175. while (!OSAtomicCompareAndSwapPtrBarrier(pixelBuffer, newBuffer, (void **) &latestPixelBuffer)) {
  176. pixelBuffer = latestPixelBuffer;
  177. }
  178. return pixelBuffer;
  179. }
  180. return NULL;
  181. }
  182. - (CoolVideoInfo *)getInfo {
  183. CoolVideoInfo *info = [CoolVideoInfo new];
  184. CGSize size = [controller naturalSize];
  185. NSTimeInterval duration = [controller duration];
  186. NSTimeInterval currentPlaybackTime = [controller currentPlaybackTime];
  187. info.size = size;
  188. info.duration = duration;
  189. info.currentPosition = currentPlaybackTime;
  190. info.isPlaying = [controller isPlaying];
  191. return info;
  192. }
  193. @end