Sfoglia il codice sorgente

错误状态的回调和状态改变

Caijinglong 6 anni fa
parent
commit
b1159cf18a

+ 7 - 4
TODOLIST.md

@@ -11,7 +11,7 @@
   - [x] 停止
   - [x] 释放资源
   - [x] 控制音量
-  - [ ] 监听播放完成
+  - [x] 监听播放完成
   - [x] 控制系统音量
     - [x] 修复 iOS 系统音量与逻辑相反的问题
     - [x] 修复 iOS 系统音量出现系统弹窗的问题
@@ -40,6 +40,7 @@
   - [ ] 悬浮窗中播放
     - [ ] 悬浮窗的 UI 控制器
     - [ ] 自定义 UI
+  - [x] 视频播放出错时的回调通知
 - [x] 默认控制器 UI
   - [x] 进度条
   - [x] 播放/暂停按钮
@@ -59,6 +60,8 @@
     - [x] iOS
     - [x] 利用 ShowDialog 开发一个全屏的播放界面,不仅仅在 Example 中
       - [x] 根据屏幕宽高确定是横屏全屏还是竖屏全屏(比如:常规电影是横屏,常规自拍类视频为竖屏)
+    - [ ] 全屏的界面也接受自定义 UI 的参数
+  - [ ] 网络视频发生缓冲的情况,应在 UI 上有所反馈
 - [x] 根据视频角度自动旋转
 - [x] 保证视频图像宽高比不失真
 - [x] 允许自定义控制器 UI
@@ -76,6 +79,6 @@
   - [x] 在悬浮窗中播放
     - [x] 悬浮窗的 UI 控制器
 - [x] iOS 部分视频无法显示图像的问题: 可能很长时间内都无法解决
-- [ ] 支持在 dart 端初始化 ijkPlayer 播放器的 option
-  - [ ] android
-  - [ ] iOS
+- [x] 支持在 dart 端初始化 ijkPlayer 播放器的 option
+  - [x] android
+  - [x] iOS

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

@@ -36,7 +36,7 @@ class NotifyChannel(val registry: PluginRegistry.Registrar, val textureId: Long,
         }
         player.setOnErrorListener { mp, what, extra ->
             channel.invokeMethod("error", what)
-            logi("onError $what")
+            logi("onError $what , extra = $extra")
             false
         }
         player.setOnInfoListener { mp, what, extra ->
@@ -60,15 +60,10 @@ class NotifyChannel(val registry: PluginRegistry.Registrar, val textureId: Long,
             logi("onController message $it, isPlaying = ${player.isPlaying}")
             ""
         }
-
     }
 
     fun dispose() {
         player.resetListeners()
     }
 
-}
-
-class Temp {
-    fun invokeMethod(str: String, any: Any?) {}
 }

+ 1 - 1
example/ios/Podfile.lock

@@ -26,7 +26,7 @@ EXTERNAL SOURCES:
 
 SPEC CHECKSUMS:
   Flutter: 58dd7d1b27887414a370fcccb9e645c08ffd7a6a
-  flutter_ijkplayer: e7cd28f617bf87bad4a11aaaa13cfc59f18c6c49
+  flutter_ijkplayer: c3a5d569dde01d23b33bf0cfc685a256a10e56ce
   FlutterIJK: 9e3ccb3d74cca7ebe77db65d30a19b630ea8648e
   photo_manager: d47ddf6cb25cbfa837dc334540eb9a99b208e191
 

+ 3 - 0
example/lib/i18n/cn.dart

@@ -68,4 +68,7 @@ class _I18nZh extends I18n {
 
   @override
   String get customOption => "自定义Option的使用";
+
+  @override
+  String get errorUrl => "错误的url";
 }

+ 3 - 0
example/lib/i18n/en.dart

@@ -68,4 +68,7 @@ class _I18nEn extends I18n {
 
   @override
   String get customOption => "Usage of custom IjkPlayer options";
+
+  @override
+  String get errorUrl => "Error Url";
 }

+ 2 - 0
example/lib/i18n/i18n.dart

