浏览代码

add iOS event channel

Caijinglong 6 年之前
父节点
当前提交
f55d2cfce5

+ 6 - 1
android/src/main/java/top/kikt/ijkplayer/Ijk.kt

@@ -23,6 +23,9 @@ class Ijk(private val registry: PluginRegistry.Registrar) : MethodChannel.Method
 
     private val methodChannel: MethodChannel = MethodChannel(registry.messenger(), "top.kikt/ijkplayer/$id")
 
+
+    private val eventChannel = MethodChannel(registry.messenger(), "top.kikt/ijkplayer/event/$id")
+
     init {
         mediaPlayer = TextureMediaPlayer(ijkPlayer)
         mediaPlayer.surfaceTexture = textureEntry.surfaceTexture()
@@ -95,7 +98,8 @@ class Ijk(private val registry: PluginRegistry.Registrar) : MethodChannel.Method
                 duration = duration.toDouble() / 1000,
                 currentPosition = currentPosition.toDouble() / 1000,
                 width = width,
-                height = height
+                height = height,
+                isPlaying = ijkPlayer.isPlaying
         )
     }
 
@@ -149,6 +153,7 @@ class Ijk(private val registry: PluginRegistry.Registrar) : MethodChannel.Method
     }
 
     fun dispose() {
+        methodChannel.setMethodCallHandler(null)
         mediaPlayer.stop()
         mediaPlayer.release()
         textureEntry.release()

+ 3 - 1
android/src/main/java/top/kikt/ijkplayer/entity/Info.kt

@@ -5,7 +5,8 @@ data class Info(
         val duration: Double,
         val currentPosition: Double,
         val width: Int,
-        val height: Int
+        val height: Int,
+        val isPlaying: Boolean
 ) {
 
     fun toMap(): Map<String, Any> {
@@ -14,6 +15,7 @@ data class Info(
         map["currentPosition"] = currentPosition
         map["width"] = width
         map["height"] = height
+        map["playing"] = isPlaying
         return map
     }
 

+ 7 - 1
example/lib/main.dart

@@ -1,3 +1,5 @@
+import 'dart:async';
+
 import 'package:flutter/material.dart';
 import 'package:flutter_ijkplayer/flutter_ijkplayer.dart';
 import 'package:photo/photo.dart';
@@ -144,9 +146,13 @@ class HomePageState extends State<HomePage> {
         await controller.setAssetDataSource("assets/sample1.mp4");
         controller.play();
 
-        Future.delayed(Duration(seconds: 2), () async {
+        Timer.periodic(Duration(seconds: 2), (timer) async {
           var info = await controller.getVideoInfo();
           print("info = $info");
+
+          if (info.progress >= 0.95) {
+            timer.cancel();
+          }
         });
       },
     );

+ 2 - 6
ios/Classes/FlutterIJK.h

@@ -4,6 +4,7 @@
 
 #import <Foundation/Foundation.h>
 #import <Flutter/Flutter.h>
+#import "KKIjkNotifyChannel.h"
 
 @interface FlutterIJK : NSObject
 
@@ -17,11 +18,6 @@
 
 - (void)dispose;
 
-- (void)play;
-
-- (void)pause;
-
-- (void)stop;
-
 - (void)setDataSourceWithUri:(NSString *)uri;
+
 @end

+ 19 - 12
ios/Classes/FlutterIJK.m

@@ -4,12 +4,13 @@
 
 #import "FlutterIJK.h"
 #import "KKVideoInfo.h"
+#import "KKIjkNotifyChannel.h"
 #import <IJKMediaFramework/IJKMediaFramework.h>
 #import <IJKMediaFramework/IJKMediaPlayer.h>
 #import <AVFoundation/AVFoundation.h>
 #import <libkern/OSAtomic.h>
 
-@interface FlutterIJK () <FlutterTexture>
+@interface FlutterIJK () <FlutterTexture, KKIjkNotifyDelegate>
 @end
 
 @implementation FlutterIJK {
@@ -19,7 +20,7 @@
     IJKFFMoviePlayerController *controller;
     CVPixelBufferRef latestPixelBuffer;
     FlutterMethodChannel *channel;
-    FlutterMethodCallHandler handler;
+    KKIjkNotifyChannel *notifyChannel;
 }
 
 - (instancetype)initWithRegistrar:(NSObject <FlutterPluginRegistrar> *)registrar {
@@ -38,6 +39,17 @@
     return self;
 }
 
+
+- (void)dispose {
+    [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 ([@"play" isEqualToString:call.method]) {
         [self play];
@@ -94,15 +106,6 @@
     return textureId;
 }
 
-- (void)dispose {
-    [[self.registrar textures]unregisterTexture:self.id];
-    [controller stop];
-    [controller shutdown];
-    controller = nil;
-    displayLink.paused = YES;
-    [displayLink invalidate];
-}
-
 - (void)play {
     [controller play];
 }
@@ -135,15 +138,18 @@
 
 - (void)prepare {
     [controller prepareToPlay];
-
     if (displayLink) {
         displayLink.paused = YES;
         [displayLink invalidate];
         displayLink = nil;
     }
+
     displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(onDisplayLink:)];
     [displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
     displayLink.paused = YES;
+
+    notifyChannel = [KKIjkNotifyChannel channelWithController:controller textureId:textureId registrar:self.registrar];
+    notifyChannel.infoDelegate = self;
 }
 
 - (IJKFFMoviePlayerController *)createControllerWithAssetName:(NSString *)assetName pkg:(NSString *)pkg {
@@ -200,6 +206,7 @@
     info.size = size;
     info.duration = duration;
     info.currentPosition = currentPlaybackTime;
+    info.isPlaying = [controller isPlaying];
 
     return info;
 }

+ 34 - 0
ios/Classes/KKIjkNotifyChannel.h

@@ -0,0 +1,34 @@
+//
+// Created by Caijinglong on 2019-03-15.
+//
+
+#import <Foundation/Foundation.h>
+#import <Flutter/Flutter.h>
+#import <IJKMediaFramework/IJKMediaFramework.h>
+
+@class KKVideoInfo;
+
+@protocol KKIjkNotifyDelegate
+- (KKVideoInfo *)getInfo;
+@end
+
+@interface KKIjkNotifyChannel : NSObject
+
+@property(nonatomic, strong) IJKFFMoviePlayerController *controller;
+
+@property(nonatomic, assign) int64_t textureId;
+
+@property(nonatomic, strong) NSObject <FlutterPluginRegistrar> *registrar;
+
+@property(nonatomic, weak) NSObject <KKIjkNotifyDelegate> *infoDelegate;
+
+- (instancetype)initWithController:(IJKFFMoviePlayerController *)controller textureId:(int64_t)textureId
+                         registrar:(NSObject <FlutterPluginRegistrar> *)registrar;
+
++ (instancetype)channelWithController:(IJKFFMoviePlayerController *)controller textureId:(int64_t)textureId
+                            registrar:(NSObject <FlutterPluginRegistrar> *)registrar;
+
+
+- (void)dispose;
+
+@end

+ 128 - 0
ios/Classes/KKIjkNotifyChannel.m

@@ -0,0 +1,128 @@
+//
+// Created by Caijinglong on 2019-03-15.
+//
+
+#import "KKIjkNotifyChannel.h"
+#import "KKVideoInfo.h"
+
+
+@implementation KKIjkNotifyChannel {
+    FlutterMethodChannel *channel;
+}
+
+- (instancetype)initWithController:(IJKFFMoviePlayerController *)controller textureId:(int64_t)textureId
+                         registrar:(NSObject <FlutterPluginRegistrar> *)registrar {
+    self = [super init];
+    if (self) {
+        self.controller = controller;
+        self.textureId = textureId;
+        self.registrar = registrar;
+        [self initial];
+    }
+
+    return self;
+}
+
++ (instancetype)channelWithController:(IJKFFMoviePlayerController *)controller textureId:(int64_t)textureId
+                            registrar:(NSObject <FlutterPluginRegistrar> *)registrar {
+    return [[self alloc] initWithController:controller textureId:textureId registrar:registrar];
+}
+
+
+- (void)initial {
+    NSString *channelName = [NSString stringWithFormat:@"top.kikt/ijkplayer/event/%lli", self.textureId];
+    channel = [FlutterMethodChannel methodChannelWithName:channelName binaryMessenger:[self.registrar messenger]];
+    [self registerObserver];
+}
+
+- (void)dispose {
+    channel = nil;
+    [self unregisterObservers];
+}
+
+
+- (void)registerObserver {
+    [[NSNotificationCenter defaultCenter] addObserver:self
+                                             selector:@selector(loadStateDidChange:)
+                                                 name:IJKMPMoviePlayerLoadStateDidChangeNotification
+                                               object:_controller];
+    [[NSNotificationCenter defaultCenter] addObserver:self
+                                             selector:@selector(moviePlayBackFinish:)
+                                                 name:IJKMPMoviePlayerPlaybackDidFinishNotification
+                                               object:_controller];
+
+    [[NSNotificationCenter defaultCenter] addObserver:self
+                                             selector:@selector(mediaIsPreparedToPlayDidChange:)
+                                                 name:IJKMPMediaPlaybackIsPreparedToPlayDidChangeNotification
+                                               object:_controller];
+
+    [[NSNotificationCenter defaultCenter] addObserver:self
+                                             selector:@selector(moviePlayBackStateDidChange:)
+                                                 name:IJKMPMoviePlayerPlaybackStateDidChangeNotification
+                                               object:_controller];
+}
+
+- (void)unregisterObservers {
+    [[NSNotificationCenter defaultCenter] removeObserver:self
+                                                    name:IJKMPMoviePlayerLoadStateDidChangeNotification
+                                                  object:_controller];
+    [[NSNotificationCenter defaultCenter] removeObserver:self
+                                                    name:IJKMPMoviePlayerPlaybackDidFinishNotification
+                                                  object:_controller];
+    [[NSNotificationCenter defaultCenter] removeObserver:self
+                                                    name:IJKMPMediaPlaybackIsPreparedToPlayDidChangeNotification
+                                                  object:_controller];
+    [[NSNotificationCenter defaultCenter] removeObserver:self
+                                                    name:IJKMPMoviePlayerPlaybackStateDidChangeNotification
+                                                  object:_controller];
+}
+
+- (NSDictionary *)getInfo {
+    return [[_infoDelegate getInfo] toMap];
+}
+
+- (void)moviePlayBackStateDidChange:(NSNotification *)notification {
+    [channel invokeMethod:@"playStateChange" arguments:[self getInfo]];
+}
+
+- (void)mediaIsPreparedToPlayDidChange:(NSNotification *)notification {
+    [channel invokeMethod:@"prepare" arguments:[self getInfo]];
+}
+
+- (void)moviePlayBackFinish:(NSNotification *)notification {
+    int reason = [[[notification userInfo] valueForKey:IJKMPMoviePlayerPlaybackDidFinishReasonUserInfoKey] intValue];
+    switch (reason) {
+        case IJKMPMovieFinishReasonPlaybackEnded:
+//            if ([_infoDelegate isLooping]) {
+//                [_controller setCurrentPlaybackTime:0];
+//                [_controller play];
+//            }
+
+            break;
+
+        case IJKMPMovieFinishReasonUserExited:
+            if (_eventSink) {
+                _eventSink(@{@"event": @"user quit"});
+            }
+            break;
+
+        case IJKMPMovieFinishReasonPlaybackError:
+            if (_eventSink) {
+                _eventSink([FlutterError
+                        errorWithCode:@"VideoError"
+                              message:@"Video finished with error"
+                              details:nil]);
+            }
+            break;
+
+        default:
+            break;
+    }
+//    [channel invokeMethod:@"playFinish" arguments:[self getInfo]];
+}
+
+- (void)loadStateDidChange:(NSNotification *)notification {
+//    [channel invokeMethod:@"loadStateChange" arguments:[self getInfo]];
+}
+
+@end

+ 2 - 0
ios/Classes/KKVideoInfo.h

@@ -13,6 +13,8 @@
 
 @property(nonatomic, assign) CGSize size;
 
+@property(nonatomic, assign) BOOL isPlaying;
+
 - (NSDictionary *)toMap;
 
 @end

+ 1 - 0
ios/Classes/KKVideoInfo.m

@@ -15,6 +15,7 @@
     dict[@"height"] = @((int) self.size.height);
     dict[@"duration"] = @(self.duration);
     dict[@"currentPosition"] = @(self.currentPosition);
+    dict[@"isPlaying"] = @(self.isPlaying);
     return dict;
 }
 

+ 7 - 0
lib/src/controller.dart

@@ -9,11 +9,15 @@ class IjkMediaController extends ChangeNotifier {
 
   bool get isInit => textureId == null;
 
+  IJKEventChannel eventChannel;
+
   Future<void> _initIjk() async {
     try {
       var id = await createIjk();
       this.textureId = id;
       _plugin = _IjkPlugin(id);
+      eventChannel = IJKEventChannel(this);
+      await eventChannel.init();
     } catch (e) {
       print(e);
       print("初始化失败");
@@ -24,6 +28,9 @@ class IjkMediaController extends ChangeNotifier {
     this.textureId = null;
     this.notifyListeners();
     _plugin?.dispose();
+    _plugin = null;
+    eventChannel?.dispose();
+    eventChannel = null;
     super.dispose();
   }
 

+ 36 - 0
lib/src/ijk_event_channel.dart

@@ -0,0 +1,36 @@
+import 'package:flutter/services.dart';
+
+import './ijkplayer.dart';
+
+class IJKEventChannel {
+  int get textureId => controller?.textureId;
+
+  IjkMediaController controller;
+
+  IJKEventChannel(this.controller);
+
+  MethodChannel channel;
+
+  String get channelName => "top.kikt/ijkplayer/event/$textureId";
+
+  Future<void> init() async {
+    channel = MethodChannel(channelName);
+    channel.setMethodCallHandler(handler);
+  }
+
+  void dispose() {
+    channel.setMethodCallHandler(null);
+    controller = null;
+  }
+
+  Future<dynamic> handler(MethodCall call) async {
+    switch (call.method) {
+      case "stateChange": // 对应状态变化
+        break;
+      default:
+        return MissingPluginException(
+          "$channelName ${call.method} not implement",
+        );
+    }
+  }
+}

+ 1 - 0
lib/src/ijkplayer.dart

@@ -3,6 +3,7 @@ import 'dart:io';
 
 import 'package:flutter/material.dart';
 import 'package:flutter/services.dart';
+import 'package:flutter_ijkplayer/src/ijk_event_channel.dart';
 import 'package:flutter_ijkplayer/src/video_info.dart';
 import './error.dart';
 

+ 2 - 0
lib/src/video_info.dart

@@ -5,6 +5,7 @@ class VideoInfo {
   int height;
   double duration;
   double currentPosition;
+  bool isPlaying;
 
   Map<String, dynamic> _map;
 
@@ -21,6 +22,7 @@ class VideoInfo {
     this.height = map["height"];
     this.duration = map["duration"];
     this.currentPosition = map["currentPosition"];
+    this.isPlaying = map["isPlaying"];
   }
 
   @override