| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394 |
- //
- // Created by Caijinglong on 2019-03-08.
- //
- #import "CoolFlutterIJK.h"
- #import "CoolVideoInfo.h"
- #import "CoolFlutterResult.h"
- #import <AVFoundation/AVFoundation.h>
- #import <libkern/OSAtomic.h>
- @interface CoolFlutterIJK () <FlutterTexture, KKIjkNotifyDelegate>
- @end
- @implementation CoolFlutterIJK {
- int64_t textureId;
- CADisplayLink *displayLink;
- NSObject <FlutterTextureRegistry> *textures;
- IJKFFMoviePlayerController *controller;
- CVPixelBufferRef latestPixelBuffer;
- FlutterMethodChannel *channel;
- CoolIjkNotifyChannel *notifyChannel;
- int degree;
- CoolFlutterResult *prepareResult;
- }
- - (instancetype)initWithRegistrar:(NSObject <FlutterPluginRegistrar> *)registrar {
- self = [super init];
- if (self) {
- self.registrar = registrar;
- textures = [self.registrar textures];
- textureId = [textures registerTexture:self];
- NSString *channelName = [NSString stringWithFormat:@"top.kikt/ijkplayer/%lli", textureId];
- channel = [FlutterMethodChannel methodChannelWithName:channelName binaryMessenger:[registrar messenger]];
- __weak typeof(&*self) weakSelf = self;
- [channel setMethodCallHandler:^(FlutterMethodCall *call, FlutterResult result) {
- [weakSelf handleMethodCall:call result:result];
- }];
- self.isDisposed = NO;
- }
- return self;
- }
- - (void)dispose {
- if(self.isDisposed){
- return;
- }
- self.isDisposed = YES;
- [notifyChannel dispose];
- [[self.registrar textures] unregisterTexture:self.id];
- [controller stop];
- [controller shutdown];
- controller = nil;
- displayLink.paused = YES;
- [displayLink invalidate];
- }
- - (void)handleMethodCall:(FlutterMethodCall *)call result:(FlutterResult)result {
- if(self.isDisposed){
- return;
- }
- if ([@"play" isEqualToString:call.method]) {
- [self play];
- result(@(YES));
- } else if ([@"pause" isEqualToString:call.method]) {
- [self pause];
- result(@(YES));
- } else if ([@"stop" isEqualToString:call.method]) {
- [self stop];
- result(@(YES));
- } else if ([@"setNetworkDataSource" isEqualToString:call.method]) {
- @try {
- NSDictionary *params = call.arguments;
- NSString *uri = params[@"uri"];
- NSDictionary *headers = params[@"headers"];
- [self setDataSourceWithUri:uri headers:headers result:[CoolFlutterResult resultWithResult:result]];
- }
- @catch (NSException *exception) {
- NSLog(@"Exception occurred: %@, %@", exception, [exception userInfo]);
- result([FlutterError errorWithCode:@"1" message:@"设置失败" details:nil]);
- }
- } else if ([@"setAssetDataSource" isEqualToString:call.method]) {
- @try {
- NSDictionary *params = [call arguments];
- NSString *name = params[@"name"];
- NSString *pkg = params[@"package"];
- IJKFFMoviePlayerController *playerController = [self createControllerWithAssetName:name pkg:pkg];
- [self setDataSourceWithController:playerController result:[CoolFlutterResult resultWithResult:result]];
- }
- @catch (NSException *exception) {
- NSLog(@"Exception occurred: %@, %@", exception, [exception userInfo]);
- result([FlutterError errorWithCode:@"1" message:@"设置失败" details:nil]);
- }
- } else if ([@"setFileDataSource" isEqualToString:call.method]) {
- NSDictionary *params = call.arguments;
- NSString *path = params[@"path"];
- IJKFFMoviePlayerController *playerController = [self createControllerWithPath:path];
- [self setDataSourceWithController:playerController result:[CoolFlutterResult resultWithResult:result]];
- } else if ([@"seekTo" isEqualToString:call.method]) {
- NSDictionary *params = call.arguments;
- double target = [params[@"target"] doubleValue];
- [self seekTo:target];
- result(@(YES));
- } else if ([@"getInfo" isEqualToString:call.method]) {
- CoolVideoInfo *info = [self getInfo];
- result([info toMap]);
- } else if ([@"setVolume" isEqualToString:call.method]) {
- NSDictionary *params = [self params:call];
- float v = [params[@"volume"] floatValue] / 100;
- controller.playbackVolume = v;
- result(@(YES));
- } else if ([@"screenShot" isEqualToString:call.method]) {
- __weak typeof(&*self) weakSelf = self;
- dispatch_async(dispatch_get_main_queue(), ^{
- NSData *data = [weakSelf screenShot];
- result(data);
- });
- } else if ([@"setSpeed" isEqualToString:call.method]) {
- float speedValue = [call.arguments floatValue];
- if (controller) {
- [controller setPlaybackRate:speedValue];
- }
- result(@YES);
- } else {
- result(FlutterMethodNotImplemented);
- }
- }
- - (NSDictionary *)params:(FlutterMethodCall *)call {
- return call.arguments;
- }
- + (instancetype)ijkWithRegistrar:(NSObject <FlutterPluginRegistrar> *)registrar {
- return [[self alloc] initWithRegistrar:registrar];
- }
- - (int64_t)id {
- return textureId;
- }
- - (void)play {
- [controller play];
- if (displayLink) {
- displayLink.paused = NO;
- }
- }
- - (void)pause {
- [controller pause];
- if (displayLink) {
- displayLink.paused = YES;
- }
- }
- - (void)stop {
- [controller stop];
- if (displayLink) {
- displayLink.paused = NO;
- }
- }
- - (void)setDataSourceWithController:(IJKFFMoviePlayerController *)ctl result:(CoolFlutterResult *)result {
- if (ctl) {
- controller = ctl;
- [self prepare:result];
- }
- }
- - (IJKFFOptions *)createOption {
- IJKFFOptions *options = [IJKFFOptions optionsByDefault];
- // see https://www.jianshu.com/p/843c86a9e9ad
- // [options setFormatOptionValue:@"fastseek" forKey:@"fflags"];
- // [options setFormatOptionIntValue:100 forKey:@"analyzemaxduration"];
- // [options setFormatOptionIntValue:1 forKey:@"analyzeduration"];
- // [options setFormatOptionIntValue:10240 forKey:@"probesize"];
- // [options setFormatOptionIntValue:1 forKey:@"flush_packets"];
- // [options setFormatOptionIntValue:5 forKey:@"reconnect"];
- // [options setFormatOptionIntValue:5 forKey:@"framedrop"];
- // [options setFormatOptionIntValue:1 forKey:@"enable-accurate-seek"];
-
- // [options setPlayerOptionIntValue:0 forKey:@"video-max-frame-width-default"];
- // [options setPlayerOptionIntValue:1 forKey:@"videotoolbox"];
- for (CoolIjkOption *opt in self.options) {
- if (opt) {
- NSString* key = opt.key;
- BOOL isString = [opt.value isKindOfClass:[NSString class]];
- BOOL isInt;
- if([opt.value isKindOfClass:[NSNumber class]]){
- isInt = strcmp([opt.value objCType], @encode(int)) == 0;
- }else{
- isInt = NO;
- }
- switch (opt.type) {
- case 0:
- if(isString){
- [options setFormatOptionValue:opt.value forKey:key];
- }else if(isInt){
- [options setFormatOptionIntValue:[opt.value intValue] forKey:key];
- }
- break;
- case 1:
- if(isString){
- [options setCodecOptionValue:opt.value forKey:key];
- }else if(isInt){
- [options setCodecOptionIntValue:[opt.value intValue] forKey:key];
- }
- break;
- case 2:
- if(isString){
- [options setSwsOptionValue:opt.value forKey:key];
- }else if(isInt){
- [options setSwsOptionIntValue:[opt.value intValue] forKey:key];
- }
- break;
- case 3:
- if(isString){
- [options setPlayerOptionValue:opt.value forKey:key];
- }else if(isInt){
- [options setPlayerOptionIntValue:[opt.value intValue] forKey:key];
- }
- break;
- default:
- break;
- }
- }
- }
-
- return options;
- }
- - (void)setDataSourceWithUri:(NSString *)uri headers:(NSDictionary *)headers result:(CoolFlutterResult *)result {
- IJKFFOptions *options = [self createOption];
- if (headers) {
- NSMutableString *headerString = [NSMutableString new];
- for (NSString *key in headers.allKeys) {
- NSString *value = headers[key];
- [headerString appendFormat:@"%@:%@", key, value];
- [headerString appendString:@"\r\n"];
- }
- [options setFormatOptionValue:headerString forKey:@"headers"];
- }
- controller = [[IJKFFMoviePlayerController alloc] initWithContentURLString:uri withOptions:options];
- [self prepare:result];
- }
- - (void)setDegree:(int)d {
- degree = d;
- }
- - (void)prepare:(CoolFlutterResult *)result {
- prepareResult = result;
- [controller prepareToPlay];
- if (displayLink) {
- displayLink.paused = YES;
- [displayLink invalidate];
- displayLink = nil;
- }
- displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(onDisplayLink:)];
- [displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
- notifyChannel = [CoolIjkNotifyChannel channelWithController:controller textureId:textureId registrar:self.registrar];
- notifyChannel.infoDelegate = self;
- }
- - (void)onLoadStateChange {
- // IJKMPMovieLoadState loadState = controller.loadState;
- if (prepareResult) {
- [prepareResult replyResult:@YES];
- }
- prepareResult = nil;
- }
- - (IJKFFMoviePlayerController *)createControllerWithAssetName:(NSString *)assetName pkg:(NSString *)pkg {
- NSString *asset;
- if (!pkg) {
- asset = [self.registrar lookupKeyForAsset:assetName];
- } else {
- asset = [self.registrar lookupKeyForAsset:assetName fromPackage:pkg];
- }
- NSString *path = [[NSBundle mainBundle] pathForResource:asset ofType:nil];
- NSURL *url = [NSURL fileURLWithPath:path];
- IJKFFOptions *options = [self createOption];
- return [[IJKFFMoviePlayerController alloc] initWithContentURL:url withOptions:options];
- }
- - (IJKFFMoviePlayerController *)createControllerWithPath:(NSString *)path {
- NSURL *url = [NSURL fileURLWithPath:path];
- IJKFFOptions *options = [self createOption];
- return [[IJKFFMoviePlayerController alloc] initWithContentURL:url withOptions:options];
- }
- - (void)seekTo:(double)target {
- [controller setCurrentPlaybackTime:target];
- }
- - (void)onDisplayLink:(CADisplayLink *)link {
- [textures textureFrameAvailable:textureId];
- }
- - (CVPixelBufferRef _Nullable)copyPixelBuffer {
- CVPixelBufferRef newBuffer = [controller framePixelbuffer];
- if (newBuffer) {
- CFRetain(newBuffer);
- CVPixelBufferRef pixelBuffer = latestPixelBuffer;
- while (!OSAtomicCompareAndSwapPtrBarrier(pixelBuffer, newBuffer, (void **) &latestPixelBuffer)) {
- pixelBuffer = latestPixelBuffer;
- }
- return pixelBuffer;
- }
- return NULL;
- }
- - (CoolVideoInfo *)getInfo {
- CoolVideoInfo *info = [CoolVideoInfo new];
- CGSize size = [controller naturalSize];
- NSTimeInterval duration = [controller duration];
- NSTimeInterval currentPlaybackTime = [controller currentPlaybackTime];
- info.size = size;
- info.duration = duration;
- info.currentPosition = currentPlaybackTime;
- info.isPlaying = [controller isPlaying];
- info.degree = degree;
- info.tcpSpeed = [controller tcpSpeed];
- info.outputFps = [controller fpsAtOutput];
- return info;
- }
- - (NSUInteger)degreeFromVideoFileWithURL:(NSURL *)url {
- NSUInteger mDegree = 0;
- AVAsset *asset = [AVAsset assetWithURL:url];
- NSArray *tracks = [asset tracksWithMediaType:AVMediaTypeVideo];
- if ([tracks count] > 0) {
- AVAssetTrack *videoTrack = tracks[0];
- CGAffineTransform t = videoTrack.preferredTransform;
- if (t.a == 0 && t.b == 1.0 && t.c == -1.0 && t.d == 0) {
- // Portrait
- mDegree = 90;
- } else if (t.a == 0 && t.b == -1.0 && t.c == 1.0 && t.d == 0) {
- // PortraitUpsideDown
- mDegree = 270;
- } else if (t.a == 1.0 && t.b == 0 && t.c == 0 && t.d == 1.0) {
- // LandscapeRight
- mDegree = 0;
- } else if (t.a == -1.0 && t.b == 0 && t.c == 0 && t.d == -1.0) {
- // LandscapeLeft
- mDegree = 180;
- }
- }
- return mDegree;
- }
- - (NSData*) screenShot{
- CVPixelBufferRef ref = [self copyPixelBuffer];
- if(!ref){
- return nil;
- }
-
- UIImage *img = [self convertPixeclBufferToUIImage:ref];
- return UIImageJPEGRepresentation(img, 1.0);
- }
- -(UIImage*)convertPixeclBufferToUIImage:(CVPixelBufferRef)pixelBuffer{
- CIImage *ciImage = [CIImage imageWithCVPixelBuffer:pixelBuffer];
-
- CIContext *temporaryContext = [CIContext contextWithOptions:nil];
- CGImageRef videoImage = [temporaryContext
- createCGImage:ciImage
- fromRect:CGRectMake(0, 0, CVPixelBufferGetWidth(pixelBuffer), CVPixelBufferGetHeight(pixelBuffer))];
-
- UIImage *uiImage = [UIImage imageWithCGImage:videoImage];
- CGImageRelease(videoImage);
-
- return uiImage;
- }
- @end
|