@@ -48,6 +48,8 @@ abstract class I18n {
   String get ijkStatusTitle;
 
   String get customOption;
+
+  String get errorUrl;
 }
 
 I18n get currentI18n => I18n(window.locale);

+ 78 - 0
example/lib/page/error_url.dart

@@ -0,0 +1,78 @@
+import 'dart:async';
+
+import 'package:flutter/material.dart';
+import 'package:flutter_ijkplayer/flutter_ijkplayer.dart';
+import 'package:ijkplayer_example/i18n/i18n.dart';
+
+class ErrorUrlPage extends StatefulWidget {
+  @override
+  _ErrorUrlPageState createState() => _ErrorUrlPageState();
+}
+
+class _ErrorUrlPageState extends State<ErrorUrlPage> {
+  TextEditingController editingController = TextEditingController();
+  IjkMediaController mediaController = IjkMediaController();
+  StreamSubscription statusSub;
+  StreamSubscription ijkErrorSub;
+  @override
+  void initState() {
+    super.initState();
+
+    editingController.text =
+        "https://js.wshls.acgvideo.com/live-js/922199/live_8747041_1741679.m3u8?wsSecret=1337e20698b1673ac73ea8f35e2d60e8&wsTime=1556966389&trid=5afe0383d7d149dabe0c0327c2e53a75&order=1&sig=no";
+
+    statusSub = mediaController.ijkStatusStream.listen((status) {
+      print("status = $status");
+    });
+
+    ijkErrorSub = mediaController.ijkErrorStream.listen((error) {
+      print("error = $error");
+    });
+  }
+
+  @override
+  void dispose() {
+    statusSub?.cancel();
+    ijkErrorSub?.cancel();
+    editingController.dispose();
+    mediaController.dispose();
+    super.dispose();
+  }
+
+  @override
+  Widget build(BuildContext context) {
+    return Scaffold(
+      appBar: AppBar(
+        title: Text(currentI18n.networkButton),
+      ),
+      body: ListView(
+        children: <Widget>[
+          Row(
+            children: <Widget>[
+              Expanded(
+                child: TextField(
+                  controller: editingController,
+                ),
+              ),
+              FlatButton(
+                child: Text(currentI18n.play),
+                onPressed: _playInput,
+              ),
+            ],
+          ),
+          Container(
+            height: 400,
+            child: IjkPlayer(
+              mediaController: mediaController,
+            ),
+          ),
+        ],
+      ),
+    );
+  }
+
+  void _playInput() async {
+    var text = editingController.text;
+    await mediaController.setNetworkDataSource(text, autoPlay: true);
+  }
+}

+ 3 - 1
example/lib/page/index.dart

@@ -2,8 +2,9 @@ import 'package:flutter/material.dart';
 import 'package:ijkplayer_example/page/paging_page.dart';
 import 'package:ijkplayer_example/page/screen_shot_page.dart';
 import 'package:ijkplayer_example/page/video_list.dart';
-import '../i18n/i18n.dart';
 
+import '../i18n/i18n.dart';
+import 'error_url.dart';
 import 'asset_page.dart';
 import 'controller_stream_use.dart';
 import 'custom_ijk_opt_page.dart';
@@ -41,6 +42,7 @@ class _IndexPageState extends State<IndexPage> {
           buildButton(currentI18n.overlayPageTitle, InOverlayPage()),
           buildButton(currentI18n.ijkStatusTitle, IjkStatusPage()),
           buildButton(currentI18n.customOption, CustomIjkOptionPage()),
+          buildButton(currentI18n.errorUrl, ErrorUrlPage()),
         ],
       ),
     );

+ 19 - 15
ios/Classes/CoolIjkNotifyChannel.m

