Procházet zdrojové kódy

完成了初版的视频播放器

Caijinglong před 6 roky
rodič
revize
567d7acf87

+ 209 - 0
README-EN.md

@@ -0,0 +1,209 @@
+# ijkplayer
+
+ijkplayer for bilibili/ijkplayer, use flutter Texture widget.
+Read this README and refer to example/lib/main.dart before using it.
+The question of Android might not be able to run will be explained in detail.
+The simulator is out of use, so please use the real machine for debugging.
+
+- Android: I added the so Library of x86, but my voice decoding here is abnormal.
+- iOS: The simulator library is added, but it has sound and no pictures.
+
+Before using library, you can star and download the code to try the example.
+
+## Install
+
+[![pub package](https://img.shields.io/pub/v/oktoast.svg)](https://pub.dartlang.org/packages/oktoast)
+
+pubspec.yaml
+
+```yaml
+dependencies:
+  flutter_ijkplayer: ^0.1.0
+```
+
+## Build
+
+Compilation options for ijkplayer:
+https://github.com/CaiJingLong/flutter_ijkplayer_pod/blob/master/config/module.sh
+
+or see bilibili/ijkplayer or ffmepg
+
+### iOS
+
+Because the libraries of some iOS codes are large, a pod-dependent ijkplayer library for hosting iOS is created.
+The pod library is hosted in the GitHub repository at https://github.com/CaiJingLong/flutter_ijkplayer_pod
+Instead of tar.gz or zip, we use tar.xz to compress. This compression format has high compression rate, but slow compression and decompression speed. Considering the way of high compression rate, we should use high compression rate.
+If a friend is willing to provide CDN acceleration, you can contact me
+IOS code comes from iOS code in https://github.com/jadennn/flutter_ijk
+On this basis, rotation notification is added.
+
+### Android
+
+Unlike iOS, this is not modified, but rather a so library compiled with BiliBili version 0.8.8 + OpenSSL
+Errors may be reported or flipped out during construction, because of problems with so Libraries
+Vscode: You need to modify. vscode / launch. JSON
+Add `"args": ["--target-platform", "android-arm"]`
+Android studio: main.dart => Edit Configurations => Additional Arguments => `--target-platform android-arm`
+
+## Simple Example
+
+```dart
+import 'package:flutter_ijkplayer/flutter_ijkplayer.dart';
+
+
+class HomePageState extends State<HomePage> {
+  IjkMediaController controller = IjkMediaController();
+
+  @override
+  void initState() {
+    super.initState();
+  }
+
+  @override
+  void dispose() {
+    controller.dispose();
+    super.dispose();
+  }
+
+  @override
+  Widget build(BuildContext context) {
+    return Scaffold(
+      appBar: AppBar(
+        title: const Text('Plugin example app'),
+        actions: <Widget>[
+          IconButton(
+            icon: Icon(Icons.videocam),
+            onPressed: _pickVideo,
+          ),
+        ],
+      ),
+      body: Container(
+        // width: MediaQuery.of(context).size.width,
+        // height: 400,
+        child: ListView(
+          children: <Widget>[
+            buildIjkPlayer(),
+          ]
+        ),
+      ),
+      floatingActionButton: FloatingActionButton(
+        child: Icon(Icons.play_arrow),
+        onPressed: () async {
+          await controller.setNetworkDataSource(
+              'https://www.sample-videos.com/video123/mp4/720/big_buck_bunny_720p_20mb.mp4',
+              // 'rtmp://172.16.100.245/live1',
+              // 'https://www.sample-videos.com/video123/flv/720/big_buck_bunny_720p_10mb.flv',
+//              "https://www.sample-videos.com/video123/mp4/720/big_buck_bunny_720p_1mb.mp4",
+              // 'http://184.72.239.149/vod/smil:BigBuckBunny.smil/playlist.m3u8',
+              // "file:///sdcard/Download/Sample1.mp4",
+              autoPlay: true);
+          print("set data source success");
+          // controller.playOrPause();
+        },
+      ),
+    );
+  }
+
+  Widget buildIjkPlayer() {
+    return Container(
+      // height: 400, // 这里随意
+      child: IjkPlayer(
+        mediaController: controller,
+      ),
+    );
+  }
+}
+```
+
+## Usage
+
+### Usage of ijkplayer
+
+```dart
+IjkMediaController controller = IjkMediaController();
+```
+
+```dart
+  var ijkplayer = IjkPlayer(
+    mediaController: controller,
+  );
+```
+
+### Usage of controller
+
+#### DataSource
+
+```dart
+// network
+await controller.setNetworkDataSource("https://www.sample-videos.com/video123/mp4/720/big_buck_bunny_720p_20mb.mp4");
+
+// asset
+await controller.setAssetDataSource("assets/test.mp4");
+
+// file
+await controller.setFileDataSource(File("/sdcard/1.mp4"));
+
+// autoplay param
+await controller.setNetworkDataSource("https://www.sample-videos.com/video123/mp4/720/big_buck_bunny_720p_20mb.mp4",autoPlay : true);
+
+// or use play()
+await controller.setNetworkDataSource("https://www.sample-videos.com/video123/mp4/720/big_buck_bunny_720p_20mb.mp4");
+await controller.play();
+```
+
+#### control your media
+
+```dart
+// play or pause your media
+await controller.playOrPause();
+
+// play your media
+await controller.play();
+
+// pause your media
+await controller.pause();
+
+// stop
+// Here I want to explain that ijkplayer's stop releases resources, which makes play unusable and requires re-preparation of resources. So, in fact, this is to go back to the progress bar and pause.
+await controller.stop();
+
+// seek progress to
+await controller.seekTo(0); // double value , such as : 1.1 = 1s100ms, 60 = 1min
+```
+
+#### get media info
+
+```dart
+  // have some properties, width , height , duration
+  VideoInfo info = await controller.getVideoInfo();
+```
+
+#### Observer for resource
+
+Broadcasting changes in information outward in the form of streams, in principle the attributes ending with streams are monitorable.
+
+```dart
+// Callback when texture ID changes
+Stream<int> textureIdStream = controller.textureIdStream;
+
+// Play status monitoring, true is playing, false is pausing
+Stream<bool> playingStream = controller.playingStream;
+
+// When controller. refreshVideoInfo () is called, this method calls back, usually for the customization of the controller UI, so as to monitor the current information (playback progress, playback status, width, height, direction change, etc.).
+Stream<VideoInfo> videoInfoStream = controller.videoInfoStream;
+
+// Volume change, which should be noted here, refers to the volume change of the current media, not the volume change of the system.
+Stream<bool> volumeStream = controller.playingStream;
+```
+
+#### release resource
+
+```dart
+await controller.reset(); // When this method is called, all native resources are released, but the dataSource is still available for resetting.
+
+await controller.dispose(); // After this method call, the current controller is theoretically no longer available, resetting dataSource is invalid and may throw an exception.
+```
+
+## LICENSE
+
+MIT

+ 206 - 31
README.md

@@ -2,77 +2,252 @@
 
 ijkplayer,通过纹理的方式接入 bilibili/ijkplayer
 
-使用前请完整阅读本README并参阅 example/lib/main.dart
+使用前请完整阅读本 README 并参阅 example/lib/main.dart
 
-有关android跑不起来的问题会详细解释
+有关 android 可能跑不起来的问题会详细解释
 
-## About English Readme
+模拟器用不了,所以调试请使用真机
 
-At present, it is a preview version, which can be easily used without providing English documents for the time being.
-English documents will be provided after the main objectives have been completed.
+- android: 我加入了 x86 的 so 库,但是我这里声音解码异常
+- iOS: 加入了模拟器的库,但是有声音,无图片
 
-待主要目标完成后再提供英文文档
+在正式使用前,可以先 star 一下, download 代码跑一下 example 尝试
+
+## English Readme
+
+https://github.com/CaiJingLong/flutter_ijkplayer/blob/master/README-EN.md
 
 ## Install
 
-pubspec.yaml  
+pubspec.yaml
 
 ```yaml
 dependencies:
-  flutter_ijkplayer: ^0.0.1 
+  flutter_ijkplayer: ^0.1.0
 ```
 
 ## Build
 
-编译规则可以参考这个,如果你有自己的特定需求,可以修改编译选项,这个参考bilibili/ijkplayer或ffmpeg
+编译规则可以参考这个,如果你有自己的特定需求,可以修改编译选项,这个参考 bilibili/ijkplayer  ffmpeg
 
 https://github.com/CaiJingLong/flutter_ijkplayer_pod/blob/master/config/module.sh
 
 ### iOS
 
-因为iOS部分代码的库文件比较大,所以创建了一个pod依赖托管iOS的ijkplayer库  
-pod库托管在github 仓库内 https://github.com/CaiJingLong/flutter_ijkplayer_pod  
-没有采用通用的tar.gz或zip,而是使用tar.xz的方式压缩,这个压缩格式压缩率高,但是压缩和解压缩的的速度慢,综合考虑使用高压缩率的方式  
-如果有朋友愿意提供全球cdn加速,可以联系我😁
-  
-iOS的代码来自于 https://github.com/jadennn/flutter_ijk 中的iOS代码  
+因为 iOS 部分代码的库文件比较大,所以创建了一个 pod 依赖托管 iOS  ijkplayer 
+pod 库托管在 github 仓库内 https://github.com/CaiJingLong/flutter_ijkplayer_pod  
+没有采用通用的 tar.gz  zip,而是使用 tar.xz 的方式压缩,这个压缩格式压缩率高,但是压缩和解压缩的的速度慢,综合考虑使用高压缩率的方式  
+如果有朋友愿意提供 cdn 加速,可以联系我 😁
+
+iOS 的代码来自于 https://github.com/jadennn/flutter_ijk 中的 iOS 代码
 在这基础上增加了旋转通知
 
 ### Android
 
-和iOS不同,这个没有修改,而是使用bilibili的0.8.8版+openssl编译的so库
+和 iOS 不同,这个没有修改,而是使用 bilibili  0.8.8 版+openssl 编译的 so 
 
-构建时可能会报错,或者闪退,这个是因为so库的问题
+构建时可能会报错,或者闪退,这个是因为 so 库的问题
 
 vscode: 你需要修改.vscode/launch.json
 添加 `"args": ["--target-platform", "android-arm"]`
 
-android studio: 你需要点击run左边那个main.dart=>Edit Configurations,然后在Additional Arguments中添加 `--target-platform android-arm`
+android studio: 你需要点击 run 左边那个 main.dart=>Edit Configurations,然后在 Additional Arguments 中添加 `--target-platform android-arm`
+
+打包时同理,尽量只保留 armv7 就可以了
+
+## Simple Example
+
+```dart
+import 'package:flutter_ijkplayer/flutter_ijkplayer.dart';
+
+
+class HomePageState extends State<HomePage> {
+  IjkMediaController controller = IjkMediaController();
+
+  @override
+  void initState() {
+    super.initState();
+  }
+
+  @override
+  void dispose() {
+    controller.dispose();
+    super.dispose();
+  }
+
+  @override
+  Widget build(BuildContext context) {
+    return Scaffold(
+      appBar: AppBar(
+        title: const Text('Plugin example app'),
+        actions: <Widget>[
+          IconButton(
+            icon: Icon(Icons.videocam),
+            onPressed: _pickVideo,
+          ),
+        ],
+      ),
+      body: Container(
+        // width: MediaQuery.of(context).size.width,
+        // height: 400,
+        child: ListView(
+          children: <Widget>[
+            buildIjkPlayer(),
+          ]
+        ),
+      ),
+      floatingActionButton: FloatingActionButton(
+        child: Icon(Icons.play_arrow),
+        onPressed: () async {
+          await controller.setNetworkDataSource(
+              'https://www.sample-videos.com/video123/mp4/720/big_buck_bunny_720p_20mb.mp4',
+              // 'rtmp://172.16.100.245/live1',
+              // 'https://www.sample-videos.com/video123/flv/720/big_buck_bunny_720p_10mb.flv',
+//              "https://www.sample-videos.com/video123/mp4/720/big_buck_bunny_720p_1mb.mp4",
+              // 'http://184.72.239.149/vod/smil:BigBuckBunny.smil/playlist.m3u8',
+              // "file:///sdcard/Download/Sample1.mp4",
+              autoPlay: true);
+          print("set data source success");
+          // controller.playOrPause();
+        },
+      ),
+    );
+  }
+
+  Widget buildIjkPlayer() {
+    return Container(
+      // height: 400, // 这里随意
+      child: IjkPlayer(
+        mediaController: controller,
+      ),
+    );
+  }
+}
+```
+
+## Usage
+
+### 设置
+
+每个 ijkplayer 对应一个 IjkMediaController;
+
+```dart
+IjkMediaController controller = IjkMediaController();
+```
+
+将 controller 设置给 ijkplayer
+
+```dart
+  var ijkplayer = IjkPlayer(
+    mediaController: controller,
+  );
+```
+
+### 控制器的使用
+
+#### 设置资源
+
+```dart
+// 根据你的资源类型设置,设置资源本身是耗时操作,建议await
+
+// 网络
+await controller.setNetworkDataSource("https://www.sample-videos.com/video123/mp4/720/big_buck_bunny_720p_20mb.mp4");
+
+// 应用内资源
+await controller.setAssetDataSource("assets/test.mp4");
+
+// 文件
+await controller.setFileDataSource(File("/sdcard/1.mp4"));
+
+// 还可以添加autoplay参数,这样会在资源准备完成后自动播放
+await controller.setNetworkDataSource("https://www.sample-videos.com/video123/mp4/720/big_buck_bunny_720p_20mb.mp4",autoPlay : true);
+
+//或者也可以在设置资源完毕后自己调用play
+await controller.setNetworkDataSource("https://www.sample-videos.com/video123/mp4/720/big_buck_bunny_720p_20mb.mp4");
+await controller.play();
+```
 
-打包时同理,尽量只保留armv7就可以了
+#### 播放的控制
 
-如果你使用模拟器开发,就把参数替换为android-x86,目标机型参阅`flutter run -h` `--target-platform`内的介绍
+```dart
+// 播放或暂停
+await controller.playOrPause();
+
+// 不管当前状态,直接play
+await controller.play();
+
+// 不管当前状态,直接pause
+await controller.pause();
+
+// 停止
+// 这里要说明,ijkplayer的stop会释放资源,导致play不能使用,需要重新准备资源,所以这里其实采用的是回到进度条开始,并暂停
+await controller.stop();
+
+// 进度跳转
+await controller.seekTo(0); //这里是一个double值, 单位是秒, 如1秒100毫秒=1.1s 1分钟=60.0
+```
+
+#### 获取播放信息
+
+```dart
+  // 包含了一些信息,是否在播放,视频宽,高,视频角度,当前播放进度,总长度等信息
+  VideoInfo info = await controller.getVideoInfo();
+```
+
+#### 资源监听
+
+使用 stream 的形式向外广播一些信息的变化,原则上以 stream 结尾的属性都是可监听的
+
+```dart
+// 当纹理id发生变化时的回调,这个对于用户不敏感
+Stream<int> textureIdStream = controller.textureIdStream;
+
+// 播放状态的监听,true为正在播放,false为暂停
+Stream<bool> playingStream = controller.playingStream;
+
+// 当有调用controller.refreshVideoInfo()时,这个方法会回调,一般用于controllerUI的自定义,以便于监听当前信息(播放进度,播放状态,宽,高,方向变化等)
+Stream<VideoInfo> videoInfoStream = controller.videoInfoStream;
+
+// 音量的变化,这里需要注意,这个变化指的是当前媒体的音量变化,而不是系统的音量变化
+Stream<bool> volumeStream = controller.playingStream;
+```
+
+#### 释放资源
+
+```dart
+await controller.reset(); // 这个方法调用后,会释放所有原生资源,但重新设置dataSource依然可用
+
+await controller.dispose(); //这个方法调用后,当前控制器理论上不再可用,重新设置dataSource无效
+```
 
 ## Progress
 
-目前正处于初始开发阶段,可能有各种问题,欢迎提出
+目前正处于初始开发阶段,可能有各种问题,欢迎提出,但不一定会实现,也不一定会修改 😌
 
-最初准备参考官方video_player的api方式进行开发,但是觉得调用的方式比较奇怪
+最初准备参考官方 video_player  api 方式进行开发,但是觉得调用的方式比较奇怪
 
-需要自定义LifeCycle进行管理,而且自定义控制器不太方便,遂决定重写api的代码结构
+需要自定义 LifeCycle 进行管理,而且自定义控制器不太方便,遂决定重写 api 的代码结构,同时清晰逻辑
 
 ## TodoList
 
-- [X] 控制器逻辑
-
-- [ ] 默认控制器UI
+- [x] 控制器逻辑
+- [x] 默认控制器 UI
+  - [x] 进度条
+  - [x] 播放/暂停按钮
+  - [x] 横向滑动进度
+  - [x] 纵向滑动音量
+  - [x] 单击显示/隐藏界面
+  - [x] 双击播放/暂停
+  - [ ] 拖动进度条快速调节进度
+  - [ ] 使用选项切换音量的控制是系统音量还是资源音量
+- [x] 根据视频角度自动旋转
+- [x] 保证图片宽高比不失真
+- [x] 允许自定义控制器 UI
 
-  - [ ] 纵向
-  - [ ] 横向
+初版为预览版,不保证质量,欢迎试用
 
-初版为预览版,不保证质量
-UI
+UI 控制功能包含常见的播放停止,手势拖动
 
 ## LICENSE
 
-MIT
+MIT

+ 1 - 1
example/pubspec.lock

@@ -47,7 +47,7 @@ packages:
       path: ".."
       relative: true
     source: path
-    version: "0.0.1"
+    version: "0.1.0"
   flutter_test:
     dependency: "direct dev"
     description: flutter

+ 2 - 0
lib/src/controller.dart

@@ -2,6 +2,8 @@ part of './ijkplayer.dart';
 
 /// Media Controller
 class IjkMediaController {
+
+  /// MediaController
   IjkMediaController({
     this.autoRotate = true,
   });

+ 5 - 0
lib/src/ijkplayer.dart

@@ -14,13 +14,16 @@ part './controller.dart';
 
 part './manager.dart';
 
+/// Using mediaController to Construct a Controller UI
 typedef Widget ControllerWidgetBuilder(IjkMediaController controller);
 
+/// Main Classes of Library
 class IjkPlayer extends StatefulWidget {
   final IjkMediaController mediaController;
   final ControllerWidgetBuilder controllerWidgetBuilder;
   final PlayerBuilder playerBuilder;
 
+  /// Main Classes of Library
   const IjkPlayer({
     Key key,
     this.mediaController,
@@ -32,7 +35,9 @@ class IjkPlayer extends StatefulWidget {
   IjkPlayerState createState() => IjkPlayerState();
 }
 
+/// state of ijkplayer
 class IjkPlayerState extends State<IjkPlayer> {
+  /// see [IjkMediaController]
   IjkMediaController controller;
 
   @override

+ 2 - 0
lib/src/manager.dart

@@ -13,12 +13,14 @@ class IjkManager {
     _globalChannel.invokeMethod("init");
   }
 
+  /// set system volume
   static Future<void> setSystemVolume(int volume) async {
     await _globalChannel.invokeMethod("setSystemVolume", {
       "volume": volume,
     });
   }
 
+  /// get system volume
   static Future<int> getSystemVolume() async {
     return _globalChannel.invokeMethod("getSystemVolume");
   }

+ 15 - 2
lib/src/video_info.dart

@@ -1,21 +1,33 @@
 import 'dart:convert';
 
 class VideoInfo {
+  /// Width of Video
   int width;
+
+  /// Height of Video
   int height;
+
+  /// Total length of video
   double duration;
+
+  /// Current playback progress
   double currentPosition;
+
+  /// In play
   bool isPlaying;
+
+  /// Degree of Video
   int degree;
 
   Map<String, dynamic> _map;
 
-  double get radio => width / height;
-
+  /// Percentage playback progress
   double get progress => (currentPosition ?? 0) / (duration ?? 1);
 
+  ///Is there any information?
   bool get hasData => _map != null;
 
+  /// Aspect ratio
   double get ratio {
     double r;
     if (width != null && height != null) {
@@ -27,6 +39,7 @@ class VideoInfo {
     return r;
   }
 
+  /// Constructing from the native method
   VideoInfo.fromMap(Map<String, dynamic> map) {
     if (map == null) {
       return;

+ 20 - 7
lib/src/widget/ijkplayer_builder.dart

@@ -1,29 +1,29 @@
 import 'package:flutter/material.dart';
 import 'package:flutter_ijkplayer/flutter_ijkplayer.dart';
 
+/// Player builder, Inheritance of this class allows you to implement your own player
 typedef Widget PlayerBuilder(
   BuildContext context,
   IjkMediaController controller,
   VideoInfo info,
 );
 
+/// default IJKPlayer method
 Widget buildDefaultIjkPlayer(
   BuildContext context,
   IjkMediaController controller,
   VideoInfo info,
 ) {
-  int degree = info?.degree ?? 0;
   double ratio = info?.ratio ?? 1280 / 720;
 
-  if (ratio == 0) {
-    ratio = 1280 / 720;
-  }
-
   var id = controller.textureId;
 
   if (id == null) {
-    return Container(
-      color: Colors.black,
+    return AspectRatio(
+      aspectRatio: ratio,
+      child: Container(
+        color: Colors.black,
+      ),
     );
   }
 
@@ -33,6 +33,19 @@ Widget buildDefaultIjkPlayer(
     ),
   );
 
+  if (!controller.autoRotate) {
+    return AspectRatio(
+      aspectRatio: null,
+      child: w,
+    );
+  }
+
+  int degree = info?.degree ?? 0;
+
+  if (ratio == 0) {
+    ratio = 1280 / 720;
+  }
+
   w = AspectRatio(
     aspectRatio: ratio,
     child: w,

+ 4 - 4
pubspec.yaml

@@ -1,8 +1,8 @@
 name: flutter_ijkplayer
-description: A new flutter plugin project.
-version: 0.0.1
-author:
-homepage:
+description: Flutter version of bilibilibili ijkplayer, supports common playback protocols, easy to use.
+version: 0.1.0
+author: caijinglong<cjl_spy@163.com>
+homepage: https://github.com/CaiJingLong/flutter_ijkplayer
 
 environment:
   sdk: ">=2.1.0 <3.0.0"