@@ -99,20 +99,24 @@
 
 
 - (void)moviePlayBackFinish:(NSNotification *)notification {
-//    int reason = [[[notification userInfo] valueForKey:IJKMPMoviePlayerPlaybackDidFinishReasonUserInfoKey] intValue];
-//    int type = 2;
-//    switch (reason) {
-//        case IJKMPMovieFinishReasonPlaybackEnded:
-//            type = 0;
-//            break;
-//        case IJKMPMovieFinishReasonUserExited:
-//            type = 1;
-//            break;
-//        case IJKMPMovieFinishReasonPlaybackError:
-//            break;
-//        default:
-//            break;
-//    }
+    int reason = [[[notification userInfo] valueForKey:IJKMPMoviePlayerPlaybackDidFinishReasonUserInfoKey] intValue];
+    int type = 2;
+    switch (reason) {
+        case IJKMPMovieFinishReasonPlaybackEnded:
+            type = 0;
+            break;
+        case IJKMPMovieFinishReasonUserExited:
+            type = 1;
+            break;
+        case IJKMPMovieFinishReasonPlaybackError:{
+            int errValue = [[[notification userInfo] valueForKey:@"error"] intValue];
+            [channel invokeMethod:@"error" arguments:@(errValue)];
+            return;
+        }
+        default:
+            break;
+    }
+    NSLog(@"type = %d", type);
 //    [channel invokeMethod:@"finish" arguments:@{@"type": @(type)}];
     [channel invokeMethod:@"finish" arguments:[self getInfo]];
 }
@@ -127,4 +131,4 @@
     [channel invokeMethod:@"rotateChanged" arguments:[self getInfo]];
 }
 
-@end
+@end

+ 12 - 1
lib/src/controller/controller.dart

@@ -38,6 +38,7 @@ class IjkMediaController
       await eventChannel.init();
       volume = 100;
     } catch (e) {
+      await reset();
       LogUtils.warning(e);
       LogUtils.warning("初始化失败");
     }
@@ -151,7 +152,11 @@ class IjkMediaController
     }
     await _initIjk();
     Future playFuture = _autoPlay(autoPlay);
-    await setDataSource();
+    try {
+      await setDataSource();
+    } on Exception catch (e) {
+      print("init data error is ${e.toString()}");
+    }
     return playFuture;
   }
 
@@ -278,6 +283,12 @@ class IjkMediaController
     _ijkStatus = IjkStatus.complete;
   }
 
+  void _onError(int errorValueInt) async {
+    _playFinishController?.add(this);
+    _ijkStatus = IjkStatus.error;
+    _ijkErrorController?.add(errorValueInt);
+  }
+
   /// Intercept the video frame image and get the `Uint8List` format.
   ///
   /// Player UI is not included. If you need the effect of the player, use the screenshot of the system.

+ 11 - 0
lib/src/controller/ijk_event_channel.dart

@@ -39,6 +39,12 @@ class _IJKEventChannel {
       case "rotateChanged":
         onRotateChanged(call);
         break;
+      case "error":
+        var info = await controller.getVideoInfo();
+        _onPlayFinish(info);
+        int errorValue = call.arguments;
+        _onPlayError(errorValue);
+        break;
       default:
         return MissingPluginException(
           "$channelName ${call.method} not implement",
@@ -93,6 +99,11 @@ class _IJKEventChannel {
     var info = getInfo(call);
     LogUtils.debug("onRotateChanged , info = $info");
   }
+
+  void _onPlayError(int errorValue) {
+    LogUtils.warning("play error , errorValue : $errorValue");
+    controller._onError(errorValue);
+  }
 }
 
 // enum FinishType {

+ 66 - 0
lib/src/controller/ijkplayer_controller_mixin.dart

@@ -127,6 +127,70 @@ mixin IjkMediaControllerStreamMixin {
   /// On play finish
   Stream<IjkStatus> get ijkStatusStream => _ijkStatusController.stream;
 
+  /// errorStream
+  StreamController<int> _ijkErrorController = StreamController.broadcast();
+
+  /// # On Error stream
+  ///
+  /// In iOS, this value is zero for a lot of time.
+  ///
+  /// see [bilibili-doc](https://github.com/bilibili/ijkplayer/blob/cced91e3ae3730f5c63f3605b00d25eafcf5b97b/ios/IJKMediaPlayer/IJKMediaPlayer/IJKFFMoviePlayerController.m#L1025-L1041)
+  ///
+  /// ----
+  ///
+  /// In android, see next document.
+  ///
+  /// Code value see [bilibili-doc](https://github.com/bilibili/ijkplayer/blob/cced91e3ae3730f5c63f3605b00d25eafcf5b97b/ijkmedia/ijkplayer/android/ijkplayer_android_def.h#L48-L83)
+  ///
+  /// ## Next is the part code
+  ///
+  /// Generic error codes for the media player framework.  Errors are fatal, the
+  /// playback must abort.
+  ///
+  /// Errors are communicated back to the client using the
+  /// MediaPlayerListener::notify method defined below.
+  ///
+  /// ### In this situation, 'notify' is invoked with the following:
+  ///
+  ///  - 'msg' is set to MEDIA_ERROR.
+  ///  - 'ext1' should be a value from the enum media_error_type.
+  ///  - 'ext2' contains an implementation dependant error code to provide
+  ///          more details. Should default to 0 when not used.
+  ///
+  /// ### The codes are distributed as follow:
+  ///
+  ///  - 0xx: Reserved
+  ///  - 1xx: Android Player errors. Something went wrong inside the MediaPlayer.
+  ///  - 2xx: Media errors (e.g Codec not supported). There is a problem with the
+  ///        media itself.
+  ///  - 3xx: Runtime errors. Some extraordinary condition arose making the playback
+  ///        impossible.
+  ///
+  /// ```c
+  ///
+  /// enum media_error_type {
+  ///    // 0xx
+  ///    MEDIA_ERROR_UNKNOWN = 1,
+  ///    // 1xx
+  ///    MEDIA_ERROR_SERVER_DIED = 100,
+  ///    // 2xx
+  ///    MEDIA_ERROR_NOT_VALID_FOR_PROGRESSIVE_PLAYBACK = 200,
+  ///    // 3xx
+  ///
+  ///
+  ///    // -xx
+  ///    MEDIA_ERROR_IO          = -1004,
+  ///    MEDIA_ERROR_MALFORMED   = -1007,
+  ///    MEDIA_ERROR_UNSUPPORTED = -1010,
+  ///    MEDIA_ERROR_TIMED_OUT   = -110,
+  ///
+  ///
+  ///    MEDIA_ERROR_IJK_PLAYER  = -10000,
+  /// };
+  ///
+  /// ```
+  Stream<int> get ijkErrorStream => _ijkErrorController.stream;
+
   void _setVolume(int value);
 
   Future<void> _disposeStream() async {
@@ -137,6 +201,7 @@ mixin IjkMediaControllerStreamMixin {
     _volumeController?.close();
     _playFinishController?.close();
     _ijkStatusController?.close();
+    _ijkErrorController?.close();
 
     _playingController = null;
     _videoInfoController = null;
@@ -144,5 +209,6 @@ mixin IjkMediaControllerStreamMixin {
     _volumeController = null;
     _playFinishController = null;
     _ijkStatusController = null;
+    _ijkErrorController = null;
   }
 }

+ 9 - 3
lib/src/ijkplayer.dart

@@ -5,21 +5,27 @@ import 'dart:typed_data';
 import 'package:flutter/material.dart';
 import 'package:flutter/services.dart';
 
-import 'entity/video_info.dart';
-import 'entity/options.dart';
 import 'engine/ijk_controller_manager.dart';
+import 'entity/options.dart';
+import 'entity/video_info.dart';
 import 'error.dart';
 import 'helper/logutil.dart';
 import 'widget/controller_widget_builder.dart';
-import 'widget/ijkplayer_builder.dart';
 import 'widget/ijk_status_widget.dart';
+import 'widget/ijkplayer_builder.dart';
 
 part 'controller/controller.dart';
+
 part 'controller/datasoure.dart';
+
 part 'controller/enums.dart';
+
 part 'controller/ijk_event_channel.dart';
+
 part 'controller/ijkplayer_controller_mixin.dart';
+
 part 'controller/plugin.dart';
+
 part 'engine/manager.dart';
 
 /// Main Classes of Library