├── .gitignore
├── .idea
├── .gitignore
├── libraries
│ ├── Dart_SDK.xml
│ └── Flutter_Plugins.xml
├── misc.xml
├── modules.xml
├── runConfigurations
│ └── example_lib_main_dart.xml
└── vcs.xml
├── .metadata
├── CHANGELOG.md
├── LICENSE
├── README.md
├── analysis_options.yaml
├── android
├── .gitignore
├── build.gradle
├── gradle.properties
├── gradle
│ └── wrapper
│ │ └── gradle-wrapper.properties
├── settings.gradle
└── src
│ └── main
│ ├── AndroidManifest.xml
│ └── kotlin
│ └── icu
│ └── bughub
│ └── plugins
│ └── video_player
│ └── flt_video_player
│ ├── Constants.kt
│ ├── FltBasePlayer.kt
│ ├── FltVideoPlayerPlugin.kt
│ ├── FltVideoView.kt
│ ├── FltVodPlayer.kt
│ └── PlayerEventSink.kt
├── example
├── .gitignore
├── .metadata
├── README.md
├── analysis_options.yaml
├── android
│ ├── .gitignore
│ ├── app
│ │ ├── build.gradle
│ │ └── src
│ │ │ ├── debug
│ │ │ └── AndroidManifest.xml
│ │ │ ├── main
│ │ │ ├── AndroidManifest.xml
│ │ │ ├── kotlin
│ │ │ │ └── icu
│ │ │ │ │ └── bughub
│ │ │ │ │ └── plugins
│ │ │ │ │ └── video_player
│ │ │ │ │ └── flt_video_player_example
│ │ │ │ │ └── MainActivity.kt
│ │ │ └── res
│ │ │ │ ├── drawable-v21
│ │ │ │ └── launch_background.xml
│ │ │ │ ├── drawable
│ │ │ │ └── launch_background.xml
│ │ │ │ ├── mipmap-hdpi
│ │ │ │ └── ic_launcher.png
│ │ │ │ ├── mipmap-mdpi
│ │ │ │ └── ic_launcher.png
│ │ │ │ ├── mipmap-xhdpi
│ │ │ │ └── ic_launcher.png
│ │ │ │ ├── mipmap-xxhdpi
│ │ │ │ └── ic_launcher.png
│ │ │ │ ├── mipmap-xxxhdpi
│ │ │ │ └── ic_launcher.png
│ │ │ │ ├── values-night
│ │ │ │ └── styles.xml
│ │ │ │ └── values
│ │ │ │ └── styles.xml
│ │ │ └── profile
│ │ │ └── AndroidManifest.xml
│ ├── build.gradle
│ ├── gradle.properties
│ ├── gradle
│ │ └── wrapper
│ │ │ └── gradle-wrapper.properties
│ └── settings.gradle
├── ios
│ ├── .gitignore
│ ├── Flutter
│ │ ├── AppFrameworkInfo.plist
│ │ ├── Debug.xcconfig
│ │ └── Release.xcconfig
│ ├── Podfile
│ ├── Podfile.lock
│ ├── Runner.xcodeproj
│ │ ├── project.pbxproj
│ │ ├── project.xcworkspace
│ │ │ ├── contents.xcworkspacedata
│ │ │ └── xcshareddata
│ │ │ │ ├── IDEWorkspaceChecks.plist
│ │ │ │ └── WorkspaceSettings.xcsettings
│ │ └── xcshareddata
│ │ │ └── xcschemes
│ │ │ └── Runner.xcscheme
│ ├── Runner.xcworkspace
│ │ ├── contents.xcworkspacedata
│ │ └── xcshareddata
│ │ │ ├── IDEWorkspaceChecks.plist
│ │ │ └── WorkspaceSettings.xcsettings
│ └── Runner
│ │ ├── AppDelegate.h
│ │ ├── AppDelegate.m
│ │ ├── Assets.xcassets
│ │ ├── AppIcon.appiconset
│ │ │ ├── Contents.json
│ │ │ ├── Icon-App-1024x1024@1x.png
│ │ │ ├── Icon-App-20x20@1x.png
│ │ │ ├── Icon-App-20x20@2x.png
│ │ │ ├── Icon-App-20x20@3x.png
│ │ │ ├── Icon-App-29x29@1x.png
│ │ │ ├── Icon-App-29x29@2x.png
│ │ │ ├── Icon-App-29x29@3x.png
│ │ │ ├── Icon-App-40x40@1x.png
│ │ │ ├── Icon-App-40x40@2x.png
│ │ │ ├── Icon-App-40x40@3x.png
│ │ │ ├── Icon-App-60x60@2x.png
│ │ │ ├── Icon-App-60x60@3x.png
│ │ │ ├── Icon-App-76x76@1x.png
│ │ │ ├── Icon-App-76x76@2x.png
│ │ │ └── Icon-App-83.5x83.5@2x.png
│ │ └── LaunchImage.imageset
│ │ │ ├── Contents.json
│ │ │ ├── LaunchImage.png
│ │ │ ├── LaunchImage@2x.png
│ │ │ ├── LaunchImage@3x.png
│ │ │ └── README.md
│ │ ├── Base.lproj
│ │ ├── LaunchScreen.storyboard
│ │ └── Main.storyboard
│ │ ├── Info.plist
│ │ └── main.m
├── lib
│ ├── define
│ │ └── function_define.dart
│ ├── extensions
│ │ └── extension.dart
│ ├── full
│ │ ├── full_player.dart
│ │ ├── full_player_demo.dart
│ │ ├── full_screen_button.dart
│ │ ├── mute_button.dart
│ │ ├── play_button.dart
│ │ ├── player_control_overlay.dart
│ │ └── rate_button.dart
│ ├── main.dart
│ ├── platform_view_demo.dart
│ └── simple_demo.dart
├── pubspec.lock
├── pubspec.yaml
├── resources
│ ├── arrow.down.right.and.arrow.up.left.circle.png
│ ├── arrow.up.backward.and.arrow.down.forward.circle.png
│ ├── pause_circle.png
│ ├── play_circle.png
│ ├── speaker.circle.png
│ └── speaker.slash.circle.png
└── test
│ └── widget_test.dart
├── flt_video_player.iml
├── ios
├── .gitignore
├── Assets
│ └── .gitkeep
├── Classes
│ ├── EventSinkQueue.h
│ ├── EventSinkQueue.m
│ ├── FltBasePlayer.h
│ ├── FltBasePlayer.m
│ ├── FltVideoPlayerPlugin.h
│ ├── FltVideoPlayerPlugin.m
│ ├── FltVideoView.h
│ ├── FltVideoView.m
│ ├── FltVideoViewFactory.h
│ ├── FltVideoViewFactory.m
│ ├── FltVodPlayer.h
│ └── FltVodPlayer.m
└── flt_video_player.podspec
├── lib
├── flt_video_player.dart
└── src
│ ├── plugin.dart
│ └── vod
│ ├── platform_video_view.dart
│ ├── player_config.dart
│ ├── player_define.dart
│ ├── vod_player.dart
│ └── vodplayer_controller.dart
├── pubspec.lock
├── pubspec.yaml
└── test
└── flt_video_player_test.dart
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | .dart_tool/
3 |
4 | .packages
5 | .pub/
6 |
7 | build/
8 |
9 | .idea/Dart_SDK.xml
10 |
--------------------------------------------------------------------------------
/.idea/.gitignore:
--------------------------------------------------------------------------------
1 | # Default ignored files
2 | /shelf/
3 | /workspace.xml
4 |
--------------------------------------------------------------------------------
/.idea/libraries/Dart_SDK.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/.idea/libraries/Flutter_Plugins.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/.idea/runConfigurations/example_lib_main_dart.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.metadata:
--------------------------------------------------------------------------------
1 | # This file tracks properties of this Flutter project.
2 | # Used by Flutter tool to assess capabilities and perform upgrades etc.
3 | #
4 | # This file should be version controlled and should not be manually edited.
5 |
6 | version:
7 | revision: 7a4c33425ddd78c54aba07d86f3f9a4a0051769b
8 | channel: stable
9 |
10 | project_type: plugin
11 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | ## 0.0.1
2 |
3 | * TODO: Describe initial release.
4 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | TODO: Add your license here.
2 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # flt_video_player
2 |
3 | 基于腾讯云原生播放器封装的 Flutter 版本。
4 | 使用本播放器前建议查看腾讯云[官方文档](https://cloud.tencent.com/document/product/881/20191)
5 |
6 | ## 安装
7 |
8 | ```yaml
9 | //import
10 | dependencies:
11 | flt_video_player:
12 | git:
13 | url: https://github.com/RandyWei/flt_video_player.git
14 | ```
15 | ## Android
16 |
17 | 在 app/build.gradle 的 defaultConfig 中添加配置
18 | ```groovy
19 | ndk {
20 | abiFilters "armeabi", "armeabi-v7a", "arm64-v8a"
21 | }
22 | ```
23 | 在 android/app/src/main/AndroidManifest.xml 中增加如下配置
24 | ```xml
25 |
29 |
30 |
32 | ...
33 |
34 | ...
35 | _SimpleDemoState();
53 | }
54 |
55 | class _SimpleDemoState extends State {
56 | late VodPlayerController controller;
57 | double _aspectRation = 16 / 9;
58 |
59 | @override
60 | void initState() {
61 | super.initState();
62 |
63 | var playerConfig = PlayerConfig();
64 |
65 | //可以通过renderType来配置使用外接纹理方式还是 PlatformView 方式对接,经实测同一视频在安卓机型中两者在内存占用上区别不大,iOS 环境下外接纹理稍高
66 | controller = VodPlayerController(config: playerConfig);
67 |
68 | //监听播放状态
69 | controller.playState.listen((event) {
70 | debugPrint("playerState:$event");
71 | });
72 |
73 | controller.onPlayerEvent.listen((event) {
74 | debugPrint("PlayerEvent:$event");
75 | });
76 |
77 | controller.onNetEvent.listen((event) {
78 | //获取视频宽度高度
79 | double w = (event["VIDEO_WIDTH"]).toDouble();
80 | double h = (event["VIDEO_HEIGHT"]).toDouble();
81 |
82 | //计算比例
83 | if (w > 0 && h > 0) {
84 | _aspectRation = 1.0 * w / h;
85 | setState(() {});
86 | }
87 | });
88 |
89 | controller.initialize();
90 | }
91 |
92 | @override
93 | Widget build(BuildContext context) {
94 | return Scaffold(
95 | appBar: AppBar(
96 | title: const Text('Plugin example app'),
97 | ),
98 | body: Center(
99 | child: AspectRatio(
100 | aspectRatio: _aspectRation,
101 | child: VodPlayer(
102 | controller: controller,
103 | ),
104 | ),
105 | ),
106 | floatingActionButton: FloatingActionButton(
107 | onPressed: () {
108 | controller.play(
109 | "https://stream7.iqilu.com/10339/article/202002/18/2fca1c77730e54c7b500573c2437003f.mp4");
110 | },
111 | ),
112 | );
113 | }
114 |
115 | @override
116 | void dispose() {
117 | controller.dispose();
118 | super.dispose();
119 | }
120 | }
121 | ```
122 |
123 | 更多示例,可以查看 example 示例
124 |
--------------------------------------------------------------------------------
/analysis_options.yaml:
--------------------------------------------------------------------------------
1 | include: package:flutter_lints/flutter.yaml
2 |
3 | # Additional information about this file can be found at
4 | # https://dart.dev/guides/language/analysis-options
5 |
--------------------------------------------------------------------------------
/android/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | /local.properties
4 | /.idea/workspace.xml
5 | /.idea/libraries
6 | .DS_Store
7 | /build
8 | /captures
9 |
--------------------------------------------------------------------------------
/android/build.gradle:
--------------------------------------------------------------------------------
1 | group 'icu.bughub.plugins.video_player.flt_video_player'
2 | version '1.0-SNAPSHOT'
3 |
4 | buildscript {
5 | ext.kotlin_version = '1.6.21'
6 | repositories {
7 | google()
8 | mavenCentral()
9 | }
10 |
11 | dependencies {
12 | classpath 'com.android.tools.build:gradle:4.1.0'
13 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
14 | }
15 | }
16 |
17 | rootProject.allprojects {
18 | repositories {
19 | google()
20 | mavenCentral()
21 | }
22 | }
23 |
24 | apply plugin: 'com.android.library'
25 | apply plugin: 'kotlin-android'
26 |
27 | android {
28 | compileSdkVersion 30
29 |
30 | compileOptions {
31 | sourceCompatibility JavaVersion.VERSION_1_8
32 | targetCompatibility JavaVersion.VERSION_1_8
33 | }
34 |
35 | kotlinOptions {
36 | jvmTarget = '1.8'
37 | }
38 |
39 | sourceSets {
40 | main.java.srcDirs += 'src/main/kotlin'
41 | }
42 |
43 | defaultConfig {
44 | minSdkVersion 16
45 | }
46 | }
47 |
48 | dependencies {
49 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
50 | implementation 'com.tencent.liteav:LiteAVSDK_Player:9.5.29008'
51 | }
52 |
--------------------------------------------------------------------------------
/android/gradle.properties:
--------------------------------------------------------------------------------
1 | org.gradle.jvmargs=-Xmx1536M
2 | android.useAndroidX=true
3 | android.enableJetifier=true
4 |
--------------------------------------------------------------------------------
/android/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionBase=GRADLE_USER_HOME
2 | distributionPath=wrapper/dists
3 | zipStoreBase=GRADLE_USER_HOME
4 | zipStorePath=wrapper/dists
5 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.7-all.zip
6 |
--------------------------------------------------------------------------------
/android/settings.gradle:
--------------------------------------------------------------------------------
1 | rootProject.name = 'flt_video_player'
2 |
--------------------------------------------------------------------------------
/android/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
4 |
--------------------------------------------------------------------------------
/android/src/main/kotlin/icu/bughub/plugins/video_player/flt_video_player/Constants.kt:
--------------------------------------------------------------------------------
1 | package icu.bughub.plugins.video_player.flt_video_player
2 |
3 | object Constants {
4 | val CHANNEL_PREFIX = "plugins.bughub.icu"
5 | }
--------------------------------------------------------------------------------
/android/src/main/kotlin/icu/bughub/plugins/video_player/flt_video_player/FltBasePlayer.kt:
--------------------------------------------------------------------------------
1 | package icu.bughub.plugins.video_player.flt_video_player
2 |
3 | import android.content.Context
4 | import android.os.Bundle
5 | import com.tencent.rtmp.ITXVodPlayListener
6 | import com.tencent.rtmp.TXVodPlayConfig
7 | import com.tencent.rtmp.TXVodPlayer
8 | import io.flutter.embedding.engine.plugins.FlutterPlugin
9 | import io.flutter.plugin.common.EventChannel
10 | import io.flutter.plugin.common.MethodCall
11 | import io.flutter.plugin.common.MethodChannel
12 | import java.util.concurrent.atomic.AtomicInteger
13 | import kotlin.math.max
14 | import kotlin.math.min
15 |
16 | open class FltBasePlayer(private val flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) :
17 | MethodChannel.MethodCallHandler, ITXVodPlayListener {
18 | private val mAtomicId = AtomicInteger(0)
19 | private var mPlayerId: Int = -1
20 |
21 | private val uninitialized = -1
22 | protected var textureId: Long = -1
23 |
24 | protected var methodChannel: MethodChannel? = null
25 | protected var eventChannel: EventChannel? = null
26 | protected var netChannel: EventChannel? = null
27 |
28 | protected var eventSink: PlayerEventSink = PlayerEventSink()
29 | protected var netEventSink: PlayerEventSink = PlayerEventSink()
30 | protected var vodPlayer: TXVodPlayer? = null
31 |
32 | init {
33 | this.mPlayerId = mAtomicId.incrementAndGet()
34 | }
35 |
36 | open fun getPlayerId(): Int {
37 | return mPlayerId
38 | }
39 |
40 | /**
41 | * 初始化播放器
42 | *
43 | * @return texture id
44 | */
45 | protected open fun initPlayer(playConfig: TXVodPlayConfig) {
46 | if (vodPlayer == null) {
47 | vodPlayer = TXVodPlayer(flutterPluginBinding.applicationContext)
48 |
49 | vodPlayer?.setConfig(playConfig)
50 | vodPlayer?.enableHardwareDecode(true)
51 | vodPlayer?.setVodListener(this)
52 | }
53 | }
54 |
55 |
56 | private fun stopPlay(isNeedClearLastImg: Boolean): Int? {
57 | return vodPlayer?.stopPlay(isNeedClearLastImg)
58 | }
59 |
60 |
61 | private fun startPlay(url: String): Int {
62 | return vodPlayer?.startPlay(url) ?: uninitialized
63 | }
64 |
65 |
66 | override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) {
67 |
68 | when (call.method) {
69 | "init" -> {
70 |
71 | val playConfig = TXVodPlayConfig()
72 |
73 | playConfig.setConnectRetryCount(call.argument("connectRetryCount") ?: 3)
74 | playConfig.setConnectRetryInterval(call.argument("connectRetryInterval") ?: 3)
75 | playConfig.setTimeout(call.argument("timeout") ?: 10)
76 | playConfig.setFirstStartPlayBufferTime(
77 | call.argument("firstStartPlayBufferTime") ?: 100
78 | )
79 | playConfig.setNextStartPlayBufferTime(
80 | call.argument("nextStartPlayBufferTime") ?: 250
81 | )
82 |
83 | playConfig.setCacheFolderPath(call.argument("cacheFolderPath"))
84 | playConfig.setMaxCacheItems(call.argument("maxCacheItems") ?: 0)
85 | playConfig.setHeaders(call.argument("headers"))
86 | //< 是否精确 seek,默认YES。开启精确后seek,seek 的时间平均多出200ms
87 | playConfig.setEnableAccurateSeek(
88 | call.argument("enableAccurateSeek") ?: false
89 | )
90 | playConfig.setProgressInterval(
91 | (call.argument("progressInterval")?.toInt() ?: 0)
92 | )
93 |
94 | playConfig.setMaxBufferSize(call.argument("maxBufferSize") ?: 0)
95 |
96 | playConfig.setOverlayKey(call.argument("overlayKey"))
97 | playConfig.setOverlayIv(call.argument("overlayIv"))
98 |
99 |
100 | initPlayer(playConfig)
101 |
102 | result.success(textureId)
103 | }
104 |
105 | "play" -> {
106 | val url = call.argument("url")
107 | if (url?.isNotEmpty() == true) {
108 | val r = startPlay(url)
109 | result.success(r)
110 | } else {
111 | result.error("404", "url为空", "url为空")
112 | }
113 | }
114 |
115 | "stop" -> {
116 | val isNeedClearLastImg = call.argument("isNeedClearLastImg") ?: false
117 | val r: Int? = stopPlay(isNeedClearLastImg)
118 | result.success(r)
119 | }
120 |
121 |
122 | "isPlaying" -> {
123 | result.success(vodPlayer?.isPlaying ?: false)
124 | }
125 |
126 | "pause" -> {
127 | vodPlayer?.pause()
128 | result.success(null)
129 | }
130 |
131 | "resume" -> {
132 | vodPlayer?.resume()
133 | result.success(null)
134 | }
135 |
136 | "seek" -> {
137 | val time = call.argument("time")
138 | if (time != null) {
139 | vodPlayer?.seek(time)
140 | }
141 | result.success(null)
142 | }
143 |
144 | "setStartTime" -> {
145 | val time = call.argument("time")?.toFloat()
146 | if (time != null) {
147 | vodPlayer?.setStartTime(time)
148 | }
149 | result.success(null)
150 | }
151 |
152 | "currentPlaybackTime" -> {
153 | result.success(vodPlayer?.currentPlaybackTime)
154 | }
155 |
156 | "duration" -> result.success(vodPlayer?.duration)
157 |
158 | "playableDuration" -> result.success(vodPlayer?.playableDuration)
159 |
160 | "setMute" -> {
161 | val enable = call.argument("enable") ?: false
162 | vodPlayer?.setMute(enable)
163 | result.success(null)
164 | }
165 |
166 | "setAudioPlayoutVolume" -> {
167 | var volume = call.argument("volume") ?: 0
168 | volume = max(0, volume)
169 | volume = min(100, volume)
170 | vodPlayer?.setAudioPlayoutVolume(volume)
171 | result.success(null)
172 | }
173 |
174 | "setRate" -> {
175 | val rate = (call.argument("rate") ?: 1.0).toFloat()
176 | vodPlayer?.setRate(rate)
177 | result.success(null)
178 | }
179 |
180 | "setMirror" -> {
181 | val mirror = call.argument("mirror") ?: false
182 | vodPlayer?.setMirror(mirror)
183 | result.success(null)
184 | }
185 |
186 | "setLoop" -> {
187 | val loop = call.argument("loop") ?: false
188 | vodPlayer?.isLoop = loop
189 | result.success(null)
190 | }
191 |
192 | "setRenderRotation" -> {
193 | val rotation = call.argument("rotation") ?: 1
194 | vodPlayer?.setRenderRotation(rotation)
195 | result.success(null)
196 | }
197 | }
198 | }
199 |
200 |
201 | private fun getParams(event: Int, bundle: Bundle?): Map {
202 | val param = HashMap()
203 |
204 | if (event != 0) {
205 | param["event"] = event
206 | }
207 |
208 | if (bundle?.isEmpty == false) {
209 | val keySet = bundle.keySet()
210 | for (key in keySet) {
211 | val value = bundle.get(key)
212 | param[key] = value
213 | }
214 | }
215 |
216 | return param
217 | }
218 |
219 |
220 | override fun onPlayEvent(player: TXVodPlayer?, i: Int, bundle: Bundle?) {
221 | eventSink.success(getParams(i, bundle))
222 | }
223 |
224 | override fun onNetStatus(player: TXVodPlayer?, bundle: Bundle?) {
225 | netEventSink.success(getParams(0, bundle))
226 | }
227 |
228 | open fun destroy() {
229 | stopPlay(false)
230 | vodPlayer = null
231 | }
232 |
233 | }
--------------------------------------------------------------------------------
/android/src/main/kotlin/icu/bughub/plugins/video_player/flt_video_player/FltVideoPlayerPlugin.kt:
--------------------------------------------------------------------------------
1 | package icu.bughub.plugins.video_player.flt_video_player
2 |
3 | import android.util.SparseArray
4 | import androidx.annotation.NonNull
5 | import icu.bughub.plugins.video_player.flt_video_player.Constants.CHANNEL_PREFIX
6 |
7 | import io.flutter.embedding.engine.plugins.FlutterPlugin
8 | import io.flutter.embedding.engine.plugins.activity.ActivityAware
9 | import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding
10 | import io.flutter.plugin.common.MethodCall
11 | import io.flutter.plugin.common.MethodChannel
12 | import io.flutter.plugin.common.MethodChannel.MethodCallHandler
13 | import io.flutter.plugin.common.MethodChannel.Result
14 |
15 | /** FltVideoPlayerPlugin */
16 | class FltVideoPlayerPlugin : FlutterPlugin, MethodCallHandler, ActivityAware {
17 |
18 |
19 | /// The MethodChannel that will the communication between Flutter and native Android
20 | ///
21 | /// This local reference serves to register the plugin with the Flutter Engine and unregister it
22 | /// when the Flutter Engine is detached from the Activity
23 | private lateinit var channel: MethodChannel
24 |
25 | //存储播放器
26 | private val players = SparseArray()
27 |
28 | private var flutterPluginBinding: FlutterPlugin.FlutterPluginBinding? = null
29 |
30 | override fun onAttachedToEngine(@NonNull flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) {
31 | this.flutterPluginBinding = flutterPluginBinding
32 | channel = MethodChannel(
33 | flutterPluginBinding.binaryMessenger,
34 | "${CHANNEL_PREFIX}/flt_video_player"
35 | )
36 | channel.setMethodCallHandler(this)
37 | flutterPluginBinding.platformViewRegistry.registerViewFactory(
38 | "FltVideoView",
39 | FltVideoViewFactory(flutterPluginBinding) { viewId: Int, view: FltVideoView ->
40 | players.append(viewId, view)
41 | }
42 | )
43 | }
44 |
45 | override fun onMethodCall(@NonNull call: MethodCall, @NonNull result: Result) {
46 | when (call.method) {
47 | "getPlatformVersion" -> {
48 | result.success("Android ${android.os.Build.VERSION.RELEASE}")
49 | }
50 | "createVodPlayer" -> {
51 | val vodPlayer = flutterPluginBinding?.let { FltVodPlayer(it) }
52 | val playerId = vodPlayer?.getPlayerId() ?: -1
53 | players.append(playerId, vodPlayer)
54 | result.success(playerId)
55 | }
56 | "releaseVodPlayer" -> {
57 | val playerId = call.argument("playerId") ?: -1
58 | val player = players[playerId]
59 | player.destroy()
60 | players.remove(playerId)
61 | }
62 | else -> {
63 | result.notImplemented()
64 | }
65 | }
66 | }
67 |
68 |
69 | override fun onDetachedFromEngine(@NonNull binding: FlutterPlugin.FlutterPluginBinding) {
70 | channel.setMethodCallHandler(null)
71 | }
72 |
73 | override fun onAttachedToActivity(binding: ActivityPluginBinding) {
74 |
75 | }
76 |
77 | override fun onDetachedFromActivityForConfigChanges() {
78 |
79 | }
80 |
81 | override fun onReattachedToActivityForConfigChanges(binding: ActivityPluginBinding) {
82 |
83 | }
84 |
85 | override fun onDetachedFromActivity() {
86 |
87 | }
88 | }
89 |
--------------------------------------------------------------------------------
/android/src/main/kotlin/icu/bughub/plugins/video_player/flt_video_player/FltVideoView.kt:
--------------------------------------------------------------------------------
1 | package icu.bughub.plugins.video_player.flt_video_player
2 |
3 | import android.content.Context
4 | import android.view.View
5 | import com.tencent.rtmp.TXVodPlayConfig
6 | import com.tencent.rtmp.ui.TXCloudVideoView
7 | import io.flutter.embedding.engine.plugins.FlutterPlugin
8 | import io.flutter.plugin.common.EventChannel
9 | import io.flutter.plugin.common.MethodChannel
10 | import io.flutter.plugin.common.StandardMessageCodec
11 | import io.flutter.plugin.platform.PlatformView
12 | import io.flutter.plugin.platform.PlatformViewFactory
13 |
14 | class FltVideoView(
15 | flutterPluginBinding: FlutterPlugin.FlutterPluginBinding,
16 | viewId: Int
17 | ) : PlatformView, FltBasePlayer(flutterPluginBinding) {
18 |
19 | private var container: TXCloudVideoView =
20 | TXCloudVideoView(flutterPluginBinding.applicationContext)
21 |
22 | init {
23 | methodChannel = MethodChannel(
24 | flutterPluginBinding.binaryMessenger,
25 | "${Constants.CHANNEL_PREFIX}/vodplayer/${viewId}"
26 | )
27 | methodChannel?.setMethodCallHandler(this)
28 |
29 | //注册通信通道
30 | eventChannel = EventChannel(
31 | flutterPluginBinding.binaryMessenger,
32 | "${Constants.CHANNEL_PREFIX}/vodplayer/event/${viewId}"
33 | )
34 | eventChannel?.setStreamHandler(object : EventChannel.StreamHandler {
35 | override fun onListen(p0: Any?, p1: EventChannel.EventSink?) {
36 | eventSink.setSinkProxy(p1)
37 | }
38 |
39 | override fun onCancel(p0: Any?) {
40 | eventSink.setSinkProxy(null)
41 | }
42 | })
43 |
44 | netChannel = EventChannel(
45 | flutterPluginBinding.binaryMessenger,
46 | "${Constants.CHANNEL_PREFIX}/vodplayer/net/${viewId}"
47 | )
48 | netChannel?.setStreamHandler(object : EventChannel.StreamHandler {
49 | override fun onListen(p0: Any?, p1: EventChannel.EventSink?) {
50 | netEventSink.setSinkProxy(p1)
51 | }
52 |
53 | override fun onCancel(p0: Any?) {
54 | netEventSink.setSinkProxy(null)
55 | }
56 | })
57 | }
58 |
59 | override fun initPlayer(playConfig: TXVodPlayConfig) {
60 | super.initPlayer(playConfig)
61 | vodPlayer?.setPlayerView(container)
62 | }
63 |
64 | override fun getView(): View {
65 | return container
66 | }
67 |
68 | override fun dispose() {
69 | }
70 | }
71 |
72 | class FltVideoViewFactory(
73 | private val
74 | flutterPluginBinding: FlutterPlugin.FlutterPluginBinding,
75 | private val onViewCreated: (Int, FltVideoView) -> Unit
76 | ) : PlatformViewFactory(
77 | StandardMessageCodec.INSTANCE
78 | ) {
79 |
80 | override fun create(context: Context?, viewId: Int, args: Any?): PlatformView {
81 | val view = FltVideoView(flutterPluginBinding, viewId)
82 | onViewCreated.invoke(viewId, view)
83 | return view
84 | }
85 |
86 | }
--------------------------------------------------------------------------------
/android/src/main/kotlin/icu/bughub/plugins/video_player/flt_video_player/FltVodPlayer.kt:
--------------------------------------------------------------------------------
1 | package icu.bughub.plugins.video_player.flt_video_player
2 |
3 | import android.graphics.SurfaceTexture
4 | import android.view.Surface
5 | import com.tencent.rtmp.TXVodPlayConfig
6 | import io.flutter.embedding.engine.plugins.FlutterPlugin
7 | import io.flutter.plugin.common.EventChannel
8 | import io.flutter.plugin.common.MethodChannel
9 | import io.flutter.view.TextureRegistry
10 |
11 | class FltVodPlayer(private val flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) :
12 | FltBasePlayer(flutterPluginBinding) {
13 |
14 | private var surfaceTextureEntry: TextureRegistry.SurfaceTextureEntry? = null
15 | private var surfaceTexture: SurfaceTexture? = null
16 | private var surface: Surface? = null
17 |
18 |
19 | init {
20 | methodChannel = MethodChannel(
21 | flutterPluginBinding.binaryMessenger,
22 | "${Constants.CHANNEL_PREFIX}/vodplayer/${getPlayerId()}"
23 | )
24 | methodChannel?.setMethodCallHandler(this)
25 |
26 | //注册通信通道
27 | eventChannel = EventChannel(
28 | flutterPluginBinding.binaryMessenger,
29 | "${Constants.CHANNEL_PREFIX}/vodplayer/event/${getPlayerId()}"
30 | )
31 | eventChannel?.setStreamHandler(object : EventChannel.StreamHandler {
32 | override fun onListen(p0: Any?, p1: EventChannel.EventSink?) {
33 | eventSink.setSinkProxy(p1)
34 | }
35 |
36 | override fun onCancel(p0: Any?) {
37 | eventSink.setSinkProxy(null)
38 | }
39 | })
40 |
41 | netChannel = EventChannel(
42 | flutterPluginBinding.binaryMessenger,
43 | "${Constants.CHANNEL_PREFIX}/vodplayer/net/${getPlayerId()}"
44 | )
45 | netChannel?.setStreamHandler(object : EventChannel.StreamHandler {
46 | override fun onListen(p0: Any?, p1: EventChannel.EventSink?) {
47 | netEventSink.setSinkProxy(p1)
48 | }
49 |
50 | override fun onCancel(p0: Any?) {
51 | netEventSink.setSinkProxy(null)
52 | }
53 | })
54 | }
55 |
56 | override fun destroy() {
57 |
58 | super.destroy()
59 |
60 | surfaceTextureEntry?.release()
61 | surfaceTextureEntry = null
62 |
63 | surfaceTexture?.release()
64 | surfaceTexture = null
65 |
66 | surface?.release()
67 | surface = null
68 |
69 |
70 | methodChannel?.setMethodCallHandler(null)
71 | methodChannel = null
72 |
73 | eventChannel?.setStreamHandler(null)
74 | eventChannel = null
75 | netChannel?.setStreamHandler(null)
76 | netChannel = null
77 |
78 |
79 | }
80 |
81 | override fun initPlayer(playConfig: TXVodPlayConfig) {
82 | super.initPlayer(playConfig)
83 | setupPlayer()
84 | }
85 |
86 | /**
87 | * 配置播放器
88 | *
89 | */
90 | private fun setupPlayer() {
91 | surfaceTextureEntry = flutterPluginBinding.textureRegistry.createSurfaceTexture()
92 | surfaceTexture = surfaceTextureEntry?.surfaceTexture()
93 | surface = Surface(surfaceTexture)
94 |
95 | vodPlayer?.setSurface(surface)
96 |
97 | textureId = surfaceTextureEntry?.id() ?: -1
98 | }
99 |
100 | }
--------------------------------------------------------------------------------
/android/src/main/kotlin/icu/bughub/plugins/video_player/flt_video_player/PlayerEventSink.kt:
--------------------------------------------------------------------------------
1 | package icu.bughub.plugins.video_player.flt_video_player
2 |
3 | import io.flutter.plugin.common.EventChannel
4 | import java.util.*
5 |
6 | class PlayerEventSink : EventChannel.EventSink {
7 |
8 | private var eventSink: EventChannel.EventSink? = null
9 |
10 | private val eventQueue: Queue = LinkedList()
11 |
12 | private var isEnd = false
13 |
14 | fun setSinkProxy(sink: EventChannel.EventSink?) {
15 | this.eventSink = sink
16 | consume()
17 | }
18 |
19 | private fun enqueue(event: Any?) {
20 | if (isEnd) return
21 | eventQueue.offer(event)
22 | }
23 |
24 | private fun consume() {
25 | while (!eventQueue.isEmpty()) {
26 | when (val event = eventQueue.poll()) {
27 | is EndEvent -> {
28 | eventSink?.endOfStream()
29 | }
30 | is ErrorEvent -> {
31 | val errorEvent: ErrorEvent = event
32 | eventSink?.error(errorEvent.code, errorEvent.message, errorEvent.details)
33 | }
34 | else -> {
35 | eventSink?.success(event)
36 | }
37 | }
38 | }
39 | }
40 |
41 | override fun success(event: Any?) {
42 | enqueue(event)
43 | consume()
44 | }
45 |
46 | override fun error(code: String?, message: String?, details: Any?) {
47 | enqueue(ErrorEvent(code, message, details))
48 | consume()
49 | }
50 |
51 | override fun endOfStream() {
52 | enqueue(EndEvent())
53 | consume()
54 | isEnd = true
55 | }
56 |
57 | private class EndEvent
58 |
59 | private data class ErrorEvent(val code: String?, val message: String?, val details: Any?)
60 | }
--------------------------------------------------------------------------------
/example/.gitignore:
--------------------------------------------------------------------------------
1 | # Miscellaneous
2 | *.class
3 | *.log
4 | *.pyc
5 | *.swp
6 | .DS_Store
7 | .atom/
8 | .buildlog/
9 | .history
10 | .svn/
11 |
12 | # IntelliJ related
13 | *.iml
14 | *.ipr
15 | *.iws
16 | .idea/
17 |
18 | # The .vscode folder contains launch configuration and tasks you configure in
19 | # VS Code which you may wish to be included in version control, so this line
20 | # is commented out by default.
21 | #.vscode/
22 |
23 | # Flutter/Dart/Pub related
24 | **/doc/api/
25 | **/ios/Flutter/.last_build_id
26 | .dart_tool/
27 | .flutter-plugins
28 | .flutter-plugins-dependencies
29 | .packages
30 | .pub-cache/
31 | .pub/
32 | /build/
33 |
34 | # Web related
35 | lib/generated_plugin_registrant.dart
36 |
37 | # Symbolication related
38 | app.*.symbols
39 |
40 | # Obfuscation related
41 | app.*.map.json
42 |
43 | # Android Studio will place build artifacts here
44 | /android/app/debug
45 | /android/app/profile
46 | /android/app/release
47 |
--------------------------------------------------------------------------------
/example/.metadata:
--------------------------------------------------------------------------------
1 | # This file tracks properties of this Flutter project.
2 | # Used by Flutter tool to assess capabilities and perform upgrades etc.
3 | #
4 | # This file should be version controlled and should not be manually edited.
5 |
6 | version:
7 | revision: 3595343e20a61ff16d14e8ecc25f364276bb1b8b
8 | channel: stable
9 |
10 | project_type: app
11 |
--------------------------------------------------------------------------------
/example/README.md:
--------------------------------------------------------------------------------
1 | # flt_video_player_example
2 |
3 | Demonstrates how to use the flt_video_player plugin.
4 |
5 | ## Getting Started
6 |
7 | This project is a starting point for a Flutter application.
8 |
9 | A few resources to get you started if this is your first Flutter project:
10 |
11 | - [Lab: Write your first Flutter app](https://flutter.dev/docs/get-started/codelab)
12 | - [Cookbook: Useful Flutter samples](https://flutter.dev/docs/cookbook)
13 |
14 | For help getting started with Flutter, view our
15 | [online documentation](https://flutter.dev/docs), which offers tutorials,
16 | samples, guidance on mobile development, and a full API reference.
17 |
--------------------------------------------------------------------------------
/example/analysis_options.yaml:
--------------------------------------------------------------------------------
1 | # This file configures the analyzer, which statically analyzes Dart code to
2 | # check for errors, warnings, and lints.
3 | #
4 | # The issues identified by the analyzer are surfaced in the UI of Dart-enabled
5 | # IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be
6 | # invoked from the command line by running `flutter analyze`.
7 |
8 | # The following line activates a set of recommended lints for Flutter apps,
9 | # packages, and plugins designed to encourage good coding practices.
10 | include: package:flutter_lints/flutter.yaml
11 |
12 | linter:
13 | # The lint rules applied to this project can be customized in the
14 | # section below to disable rules from the `package:flutter_lints/flutter.yaml`
15 | # included above or to enable additional rules. A list of all available lints
16 | # and their documentation is published at
17 | # https://dart-lang.github.io/linter/lints/index.html.
18 | #
19 | # Instead of disabling a lint rule for the entire project in the
20 | # section below, it can also be suppressed for a single line of code
21 | # or a specific dart file by using the `// ignore: name_of_lint` and
22 | # `// ignore_for_file: name_of_lint` syntax on the line or in the file
23 | # producing the lint.
24 | rules:
25 | # avoid_print: false # Uncomment to disable the `avoid_print` rule
26 | # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule
27 |
28 | # Additional information about this file can be found at
29 | # https://dart.dev/guides/language/analysis-options
30 |
--------------------------------------------------------------------------------
/example/android/.gitignore:
--------------------------------------------------------------------------------
1 | gradle-wrapper.jar
2 | /.gradle
3 | /captures/
4 | /gradlew
5 | /gradlew.bat
6 | /local.properties
7 | GeneratedPluginRegistrant.java
8 |
9 | # Remember to never publicly share your keystore.
10 | # See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app
11 | key.properties
12 | **/*.keystore
13 | **/*.jks
14 |
--------------------------------------------------------------------------------
/example/android/app/build.gradle:
--------------------------------------------------------------------------------
1 | def localProperties = new Properties()
2 | def localPropertiesFile = rootProject.file('local.properties')
3 | if (localPropertiesFile.exists()) {
4 | localPropertiesFile.withReader('UTF-8') { reader ->
5 | localProperties.load(reader)
6 | }
7 | }
8 |
9 | def flutterRoot = localProperties.getProperty('flutter.sdk')
10 | if (flutterRoot == null) {
11 | throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.")
12 | }
13 |
14 | def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
15 | if (flutterVersionCode == null) {
16 | flutterVersionCode = '1'
17 | }
18 |
19 | def flutterVersionName = localProperties.getProperty('flutter.versionName')
20 | if (flutterVersionName == null) {
21 | flutterVersionName = '1.0'
22 | }
23 |
24 | apply plugin: 'com.android.application'
25 | apply plugin: 'kotlin-android'
26 | apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
27 |
28 | android {
29 | compileSdkVersion 30
30 |
31 | compileOptions {
32 | sourceCompatibility JavaVersion.VERSION_1_8
33 | targetCompatibility JavaVersion.VERSION_1_8
34 | }
35 |
36 | kotlinOptions {
37 | jvmTarget = '1.8'
38 | }
39 |
40 | sourceSets {
41 | main.java.srcDirs += 'src/main/kotlin'
42 | }
43 |
44 | defaultConfig {
45 | // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
46 | applicationId "icu.bughub.plugins.video_player.flt_video_player_example"
47 | minSdkVersion 16
48 | targetSdkVersion 30
49 | versionCode flutterVersionCode.toInteger()
50 | versionName flutterVersionName
51 |
52 | ndk {
53 | abiFilters "armeabi", "armeabi-v7a", "arm64-v8a"
54 | }
55 | }
56 |
57 | buildTypes {
58 | release {
59 | // TODO: Add your own signing config for the release build.
60 | // Signing with the debug keys for now, so `flutter run --release` works.
61 | signingConfig signingConfigs.debug
62 | }
63 | }
64 | }
65 |
66 | flutter {
67 | source '../..'
68 | }
69 |
70 | dependencies {
71 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
72 | }
73 |
--------------------------------------------------------------------------------
/example/android/app/src/debug/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/example/android/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
9 |
16 |
20 |
23 |
28 |
31 |
32 |
33 |
34 |
35 |
36 |
38 |
41 |
42 |
43 |
--------------------------------------------------------------------------------
/example/android/app/src/main/kotlin/icu/bughub/plugins/video_player/flt_video_player_example/MainActivity.kt:
--------------------------------------------------------------------------------
1 | package icu.bughub.plugins.video_player.flt_video_player_example
2 |
3 | import io.flutter.embedding.android.FlutterActivity
4 |
5 | class MainActivity: FlutterActivity() {
6 | }
7 |
--------------------------------------------------------------------------------
/example/android/app/src/main/res/drawable-v21/launch_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
12 |
13 |
--------------------------------------------------------------------------------
/example/android/app/src/main/res/drawable/launch_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
12 |
13 |
--------------------------------------------------------------------------------
/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RandyWei/flt_video_player/fa8d074a73c354362459d2106b68e0f0882088e4/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RandyWei/flt_video_player/fa8d074a73c354362459d2106b68e0f0882088e4/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RandyWei/flt_video_player/fa8d074a73c354362459d2106b68e0f0882088e4/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RandyWei/flt_video_player/fa8d074a73c354362459d2106b68e0f0882088e4/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RandyWei/flt_video_player/fa8d074a73c354362459d2106b68e0f0882088e4/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/example/android/app/src/main/res/values-night/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
9 |
15 |
18 |
19 |
--------------------------------------------------------------------------------
/example/android/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
9 |
15 |
18 |
19 |
--------------------------------------------------------------------------------
/example/android/app/src/profile/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/example/android/build.gradle:
--------------------------------------------------------------------------------
1 | buildscript {
2 | ext.kotlin_version = '1.3.50'
3 | repositories {
4 | maven { url 'https://maven.aliyun.com/nexus/content/repositories/central/' }
5 | maven { url 'https://repo1.maven.org/maven2/'}
6 | google()
7 | mavenCentral()
8 | }
9 |
10 | dependencies {
11 | classpath 'com.android.tools.build:gradle:7.0.3'
12 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
13 | }
14 | }
15 |
16 | allprojects {
17 | repositories {
18 | maven { url 'https://maven.aliyun.com/nexus/content/repositories/central/' }
19 | maven { url 'https://repo1.maven.org/maven2/'}
20 | google()
21 | mavenCentral()
22 | }
23 | }
24 |
25 | rootProject.buildDir = '../build'
26 | subprojects {
27 | project.buildDir = "${rootProject.buildDir}/${project.name}"
28 | project.evaluationDependsOn(':app')
29 | }
30 |
31 | task clean(type: Delete) {
32 | delete rootProject.buildDir
33 | }
34 |
--------------------------------------------------------------------------------
/example/android/gradle.properties:
--------------------------------------------------------------------------------
1 | org.gradle.jvmargs=-Xmx1536M
2 | android.useAndroidX=true
3 | android.enableJetifier=true
4 |
--------------------------------------------------------------------------------
/example/android/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Fri Jun 23 08:50:38 CEST 2017
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-all.zip
7 |
--------------------------------------------------------------------------------
/example/android/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app'
2 |
3 | def localPropertiesFile = new File(rootProject.projectDir, "local.properties")
4 | def properties = new Properties()
5 |
6 | assert localPropertiesFile.exists()
7 | localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) }
8 |
9 | def flutterSdkPath = properties.getProperty("flutter.sdk")
10 | assert flutterSdkPath != null, "flutter.sdk not set in local.properties"
11 | apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle"
12 |
--------------------------------------------------------------------------------
/example/ios/.gitignore:
--------------------------------------------------------------------------------
1 | **/dgph
2 | *.mode1v3
3 | *.mode2v3
4 | *.moved-aside
5 | *.pbxuser
6 | *.perspectivev3
7 | **/*sync/
8 | .sconsign.dblite
9 | .tags*
10 | **/.vagrant/
11 | **/DerivedData/
12 | Icon?
13 | **/Pods/
14 | **/.symlinks/
15 | profile
16 | xcuserdata
17 | **/.generated/
18 | Flutter/App.framework
19 | Flutter/Flutter.framework
20 | Flutter/Flutter.podspec
21 | Flutter/Generated.xcconfig
22 | Flutter/ephemeral/
23 | Flutter/app.flx
24 | Flutter/app.zip
25 | Flutter/flutter_assets/
26 | Flutter/flutter_export_environment.sh
27 | ServiceDefinitions.json
28 | Runner/GeneratedPluginRegistrant.*
29 |
30 | # Exceptions to above rules.
31 | !default.mode1v3
32 | !default.mode2v3
33 | !default.pbxuser
34 | !default.perspectivev3
35 |
--------------------------------------------------------------------------------
/example/ios/Flutter/AppFrameworkInfo.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | App
9 | CFBundleIdentifier
10 | io.flutter.flutter.app
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | App
15 | CFBundlePackageType
16 | FMWK
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | 1.0
23 | MinimumOSVersion
24 | 9.0
25 |
26 |
27 |
--------------------------------------------------------------------------------
/example/ios/Flutter/Debug.xcconfig:
--------------------------------------------------------------------------------
1 | #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"
2 | #include "Generated.xcconfig"
3 |
--------------------------------------------------------------------------------
/example/ios/Flutter/Release.xcconfig:
--------------------------------------------------------------------------------
1 | #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"
2 | #include "Generated.xcconfig"
3 |
--------------------------------------------------------------------------------
/example/ios/Podfile:
--------------------------------------------------------------------------------
1 | # Uncomment this line to define a global platform for your project
2 | # platform :ios, '9.0'
3 |
4 | # CocoaPods analytics sends network stats synchronously affecting flutter build latency.
5 | ENV['COCOAPODS_DISABLE_STATS'] = 'true'
6 |
7 | project 'Runner', {
8 | 'Debug' => :debug,
9 | 'Profile' => :release,
10 | 'Release' => :release,
11 | }
12 |
13 | def flutter_root
14 | generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__)
15 | unless File.exist?(generated_xcode_build_settings_path)
16 | raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first"
17 | end
18 |
19 | File.foreach(generated_xcode_build_settings_path) do |line|
20 | matches = line.match(/FLUTTER_ROOT\=(.*)/)
21 | return matches[1].strip if matches
22 | end
23 | raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get"
24 | end
25 |
26 | require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root)
27 |
28 | flutter_ios_podfile_setup
29 |
30 | target 'Runner' do
31 | flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__))
32 | end
33 |
34 | post_install do |installer|
35 | installer.pods_project.targets.each do |target|
36 | flutter_additional_ios_build_settings(target)
37 | end
38 | end
39 |
--------------------------------------------------------------------------------
/example/ios/Podfile.lock:
--------------------------------------------------------------------------------
1 | PODS:
2 | - flt_video_player (1.0.0):
3 | - Flutter
4 | - TXLiteAVSDK_Player
5 | - Flutter (1.0.0)
6 | - orientation (0.0.1):
7 | - Flutter
8 | - TXLiteAVSDK_Player (9.2.10637)
9 |
10 | DEPENDENCIES:
11 | - flt_video_player (from `.symlinks/plugins/flt_video_player/ios`)
12 | - Flutter (from `Flutter`)
13 | - orientation (from `.symlinks/plugins/orientation/ios`)
14 |
15 | SPEC REPOS:
16 | trunk:
17 | - TXLiteAVSDK_Player
18 |
19 | EXTERNAL SOURCES:
20 | flt_video_player:
21 | :path: ".symlinks/plugins/flt_video_player/ios"
22 | Flutter:
23 | :path: Flutter
24 | orientation:
25 | :path: ".symlinks/plugins/orientation/ios"
26 |
27 | SPEC CHECKSUMS:
28 | flt_video_player: b22e5e3f4a408e50e5bf28a81f52a891bced35a3
29 | Flutter: 50d75fe2f02b26cc09d224853bb45737f8b3214a
30 | orientation: 6c9203efe86ce4cff379756910f18b2d745628c3
31 | TXLiteAVSDK_Player: 300e6fc7262ae095ee13b18d7d821c5fae0996f9
32 |
33 | PODFILE CHECKSUM: 8e679eca47255a8ca8067c4c67aab20e64cb974d
34 |
35 | COCOAPODS: 1.10.1
36 |
--------------------------------------------------------------------------------
/example/ios/Runner.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 50;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; };
11 | 22B0993FE1A8142609529A27 /* libPods-Runner.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 0B5F01449A1B301F58B80EC3 /* libPods-Runner.a */; };
12 | 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
13 | 978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */; };
14 | 97C146F31CF9000F007C117D /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 97C146F21CF9000F007C117D /* main.m */; };
15 | 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
16 | 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
17 | 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
18 | /* End PBXBuildFile section */
19 |
20 | /* Begin PBXCopyFilesBuildPhase section */
21 | 9705A1C41CF9048500538489 /* Embed Frameworks */ = {
22 | isa = PBXCopyFilesBuildPhase;
23 | buildActionMask = 2147483647;
24 | dstPath = "";
25 | dstSubfolderSpec = 10;
26 | files = (
27 | );
28 | name = "Embed Frameworks";
29 | runOnlyForDeploymentPostprocessing = 0;
30 | };
31 | /* End PBXCopyFilesBuildPhase section */
32 |
33 | /* Begin PBXFileReference section */
34 | 0B5F01449A1B301F58B80EC3 /* libPods-Runner.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Runner.a"; sourceTree = BUILT_PRODUCTS_DIR; };
35 | 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; };
36 | 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; };
37 | 2F42C48AAC58E6A891FCF93A /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; };
38 | 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; };
39 | 5E7C7C8BEA8999B70296E77C /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; };
40 | 712BD8BE1FD6D50F937D6C7F /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; };
41 | 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; };
42 | 7AFFD8ED1D35381100E5BB4D /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; };
43 | 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; };
44 | 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; };
45 | 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; };
46 | 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; };
47 | 97C146F21CF9000F007C117D /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; };
48 | 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; };
49 | 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
50 | 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; };
51 | 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
52 | /* End PBXFileReference section */
53 |
54 | /* Begin PBXFrameworksBuildPhase section */
55 | 97C146EB1CF9000F007C117D /* Frameworks */ = {
56 | isa = PBXFrameworksBuildPhase;
57 | buildActionMask = 2147483647;
58 | files = (
59 | 22B0993FE1A8142609529A27 /* libPods-Runner.a in Frameworks */,
60 | );
61 | runOnlyForDeploymentPostprocessing = 0;
62 | };
63 | /* End PBXFrameworksBuildPhase section */
64 |
65 | /* Begin PBXGroup section */
66 | 9740EEB11CF90186004384FC /* Flutter */ = {
67 | isa = PBXGroup;
68 | children = (
69 | 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */,
70 | 9740EEB21CF90195004384FC /* Debug.xcconfig */,
71 | 7AFA3C8E1D35360C0083082E /* Release.xcconfig */,
72 | 9740EEB31CF90195004384FC /* Generated.xcconfig */,
73 | );
74 | name = Flutter;
75 | sourceTree = "";
76 | };
77 | 97C146E51CF9000F007C117D = {
78 | isa = PBXGroup;
79 | children = (
80 | 9740EEB11CF90186004384FC /* Flutter */,
81 | 97C146F01CF9000F007C117D /* Runner */,
82 | 97C146EF1CF9000F007C117D /* Products */,
83 | F65B27B078732A68DA518C24 /* Pods */,
84 | D1D8283238D29F69C25A8514 /* Frameworks */,
85 | );
86 | sourceTree = "";
87 | };
88 | 97C146EF1CF9000F007C117D /* Products */ = {
89 | isa = PBXGroup;
90 | children = (
91 | 97C146EE1CF9000F007C117D /* Runner.app */,
92 | );
93 | name = Products;
94 | sourceTree = "";
95 | };
96 | 97C146F01CF9000F007C117D /* Runner */ = {
97 | isa = PBXGroup;
98 | children = (
99 | 7AFFD8ED1D35381100E5BB4D /* AppDelegate.h */,
100 | 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */,
101 | 97C146FA1CF9000F007C117D /* Main.storyboard */,
102 | 97C146FD1CF9000F007C117D /* Assets.xcassets */,
103 | 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */,
104 | 97C147021CF9000F007C117D /* Info.plist */,
105 | 97C146F11CF9000F007C117D /* Supporting Files */,
106 | 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */,
107 | 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */,
108 | );
109 | path = Runner;
110 | sourceTree = "";
111 | };
112 | 97C146F11CF9000F007C117D /* Supporting Files */ = {
113 | isa = PBXGroup;
114 | children = (
115 | 97C146F21CF9000F007C117D /* main.m */,
116 | );
117 | name = "Supporting Files";
118 | sourceTree = "";
119 | };
120 | D1D8283238D29F69C25A8514 /* Frameworks */ = {
121 | isa = PBXGroup;
122 | children = (
123 | 0B5F01449A1B301F58B80EC3 /* libPods-Runner.a */,
124 | );
125 | name = Frameworks;
126 | sourceTree = "";
127 | };
128 | F65B27B078732A68DA518C24 /* Pods */ = {
129 | isa = PBXGroup;
130 | children = (
131 | 712BD8BE1FD6D50F937D6C7F /* Pods-Runner.debug.xcconfig */,
132 | 2F42C48AAC58E6A891FCF93A /* Pods-Runner.release.xcconfig */,
133 | 5E7C7C8BEA8999B70296E77C /* Pods-Runner.profile.xcconfig */,
134 | );
135 | path = Pods;
136 | sourceTree = "";
137 | };
138 | /* End PBXGroup section */
139 |
140 | /* Begin PBXNativeTarget section */
141 | 97C146ED1CF9000F007C117D /* Runner */ = {
142 | isa = PBXNativeTarget;
143 | buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */;
144 | buildPhases = (
145 | 9BBA38C4201FA0D419BA3140 /* [CP] Check Pods Manifest.lock */,
146 | 9740EEB61CF901F6004384FC /* Run Script */,
147 | 97C146EA1CF9000F007C117D /* Sources */,
148 | 97C146EB1CF9000F007C117D /* Frameworks */,
149 | 97C146EC1CF9000F007C117D /* Resources */,
150 | 9705A1C41CF9048500538489 /* Embed Frameworks */,
151 | 3B06AD1E1E4923F5004D2608 /* Thin Binary */,
152 | );
153 | buildRules = (
154 | );
155 | dependencies = (
156 | );
157 | name = Runner;
158 | productName = Runner;
159 | productReference = 97C146EE1CF9000F007C117D /* Runner.app */;
160 | productType = "com.apple.product-type.application";
161 | };
162 | /* End PBXNativeTarget section */
163 |
164 | /* Begin PBXProject section */
165 | 97C146E61CF9000F007C117D /* Project object */ = {
166 | isa = PBXProject;
167 | attributes = {
168 | LastUpgradeCheck = 1020;
169 | ORGANIZATIONNAME = "";
170 | TargetAttributes = {
171 | 97C146ED1CF9000F007C117D = {
172 | CreatedOnToolsVersion = 7.3.1;
173 | };
174 | };
175 | };
176 | buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */;
177 | compatibilityVersion = "Xcode 9.3";
178 | developmentRegion = en;
179 | hasScannedForEncodings = 0;
180 | knownRegions = (
181 | en,
182 | Base,
183 | );
184 | mainGroup = 97C146E51CF9000F007C117D;
185 | productRefGroup = 97C146EF1CF9000F007C117D /* Products */;
186 | projectDirPath = "";
187 | projectRoot = "";
188 | targets = (
189 | 97C146ED1CF9000F007C117D /* Runner */,
190 | );
191 | };
192 | /* End PBXProject section */
193 |
194 | /* Begin PBXResourcesBuildPhase section */
195 | 97C146EC1CF9000F007C117D /* Resources */ = {
196 | isa = PBXResourcesBuildPhase;
197 | buildActionMask = 2147483647;
198 | files = (
199 | 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */,
200 | 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */,
201 | 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */,
202 | 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */,
203 | );
204 | runOnlyForDeploymentPostprocessing = 0;
205 | };
206 | /* End PBXResourcesBuildPhase section */
207 |
208 | /* Begin PBXShellScriptBuildPhase section */
209 | 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = {
210 | isa = PBXShellScriptBuildPhase;
211 | buildActionMask = 2147483647;
212 | files = (
213 | );
214 | inputPaths = (
215 | );
216 | name = "Thin Binary";
217 | outputPaths = (
218 | );
219 | runOnlyForDeploymentPostprocessing = 0;
220 | shellPath = /bin/sh;
221 | shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin";
222 | };
223 | 9740EEB61CF901F6004384FC /* Run Script */ = {
224 | isa = PBXShellScriptBuildPhase;
225 | buildActionMask = 2147483647;
226 | files = (
227 | );
228 | inputPaths = (
229 | );
230 | name = "Run Script";
231 | outputPaths = (
232 | );
233 | runOnlyForDeploymentPostprocessing = 0;
234 | shellPath = /bin/sh;
235 | shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build";
236 | };
237 | 9BBA38C4201FA0D419BA3140 /* [CP] Check Pods Manifest.lock */ = {
238 | isa = PBXShellScriptBuildPhase;
239 | buildActionMask = 2147483647;
240 | files = (
241 | );
242 | inputFileListPaths = (
243 | );
244 | inputPaths = (
245 | "${PODS_PODFILE_DIR_PATH}/Podfile.lock",
246 | "${PODS_ROOT}/Manifest.lock",
247 | );
248 | name = "[CP] Check Pods Manifest.lock";
249 | outputFileListPaths = (
250 | );
251 | outputPaths = (
252 | "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt",
253 | );
254 | runOnlyForDeploymentPostprocessing = 0;
255 | shellPath = /bin/sh;
256 | shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
257 | showEnvVarsInLog = 0;
258 | };
259 | /* End PBXShellScriptBuildPhase section */
260 |
261 | /* Begin PBXSourcesBuildPhase section */
262 | 97C146EA1CF9000F007C117D /* Sources */ = {
263 | isa = PBXSourcesBuildPhase;
264 | buildActionMask = 2147483647;
265 | files = (
266 | 978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */,
267 | 97C146F31CF9000F007C117D /* main.m in Sources */,
268 | 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */,
269 | );
270 | runOnlyForDeploymentPostprocessing = 0;
271 | };
272 | /* End PBXSourcesBuildPhase section */
273 |
274 | /* Begin PBXVariantGroup section */
275 | 97C146FA1CF9000F007C117D /* Main.storyboard */ = {
276 | isa = PBXVariantGroup;
277 | children = (
278 | 97C146FB1CF9000F007C117D /* Base */,
279 | );
280 | name = Main.storyboard;
281 | sourceTree = "";
282 | };
283 | 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = {
284 | isa = PBXVariantGroup;
285 | children = (
286 | 97C147001CF9000F007C117D /* Base */,
287 | );
288 | name = LaunchScreen.storyboard;
289 | sourceTree = "";
290 | };
291 | /* End PBXVariantGroup section */
292 |
293 | /* Begin XCBuildConfiguration section */
294 | 249021D3217E4FDB00AE95B9 /* Profile */ = {
295 | isa = XCBuildConfiguration;
296 | buildSettings = {
297 | ALWAYS_SEARCH_USER_PATHS = NO;
298 | CLANG_ANALYZER_NONNULL = YES;
299 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
300 | CLANG_CXX_LIBRARY = "libc++";
301 | CLANG_ENABLE_MODULES = YES;
302 | CLANG_ENABLE_OBJC_ARC = YES;
303 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
304 | CLANG_WARN_BOOL_CONVERSION = YES;
305 | CLANG_WARN_COMMA = YES;
306 | CLANG_WARN_CONSTANT_CONVERSION = YES;
307 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
308 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
309 | CLANG_WARN_EMPTY_BODY = YES;
310 | CLANG_WARN_ENUM_CONVERSION = YES;
311 | CLANG_WARN_INFINITE_RECURSION = YES;
312 | CLANG_WARN_INT_CONVERSION = YES;
313 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
314 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
315 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
316 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
317 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
318 | CLANG_WARN_STRICT_PROTOTYPES = YES;
319 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
320 | CLANG_WARN_UNREACHABLE_CODE = YES;
321 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
322 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
323 | COPY_PHASE_STRIP = NO;
324 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
325 | ENABLE_NS_ASSERTIONS = NO;
326 | ENABLE_STRICT_OBJC_MSGSEND = YES;
327 | GCC_C_LANGUAGE_STANDARD = gnu99;
328 | GCC_NO_COMMON_BLOCKS = YES;
329 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
330 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
331 | GCC_WARN_UNDECLARED_SELECTOR = YES;
332 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
333 | GCC_WARN_UNUSED_FUNCTION = YES;
334 | GCC_WARN_UNUSED_VARIABLE = YES;
335 | IPHONEOS_DEPLOYMENT_TARGET = 9.0;
336 | MTL_ENABLE_DEBUG_INFO = NO;
337 | SDKROOT = iphoneos;
338 | SUPPORTED_PLATFORMS = iphoneos;
339 | TARGETED_DEVICE_FAMILY = "1,2";
340 | VALIDATE_PRODUCT = YES;
341 | };
342 | name = Profile;
343 | };
344 | 249021D4217E4FDB00AE95B9 /* Profile */ = {
345 | isa = XCBuildConfiguration;
346 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
347 | buildSettings = {
348 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
349 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
350 | DEVELOPMENT_TEAM = L4674B5247;
351 | ENABLE_BITCODE = NO;
352 | INFOPLIST_FILE = Runner/Info.plist;
353 | LD_RUNPATH_SEARCH_PATHS = (
354 | "$(inherited)",
355 | "@executable_path/Frameworks",
356 | );
357 | PRODUCT_BUNDLE_IDENTIFIER = icu.bughub.plugins.videoplayer.fltVideoPlayerExample;
358 | PRODUCT_NAME = "$(TARGET_NAME)";
359 | VERSIONING_SYSTEM = "apple-generic";
360 | };
361 | name = Profile;
362 | };
363 | 97C147031CF9000F007C117D /* Debug */ = {
364 | isa = XCBuildConfiguration;
365 | buildSettings = {
366 | ALWAYS_SEARCH_USER_PATHS = NO;
367 | CLANG_ANALYZER_NONNULL = YES;
368 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
369 | CLANG_CXX_LIBRARY = "libc++";
370 | CLANG_ENABLE_MODULES = YES;
371 | CLANG_ENABLE_OBJC_ARC = YES;
372 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
373 | CLANG_WARN_BOOL_CONVERSION = YES;
374 | CLANG_WARN_COMMA = YES;
375 | CLANG_WARN_CONSTANT_CONVERSION = YES;
376 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
377 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
378 | CLANG_WARN_EMPTY_BODY = YES;
379 | CLANG_WARN_ENUM_CONVERSION = YES;
380 | CLANG_WARN_INFINITE_RECURSION = YES;
381 | CLANG_WARN_INT_CONVERSION = YES;
382 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
383 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
384 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
385 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
386 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
387 | CLANG_WARN_STRICT_PROTOTYPES = YES;
388 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
389 | CLANG_WARN_UNREACHABLE_CODE = YES;
390 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
391 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
392 | COPY_PHASE_STRIP = NO;
393 | DEBUG_INFORMATION_FORMAT = dwarf;
394 | ENABLE_STRICT_OBJC_MSGSEND = YES;
395 | ENABLE_TESTABILITY = YES;
396 | GCC_C_LANGUAGE_STANDARD = gnu99;
397 | GCC_DYNAMIC_NO_PIC = NO;
398 | GCC_NO_COMMON_BLOCKS = YES;
399 | GCC_OPTIMIZATION_LEVEL = 0;
400 | GCC_PREPROCESSOR_DEFINITIONS = (
401 | "DEBUG=1",
402 | "$(inherited)",
403 | );
404 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
405 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
406 | GCC_WARN_UNDECLARED_SELECTOR = YES;
407 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
408 | GCC_WARN_UNUSED_FUNCTION = YES;
409 | GCC_WARN_UNUSED_VARIABLE = YES;
410 | IPHONEOS_DEPLOYMENT_TARGET = 9.0;
411 | MTL_ENABLE_DEBUG_INFO = YES;
412 | ONLY_ACTIVE_ARCH = YES;
413 | SDKROOT = iphoneos;
414 | TARGETED_DEVICE_FAMILY = "1,2";
415 | };
416 | name = Debug;
417 | };
418 | 97C147041CF9000F007C117D /* Release */ = {
419 | isa = XCBuildConfiguration;
420 | buildSettings = {
421 | ALWAYS_SEARCH_USER_PATHS = NO;
422 | CLANG_ANALYZER_NONNULL = YES;
423 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
424 | CLANG_CXX_LIBRARY = "libc++";
425 | CLANG_ENABLE_MODULES = YES;
426 | CLANG_ENABLE_OBJC_ARC = YES;
427 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
428 | CLANG_WARN_BOOL_CONVERSION = YES;
429 | CLANG_WARN_COMMA = YES;
430 | CLANG_WARN_CONSTANT_CONVERSION = YES;
431 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
432 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
433 | CLANG_WARN_EMPTY_BODY = YES;
434 | CLANG_WARN_ENUM_CONVERSION = YES;
435 | CLANG_WARN_INFINITE_RECURSION = YES;
436 | CLANG_WARN_INT_CONVERSION = YES;
437 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
438 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
439 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
440 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
441 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
442 | CLANG_WARN_STRICT_PROTOTYPES = YES;
443 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
444 | CLANG_WARN_UNREACHABLE_CODE = YES;
445 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
446 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
447 | COPY_PHASE_STRIP = NO;
448 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
449 | ENABLE_NS_ASSERTIONS = NO;
450 | ENABLE_STRICT_OBJC_MSGSEND = YES;
451 | GCC_C_LANGUAGE_STANDARD = gnu99;
452 | GCC_NO_COMMON_BLOCKS = YES;
453 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
454 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
455 | GCC_WARN_UNDECLARED_SELECTOR = YES;
456 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
457 | GCC_WARN_UNUSED_FUNCTION = YES;
458 | GCC_WARN_UNUSED_VARIABLE = YES;
459 | IPHONEOS_DEPLOYMENT_TARGET = 9.0;
460 | MTL_ENABLE_DEBUG_INFO = NO;
461 | SDKROOT = iphoneos;
462 | SUPPORTED_PLATFORMS = iphoneos;
463 | TARGETED_DEVICE_FAMILY = "1,2";
464 | VALIDATE_PRODUCT = YES;
465 | };
466 | name = Release;
467 | };
468 | 97C147061CF9000F007C117D /* Debug */ = {
469 | isa = XCBuildConfiguration;
470 | baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;
471 | buildSettings = {
472 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
473 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
474 | DEVELOPMENT_TEAM = L4674B5247;
475 | ENABLE_BITCODE = NO;
476 | INFOPLIST_FILE = Runner/Info.plist;
477 | LD_RUNPATH_SEARCH_PATHS = (
478 | "$(inherited)",
479 | "@executable_path/Frameworks",
480 | );
481 | PRODUCT_BUNDLE_IDENTIFIER = icu.bughub.plugins.videoplayer.fltVideoPlayerExample;
482 | PRODUCT_NAME = "$(TARGET_NAME)";
483 | VERSIONING_SYSTEM = "apple-generic";
484 | };
485 | name = Debug;
486 | };
487 | 97C147071CF9000F007C117D /* Release */ = {
488 | isa = XCBuildConfiguration;
489 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
490 | buildSettings = {
491 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
492 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
493 | DEVELOPMENT_TEAM = L4674B5247;
494 | ENABLE_BITCODE = NO;
495 | INFOPLIST_FILE = Runner/Info.plist;
496 | LD_RUNPATH_SEARCH_PATHS = (
497 | "$(inherited)",
498 | "@executable_path/Frameworks",
499 | );
500 | PRODUCT_BUNDLE_IDENTIFIER = icu.bughub.plugins.videoplayer.fltVideoPlayerExample;
501 | PRODUCT_NAME = "$(TARGET_NAME)";
502 | VERSIONING_SYSTEM = "apple-generic";
503 | };
504 | name = Release;
505 | };
506 | /* End XCBuildConfiguration section */
507 |
508 | /* Begin XCConfigurationList section */
509 | 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = {
510 | isa = XCConfigurationList;
511 | buildConfigurations = (
512 | 97C147031CF9000F007C117D /* Debug */,
513 | 97C147041CF9000F007C117D /* Release */,
514 | 249021D3217E4FDB00AE95B9 /* Profile */,
515 | );
516 | defaultConfigurationIsVisible = 0;
517 | defaultConfigurationName = Release;
518 | };
519 | 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = {
520 | isa = XCConfigurationList;
521 | buildConfigurations = (
522 | 97C147061CF9000F007C117D /* Debug */,
523 | 97C147071CF9000F007C117D /* Release */,
524 | 249021D4217E4FDB00AE95B9 /* Profile */,
525 | );
526 | defaultConfigurationIsVisible = 0;
527 | defaultConfigurationName = Release;
528 | };
529 | /* End XCConfigurationList section */
530 | };
531 | rootObject = 97C146E61CF9000F007C117D /* Project object */;
532 | }
533 |
--------------------------------------------------------------------------------
/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | PreviewsEnabled
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
32 |
33 |
39 |
40 |
41 |
42 |
43 |
44 |
54 |
56 |
62 |
63 |
64 |
65 |
66 |
67 |
73 |
75 |
81 |
82 |
83 |
84 |
86 |
87 |
90 |
91 |
92 |
--------------------------------------------------------------------------------
/example/ios/Runner.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | PreviewsEnabled
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/example/ios/Runner/AppDelegate.h:
--------------------------------------------------------------------------------
1 | #import
2 | #import
3 |
4 | @interface AppDelegate : FlutterAppDelegate
5 |
6 | @end
7 |
--------------------------------------------------------------------------------
/example/ios/Runner/AppDelegate.m:
--------------------------------------------------------------------------------
1 | #import "AppDelegate.h"
2 | #import "GeneratedPluginRegistrant.h"
3 |
4 | @implementation AppDelegate
5 |
6 | - (BOOL)application:(UIApplication *)application
7 | didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
8 | [GeneratedPluginRegistrant registerWithRegistry:self];
9 | // Override point for customization after application launch.
10 | return [super application:application didFinishLaunchingWithOptions:launchOptions];
11 | }
12 |
13 | @end
14 |
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "size" : "20x20",
5 | "idiom" : "iphone",
6 | "filename" : "Icon-App-20x20@2x.png",
7 | "scale" : "2x"
8 | },
9 | {
10 | "size" : "20x20",
11 | "idiom" : "iphone",
12 | "filename" : "Icon-App-20x20@3x.png",
13 | "scale" : "3x"
14 | },
15 | {
16 | "size" : "29x29",
17 | "idiom" : "iphone",
18 | "filename" : "Icon-App-29x29@1x.png",
19 | "scale" : "1x"
20 | },
21 | {
22 | "size" : "29x29",
23 | "idiom" : "iphone",
24 | "filename" : "Icon-App-29x29@2x.png",
25 | "scale" : "2x"
26 | },
27 | {
28 | "size" : "29x29",
29 | "idiom" : "iphone",
30 | "filename" : "Icon-App-29x29@3x.png",
31 | "scale" : "3x"
32 | },
33 | {
34 | "size" : "40x40",
35 | "idiom" : "iphone",
36 | "filename" : "Icon-App-40x40@2x.png",
37 | "scale" : "2x"
38 | },
39 | {
40 | "size" : "40x40",
41 | "idiom" : "iphone",
42 | "filename" : "Icon-App-40x40@3x.png",
43 | "scale" : "3x"
44 | },
45 | {
46 | "size" : "60x60",
47 | "idiom" : "iphone",
48 | "filename" : "Icon-App-60x60@2x.png",
49 | "scale" : "2x"
50 | },
51 | {
52 | "size" : "60x60",
53 | "idiom" : "iphone",
54 | "filename" : "Icon-App-60x60@3x.png",
55 | "scale" : "3x"
56 | },
57 | {
58 | "size" : "20x20",
59 | "idiom" : "ipad",
60 | "filename" : "Icon-App-20x20@1x.png",
61 | "scale" : "1x"
62 | },
63 | {
64 | "size" : "20x20",
65 | "idiom" : "ipad",
66 | "filename" : "Icon-App-20x20@2x.png",
67 | "scale" : "2x"
68 | },
69 | {
70 | "size" : "29x29",
71 | "idiom" : "ipad",
72 | "filename" : "Icon-App-29x29@1x.png",
73 | "scale" : "1x"
74 | },
75 | {
76 | "size" : "29x29",
77 | "idiom" : "ipad",
78 | "filename" : "Icon-App-29x29@2x.png",
79 | "scale" : "2x"
80 | },
81 | {
82 | "size" : "40x40",
83 | "idiom" : "ipad",
84 | "filename" : "Icon-App-40x40@1x.png",
85 | "scale" : "1x"
86 | },
87 | {
88 | "size" : "40x40",
89 | "idiom" : "ipad",
90 | "filename" : "Icon-App-40x40@2x.png",
91 | "scale" : "2x"
92 | },
93 | {
94 | "size" : "76x76",
95 | "idiom" : "ipad",
96 | "filename" : "Icon-App-76x76@1x.png",
97 | "scale" : "1x"
98 | },
99 | {
100 | "size" : "76x76",
101 | "idiom" : "ipad",
102 | "filename" : "Icon-App-76x76@2x.png",
103 | "scale" : "2x"
104 | },
105 | {
106 | "size" : "83.5x83.5",
107 | "idiom" : "ipad",
108 | "filename" : "Icon-App-83.5x83.5@2x.png",
109 | "scale" : "2x"
110 | },
111 | {
112 | "size" : "1024x1024",
113 | "idiom" : "ios-marketing",
114 | "filename" : "Icon-App-1024x1024@1x.png",
115 | "scale" : "1x"
116 | }
117 | ],
118 | "info" : {
119 | "version" : 1,
120 | "author" : "xcode"
121 | }
122 | }
123 |
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RandyWei/flt_video_player/fa8d074a73c354362459d2106b68e0f0882088e4/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RandyWei/flt_video_player/fa8d074a73c354362459d2106b68e0f0882088e4/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RandyWei/flt_video_player/fa8d074a73c354362459d2106b68e0f0882088e4/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RandyWei/flt_video_player/fa8d074a73c354362459d2106b68e0f0882088e4/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RandyWei/flt_video_player/fa8d074a73c354362459d2106b68e0f0882088e4/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RandyWei/flt_video_player/fa8d074a73c354362459d2106b68e0f0882088e4/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RandyWei/flt_video_player/fa8d074a73c354362459d2106b68e0f0882088e4/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RandyWei/flt_video_player/fa8d074a73c354362459d2106b68e0f0882088e4/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RandyWei/flt_video_player/fa8d074a73c354362459d2106b68e0f0882088e4/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RandyWei/flt_video_player/fa8d074a73c354362459d2106b68e0f0882088e4/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RandyWei/flt_video_player/fa8d074a73c354362459d2106b68e0f0882088e4/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RandyWei/flt_video_player/fa8d074a73c354362459d2106b68e0f0882088e4/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RandyWei/flt_video_player/fa8d074a73c354362459d2106b68e0f0882088e4/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RandyWei/flt_video_player/fa8d074a73c354362459d2106b68e0f0882088e4/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RandyWei/flt_video_player/fa8d074a73c354362459d2106b68e0f0882088e4/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "LaunchImage.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "filename" : "LaunchImage@2x.png",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "universal",
15 | "filename" : "LaunchImage@3x.png",
16 | "scale" : "3x"
17 | }
18 | ],
19 | "info" : {
20 | "version" : 1,
21 | "author" : "xcode"
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RandyWei/flt_video_player/fa8d074a73c354362459d2106b68e0f0882088e4/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RandyWei/flt_video_player/fa8d074a73c354362459d2106b68e0f0882088e4/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RandyWei/flt_video_player/fa8d074a73c354362459d2106b68e0f0882088e4/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md:
--------------------------------------------------------------------------------
1 | # Launch Screen Assets
2 |
3 | You can customize the launch screen with your own desired assets by replacing the image files in this directory.
4 |
5 | You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images.
--------------------------------------------------------------------------------
/example/ios/Runner/Base.lproj/LaunchScreen.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
--------------------------------------------------------------------------------
/example/ios/Runner/Base.lproj/Main.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/example/ios/Runner/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | flt_video_player_example
15 | CFBundlePackageType
16 | APPL
17 | CFBundleShortVersionString
18 | $(FLUTTER_BUILD_NAME)
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | $(FLUTTER_BUILD_NUMBER)
23 | LSRequiresIPhoneOS
24 |
25 | UILaunchStoryboardName
26 | LaunchScreen
27 | UIMainStoryboardFile
28 | Main
29 | UISupportedInterfaceOrientations
30 |
31 | UIInterfaceOrientationPortrait
32 | UIInterfaceOrientationLandscapeLeft
33 | UIInterfaceOrientationLandscapeRight
34 |
35 | UISupportedInterfaceOrientations~ipad
36 |
37 | UIInterfaceOrientationPortrait
38 | UIInterfaceOrientationPortraitUpsideDown
39 | UIInterfaceOrientationLandscapeLeft
40 | UIInterfaceOrientationLandscapeRight
41 |
42 | UIViewControllerBasedStatusBarAppearance
43 |
44 |
45 |
46 |
--------------------------------------------------------------------------------
/example/ios/Runner/main.m:
--------------------------------------------------------------------------------
1 | #import
2 | #import
3 | #import "AppDelegate.h"
4 |
5 | int main(int argc, char* argv[]) {
6 | @autoreleasepool {
7 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/example/lib/define/function_define.dart:
--------------------------------------------------------------------------------
1 | ///
2 | /// Created by wei on 2021/11/11.
3 | ///
4 |
5 | typedef ControlButtonCallback = Function(T t);
--------------------------------------------------------------------------------
/example/lib/extensions/extension.dart:
--------------------------------------------------------------------------------
1 | ///
2 | /// Created by wei on 2021/11/11.
3 | ///
4 |
5 | extension BoolExtension on bool {
6 | bool toggle() {
7 | return !this;
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/example/lib/full/full_player.dart:
--------------------------------------------------------------------------------
1 | ///
2 | /// Created by wei on 2021/11/11.
3 | ///
4 |
5 | import 'package:flt_video_player/flt_video_player.dart';
6 | import 'package:flt_video_player_example/full/player_control_overlay.dart';
7 | import 'package:flutter/material.dart';
8 | import "package:flutter/widgets.dart";
9 |
10 | class FullPlayer extends StatefulWidget {
11 | const FullPlayer(
12 | {Key? key,
13 | required this.controller,
14 | this.title,
15 | this.playCall,
16 | this.aspectRatio = 16 / 9,
17 | this.coverUrl})
18 | : super(key: key);
19 |
20 | final double aspectRatio;
21 | final String? coverUrl;
22 | final String? title;
23 | final Function? playCall;
24 | final VodPlayerController controller;
25 |
26 | @override
27 | _FullPlayerState createState() => _FullPlayerState();
28 | }
29 |
30 | class _FullPlayerState extends State {
31 | @override
32 | Widget build(BuildContext context) {
33 | return Stack(
34 | fit: StackFit.expand,
35 | children: [
36 | VodPlayer(controller: widget.controller),
37 | ControlOverlay(
38 | controller: widget.controller,
39 | callPlay: widget.playCall,
40 | coverUrl: widget.coverUrl,
41 | title: widget.title,
42 | )
43 | ],
44 | );
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/example/lib/full/full_player_demo.dart:
--------------------------------------------------------------------------------
1 | ///
2 | /// Created by wei on 2021/11/11.
3 | ///
4 | import 'package:flt_video_player/flt_video_player.dart';
5 | import 'package:flt_video_player_example/full/full_player.dart';
6 | import 'package:flutter/material.dart';
7 | import "package:flutter/widgets.dart";
8 |
9 | class FullPlayerDemo extends StatefulWidget {
10 | const FullPlayerDemo({Key? key}) : super(key: key);
11 |
12 | @override
13 | _FullPlayerDemoState createState() => _FullPlayerDemoState();
14 | }
15 |
16 | class _FullPlayerDemoState extends State {
17 | late VodPlayerController controller;
18 | double _aspectRation = 16 / 9;
19 |
20 | @override
21 | void initState() {
22 | super.initState();
23 | var playerConfig = PlayerConfig();
24 | playerConfig.headers = {"Referer": "https://videoadmin.chinahrt.com"};
25 |
26 | controller = VodPlayerController(config: playerConfig);
27 |
28 | //监听播放状态
29 | controller.playState.listen((event) {
30 | debugPrint("playerState:$event");
31 | });
32 |
33 | controller.onNetEvent.listen((event) {
34 | //获取视频宽度高度
35 | double w = (event["VIDEO_WIDTH"]).toDouble();
36 | double h = (event["VIDEO_HEIGHT"]).toDouble();
37 |
38 | //计算比例
39 | if (w > 0 && h > 0) {
40 | _aspectRation = 1.0 * w / h;
41 | setState(() {});
42 | }
43 | });
44 |
45 | controller.initialize();
46 | }
47 |
48 | @override
49 | Widget build(BuildContext context) {
50 | return OrientationBuilder(builder: (context, orientation) {
51 | return Scaffold(
52 | appBar: orientation == Orientation.portrait
53 | ? AppBar(
54 | title: const Text("Full Player Demo"),
55 | )
56 | : null,
57 | body: orientation == Orientation.portrait
58 | ? Column(
59 | children: [
60 | AspectRatio(
61 | aspectRatio: _aspectRation,
62 | child: buildFullPlayer(),
63 | )
64 | ],
65 | )
66 | : Container(
67 | constraints: const BoxConstraints.expand(),
68 | child: buildFullPlayer(),
69 | ),
70 | );
71 | });
72 | }
73 |
74 | FullPlayer buildFullPlayer() {
75 | return FullPlayer(
76 | playCall: () {
77 | if (controller.value.state == PlayerState.stopped) {
78 | controller.play(
79 | "https://look.chinahrt.com.cn/courseyun/rxsl2content/transcode/20211/be3b6935-f678-4303-a1f8-b2a006352656/283006-mp4.mp4");
80 | } else {
81 | controller.resume();
82 | }
83 | },
84 | controller: controller,
85 | aspectRatio: _aspectRation,
86 | title: "测试视频",
87 | coverUrl:
88 | "https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fimg.jj20.com%2Fup%2Fallimg%2F1114%2F060421091316%2F210604091316-6-1200.jpg&refer=http%3A%2F%2Fimg.jj20.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1639190767&t=831d19c414f872da0b3cf565b3019bfd",
89 | );
90 | }
91 |
92 | @override
93 | void dispose() {
94 | controller.dispose();
95 | super.dispose();
96 | }
97 | }
98 |
--------------------------------------------------------------------------------
/example/lib/full/full_screen_button.dart:
--------------------------------------------------------------------------------
1 | ///
2 | /// Created by wei on 2021/11/11.
3 | ///
4 | import 'package:flt_video_player_example/define/function_define.dart';
5 | import 'package:flutter/material.dart';
6 | import "package:flutter/widgets.dart";
7 |
8 | class FullScreenButton extends StatefulWidget {
9 | const FullScreenButton({Key? key, this.callback}) : super(key: key);
10 |
11 | final ControlButtonCallback? callback;
12 |
13 | @override
14 | _FullScreenButtonState createState() => _FullScreenButtonState();
15 | }
16 |
17 | class _FullScreenButtonState extends State {
18 | bool _full = false;
19 |
20 | @override
21 | Widget build(BuildContext context) {
22 | return InkWell(
23 | child: Image.asset(
24 | _full
25 | ? "resources/arrow.down.right.and.arrow.up.left.circle.png"
26 | : "resources/arrow.up.backward.and.arrow.down.forward.circle.png",
27 | ),
28 | onTap: () {
29 | _full = !_full;
30 | setState(() {});
31 | widget.callback?.call(_full);
32 | },
33 | );
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/example/lib/full/mute_button.dart:
--------------------------------------------------------------------------------
1 | ///
2 | /// Created by wei on 2021/11/11.
3 | ///
4 | import 'package:flt_video_player_example/define/function_define.dart';
5 | import 'package:flutter/material.dart';
6 | import "package:flutter/widgets.dart";
7 |
8 | class MuteButton extends StatefulWidget {
9 | const MuteButton({Key? key,this.callback}) : super(key: key);
10 |
11 | final ControlButtonCallback? callback;
12 | @override
13 | _MuteButtonState createState() => _MuteButtonState();
14 | }
15 |
16 | class _MuteButtonState extends State {
17 | bool _mute = false;
18 |
19 | @override
20 | Widget build(BuildContext context) {
21 | return InkWell(
22 | child: Image.asset(
23 | _mute ? "resources/speaker.slash.circle.png" : "resources/speaker.circle.png",
24 | ),
25 | onTap: () {
26 | _mute = !_mute;
27 | setState(() {});
28 | widget.callback?.call(_mute);
29 | },
30 | );
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/example/lib/full/play_button.dart:
--------------------------------------------------------------------------------
1 | ///
2 | /// Created by wei on 2021/11/11.
3 | ///
4 |
5 | import 'package:flt_video_player_example/define/function_define.dart';
6 | import 'package:flutter/material.dart';
7 | import "package:flutter/widgets.dart";
8 |
9 | class PlayButton extends StatefulWidget {
10 | const PlayButton({Key? key, required this.isPlaying, this.callback})
11 | : super(key: key);
12 |
13 | final ControlButtonCallback? callback;
14 | final bool isPlaying;
15 |
16 | @override
17 | _PlayButtonState createState() => _PlayButtonState();
18 | }
19 |
20 | class _PlayButtonState extends State {
21 | bool _isPlaying = false;
22 |
23 | @override
24 | void initState() {
25 | super.initState();
26 | _isPlaying = widget.isPlaying;
27 | }
28 |
29 | @override
30 | Widget build(BuildContext context) {
31 | return InkWell(
32 | child: Image.asset(
33 | _isPlaying ? "resources/pause_circle.png" : "resources/play_circle.png",
34 | ),
35 | onTap: () {
36 | widget.callback?.call(_isPlaying);
37 | _isPlaying = !_isPlaying;
38 | setState(() {});
39 | },
40 | );
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/example/lib/full/player_control_overlay.dart:
--------------------------------------------------------------------------------
1 | ///
2 | /// Created by wei on 2021/11/11.
3 | ///
4 | import 'dart:async';
5 | import 'dart:io';
6 |
7 | import 'package:flt_video_player/flt_video_player.dart';
8 | import 'package:flt_video_player_example/full/full_screen_button.dart';
9 | import 'package:flt_video_player_example/full/play_button.dart';
10 | import 'package:flt_video_player_example/full/rate_button.dart';
11 | import 'package:flutter/material.dart';
12 | import 'package:flutter/services.dart';
13 | import 'package:orientation/orientation.dart';
14 |
15 | class ControlOverlay extends StatefulWidget {
16 | const ControlOverlay(
17 | {Key? key,
18 | required this.controller,
19 | this.callPlay,
20 | this.title,
21 | this.coverUrl})
22 | : super(key: key);
23 |
24 | final String? coverUrl;
25 | final String? title;
26 | final VodPlayerController controller;
27 |
28 | final Function? callPlay;
29 |
30 | @override
31 | _ControlOverlayState createState() => _ControlOverlayState();
32 | }
33 |
34 | class _ControlOverlayState extends State {
35 | bool _showLoading = false;
36 | bool _showCover = true;
37 | bool _showBigPlayButton = true;
38 |
39 | bool _showControlBar = false;
40 | final bool _showTitleBar = false;
41 |
42 | bool _isPlaying = false;
43 |
44 | Timer? _timer;
45 |
46 | double duration = 0;
47 | double progress = 0;
48 |
49 | @override
50 | void initState() {
51 | super.initState();
52 |
53 | var playState = widget.controller.value.state;
54 |
55 | if (playState != PlayerState.stopped) {
56 | _showCover = false;
57 | _showBigPlayButton = false;
58 | }
59 |
60 | widget.controller.playState.listen((PlayerState state) {
61 | debugPrint(state.toString());
62 | //状态一旦变化封面要隐藏
63 | _showLoading = false;
64 | _showBigPlayButton = true;
65 | _isPlaying = false;
66 | if (state == PlayerState.buffering) {
67 | _showLoading = true;
68 | _showBigPlayButton = false;
69 | } else if (state == PlayerState.playing) {
70 | _showBigPlayButton = false;
71 | _isPlaying = true;
72 | }
73 | setState(() {});
74 | });
75 |
76 | widget.controller.onPlayerEvent.listen((event) {
77 | switch (event["event"]) {
78 | case 2005:
79 | if (_showControlBar && mounted) {
80 | duration = event["EVT_PLAY_DURATION"] * 1.0;
81 | progress = event["EVT_PLAY_PROGRESS"] * 1.0;
82 | setState(() {});
83 | }
84 | break;
85 | }
86 | });
87 | }
88 |
89 | _switchScreenOrientation() {
90 | //屏幕旋转方向
91 | final List orientations = [];
92 | if (isPortraitUp) {
93 | if (Platform.isIOS) {
94 | orientations.add(DeviceOrientation.landscapeRight);
95 | SystemChrome.setPreferredOrientations(orientations);
96 | }
97 | OrientationPlugin.forceOrientation(DeviceOrientation.landscapeRight);
98 | } else {
99 | orientations.add(DeviceOrientation.portraitUp);
100 | //设置屏幕旋转方向
101 | SystemChrome.setPreferredOrientations(orientations);
102 | OrientationPlugin.forceOrientation(DeviceOrientation.portraitUp);
103 | }
104 | }
105 |
106 | get isPortraitUp {
107 | Size screenSize = MediaQueryData.fromView(View.of(context)).size;
108 | return screenSize.width < screenSize.height;
109 | }
110 |
111 | @override
112 | Widget build(BuildContext context) {
113 | return InkWell(
114 | onTap: () async {
115 | //如果当前状态非播放状态,不可呼出控制条
116 | if (!await widget.controller.isPlaying) return;
117 | _timer?.cancel();
118 | _timer = null;
119 | _timer = Timer(const Duration(seconds: 3), () {
120 | if (!mounted) return;
121 | _showControlBar = false;
122 | setState(() {});
123 | });
124 | _showControlBar = !_showControlBar;
125 | setState(() {});
126 | },
127 | child: Stack(
128 | children: [
129 | if (_showLoading) const Center(child: CircularProgressIndicator()),
130 |
131 | //封面
132 | if (_showCover && widget.coverUrl != null)
133 | Image.network(
134 | widget.coverUrl!,
135 | ),
136 |
137 | //大的播放按钮
138 | if (_showBigPlayButton)
139 | Center(
140 | child: SizedBox(
141 | width: 70,
142 | child: PlayButton(
143 | isPlaying: _isPlaying,
144 | callback: (isPlaying) {
145 | _showCover = false;
146 | setState(() {});
147 | widget.callPlay?.call();
148 | },
149 | ),
150 | ),
151 | ),
152 |
153 | if (_showControlBar)
154 | Column(
155 | mainAxisAlignment: MainAxisAlignment.spaceBetween,
156 | children: [
157 | _showTitleBar
158 | ? Container(
159 | padding: const EdgeInsets.only(left: 8),
160 | child: Align(
161 | alignment: Alignment.centerLeft,
162 | child: Text(
163 | widget.title ?? "",
164 | style: const TextStyle(
165 | color: Colors.white, fontSize: 12),
166 | ),
167 | ),
168 | color: Colors.black54,
169 | height: 30,
170 | )
171 | : Container(),
172 | Container(
173 | height: 35,
174 | color: Colors.black54,
175 | padding: const EdgeInsets.symmetric(horizontal: 8),
176 | child: Row(
177 | children: [
178 | SizedBox(
179 | width: 25,
180 | child: PlayButton(
181 | isPlaying: _isPlaying,
182 | callback: (isPlaying) {
183 | if (isPlaying) {
184 | widget.controller.pause();
185 | } else {
186 | widget.controller.resume();
187 | }
188 | },
189 | ),
190 | ),
191 | Expanded(
192 | child: Slider(
193 | activeColor: Colors.white,
194 | inactiveColor: Colors.grey,
195 | value: progress,
196 | onChanged: (value) {
197 | progress = value;
198 | },
199 | onChangeEnd: (value) {
200 | widget.controller.seek(value.toInt());
201 | },
202 | max: duration,
203 | ),
204 | ),
205 | Padding(
206 | padding: const EdgeInsets.symmetric(horizontal: 8),
207 | child: RateButton(
208 | controller: widget.controller,
209 | callback: (rate) {
210 | widget.controller.setRate(rate);
211 | },
212 | ),
213 | ),
214 | SizedBox(
215 | child: FullScreenButton(
216 | callback: (isFull) {
217 | _switchScreenOrientation();
218 | },
219 | ),
220 | width: 25,
221 | )
222 | ],
223 | ),
224 | )
225 | ],
226 | )
227 | ],
228 | ),
229 | );
230 | }
231 | }
232 |
--------------------------------------------------------------------------------
/example/lib/full/rate_button.dart:
--------------------------------------------------------------------------------
1 | ///
2 | /// Created by wei on 2021/11/11.
3 | ///
4 | import 'package:flt_video_player/flt_video_player.dart';
5 | import 'package:flt_video_player_example/define/function_define.dart';
6 | import 'package:flutter/material.dart';
7 |
8 | class RateButton extends StatefulWidget {
9 | const RateButton({Key? key, required this.controller, this.callback})
10 | : super(key: key);
11 |
12 | final ControlButtonCallback? callback;
13 |
14 | final VodPlayerController controller;
15 |
16 | @override
17 | _RateButtonState createState() => _RateButtonState();
18 | }
19 |
20 | class _RateButtonState extends State {
21 | final _rates = [1.0, 1.5, 1.75, 2.0];
22 | var _index = 0;
23 | double _rate = 1.0;
24 |
25 | @override
26 | void initState() {
27 | super.initState();
28 | _rate = widget.controller.rate;
29 | _index = _rates.indexWhere((element) => element == _rate);
30 | }
31 |
32 | @override
33 | Widget build(BuildContext context) {
34 | return InkWell(
35 | child: Text(
36 | "${_rate}x",
37 | style: const TextStyle(color: Colors.white),
38 | ),
39 | onTap: () {
40 | _index++;
41 | if (_index >= _rates.length) {
42 | _index = 0;
43 | }
44 | _rate = _rates[_index];
45 | widget.controller.setRate(_rate);
46 | setState(() {});
47 | widget.callback?.call(_rate);
48 | },
49 | );
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/example/lib/main.dart:
--------------------------------------------------------------------------------
1 | import 'package:flt_video_player_example/full/full_player_demo.dart';
2 | import 'package:flt_video_player_example/platform_view_demo.dart';
3 | import 'package:flt_video_player_example/simple_demo.dart';
4 | import 'package:flutter/material.dart';
5 |
6 | void main() {
7 | runApp(const MyApp());
8 | }
9 |
10 | class MyApp extends StatefulWidget {
11 | const MyApp({Key? key}) : super(key: key);
12 |
13 | @override
14 | State createState() => _MyAppState();
15 | }
16 |
17 | class _MyAppState extends State {
18 | @override
19 | Widget build(BuildContext context) {
20 | return MaterialApp(
21 | theme: ThemeData(platform: TargetPlatform.iOS),
22 | home: const HomePage(),
23 | );
24 | }
25 | }
26 |
27 | class HomePage extends StatefulWidget {
28 | const HomePage({Key? key}) : super(key: key);
29 |
30 | @override
31 | _HomePageState createState() => _HomePageState();
32 | }
33 |
34 | class _HomePageState extends State {
35 | @override
36 | Widget build(BuildContext context) {
37 | return Scaffold(
38 | appBar: AppBar(
39 | title: const Text('Plugin example app'),
40 | ),
41 | body: Center(
42 | child: Column(
43 | children: [
44 | ElevatedButton(
45 | onPressed: () {
46 | Navigator.of(context)
47 | .push(MaterialPageRoute(builder: (context) {
48 | return const SimpleDemo();
49 | }));
50 | },
51 | child: const Text("简单示例")),
52 | ElevatedButton(
53 | onPressed: () {
54 | Navigator.of(context)
55 | .push(MaterialPageRoute(builder: (context) {
56 | return const FullPlayerDemo();
57 | }));
58 | },
59 | child: const Text("完整示例")),
60 | ElevatedButton(
61 | onPressed: () {
62 | Navigator.of(context)
63 | .push(MaterialPageRoute(builder: (context) {
64 | return const PlatformViewDemo();
65 | }));
66 | },
67 | child: const Text("Platform View 示例")),
68 | ],
69 | ),
70 | ),
71 | );
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/example/lib/platform_view_demo.dart:
--------------------------------------------------------------------------------
1 | ///
2 | /// Created by wei on 2021/11/17.
3 | ///
4 | import 'package:flutter/material.dart';
5 | import "package:flutter/widgets.dart";
6 |
7 | class PlatformViewDemo extends StatefulWidget {
8 | const PlatformViewDemo({Key? key}) : super(key: key);
9 |
10 | @override
11 | _PlatformViewDemoState createState() => _PlatformViewDemoState();
12 | }
13 |
14 | class _PlatformViewDemoState extends State {
15 | @override
16 | Widget build(BuildContext context) {
17 | return Scaffold(
18 | appBar: AppBar(
19 | title: const Text("Platform View Demo"),
20 | ),
21 | body: Center(
22 | child: Column(
23 | children: const [
24 | AspectRatio(
25 | aspectRatio: 16 / 9,
26 | child: UiKitView(viewType: "FltVideoView")),
27 | AspectRatio(
28 | aspectRatio: 16 / 9, child: UiKitView(viewType: "FltVideoView"))
29 | ],
30 | ),
31 | ),
32 | );
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/example/lib/simple_demo.dart:
--------------------------------------------------------------------------------
1 | ///
2 | /// Created by wei on 2021/11/8.
3 | ///
4 | import 'package:flt_video_player/flt_video_player.dart';
5 | import 'package:flutter/material.dart';
6 | import "package:flutter/widgets.dart";
7 |
8 | class SimpleDemo extends StatefulWidget {
9 | const SimpleDemo({Key? key}) : super(key: key);
10 |
11 | @override
12 | _SimpleDemoState createState() => _SimpleDemoState();
13 | }
14 |
15 | class _SimpleDemoState extends State {
16 | late VodPlayerController controller;
17 | double _aspectRation = 16 / 9;
18 | late GlobalKey _videoViewKey;
19 |
20 | @override
21 | void initState() {
22 | super.initState();
23 |
24 | //用来保证视频 view 不会被刷新
25 | _videoViewKey = GlobalKey();
26 |
27 | var playerConfig = PlayerConfig();
28 | playerConfig.headers = {"Referer": "https://videoadmin.chinahrt.com"};
29 |
30 | controller = VodPlayerController(
31 | config: playerConfig, renderType: RenderType.platformView);
32 |
33 | //监听播放状态
34 | controller.playState.listen((event) {
35 | debugPrint("playerState:$event");
36 | });
37 |
38 | controller.onPlayerEvent.listen((event) {
39 | debugPrint("PlayerEvent:$event");
40 | });
41 |
42 | controller.onNetEvent.listen((event) {
43 | //获取视频宽度高度
44 | double w = (event["VIDEO_WIDTH"]).toDouble();
45 | double h = (event["VIDEO_HEIGHT"]).toDouble();
46 |
47 | //计算比例
48 | if (w > 0 && h > 0) {
49 | _aspectRation = 1.0 * w / h;
50 | setState(() {});
51 | }
52 | });
53 |
54 | controller.initialize();
55 | }
56 |
57 | @override
58 | Widget build(BuildContext context) {
59 | return Scaffold(
60 | appBar: AppBar(
61 | title: const Text('Plugin example app'),
62 | ),
63 | body: Center(
64 | child: Column(
65 | children: [
66 | AspectRatio(
67 | aspectRatio: _aspectRation,
68 | child: VodPlayer(
69 | key: _videoViewKey,
70 | controller: controller,
71 | ),
72 | ),
73 | ElevatedButton(
74 | onPressed: () {
75 | controller.seek(60);
76 | },
77 | child: Text("Seek"))
78 | ],
79 | ),
80 | ),
81 | floatingActionButton: FloatingActionButton(
82 | onPressed: () {
83 | //https://hwonline.oss-cn-beijing.aliyuncs.com/course/浅谈医德医风建设.mp4
84 | //https://closs.jumingedu.com/20210930/%E5%B8%B8%E8%A7%81%E5%BF%83%E5%BE%8B%E5%A4%B1%E5%B8%B8%E7%9A%84%E8%AF%8A%E6%B2%BB%E5%8E%9F%E5%88%99/%E5%B8%B8%E8%A7%81%E5%BF%83%E5%BE%8B%E5%A4%B1%E5%B8%B8%E7%9A%84%E8%AF%8A%E6%B2%BB%E5%8E%9F%E5%88%99.m3u8
85 | //https://look.chinahrt.com.cn/courseyun/rxsl2content/transcode/20211/be3b6935-f678-4303-a1f8-b2a006352656/283006-mp4.mp4
86 | //https://video.qiantucdn.com/58pic/00/20/21/09v58PICJQgaWdcC58PICSUbK.mp4?e=1636441061&token=OyzEe_0O8H433pm7zVEjtnSy5dVdfpsIawO2nx3f:eHu6r0m7_zdDEj-L6lTqq_6OYPs=
87 | //https://stream7.iqilu.com/10339/article/202002/18/2fca1c77730e54c7b500573c2437003f.mp4
88 | controller.setStartTime(60);
89 | controller.play(
90 | "https://stream7.iqilu.com/10339/article/202002/18/2fca1c77730e54c7b500573c2437003f.mp4");
91 | },
92 | ),
93 | );
94 | }
95 |
96 | @override
97 | void dispose() {
98 | controller.dispose();
99 | super.dispose();
100 | }
101 | }
102 |
--------------------------------------------------------------------------------
/example/pubspec.lock:
--------------------------------------------------------------------------------
1 | # Generated by pub
2 | # See https://dart.dev/tools/pub/glossary#lockfile
3 | packages:
4 | async:
5 | dependency: transitive
6 | description:
7 | name: async
8 | url: "https://pub.flutter-io.cn"
9 | source: hosted
10 | version: "2.8.1"
11 | boolean_selector:
12 | dependency: transitive
13 | description:
14 | name: boolean_selector
15 | url: "https://pub.flutter-io.cn"
16 | source: hosted
17 | version: "2.1.0"
18 | characters:
19 | dependency: transitive
20 | description:
21 | name: characters
22 | url: "https://pub.flutter-io.cn"
23 | source: hosted
24 | version: "1.1.0"
25 | charcode:
26 | dependency: transitive
27 | description:
28 | name: charcode
29 | url: "https://pub.flutter-io.cn"
30 | source: hosted
31 | version: "1.3.1"
32 | clock:
33 | dependency: transitive
34 | description:
35 | name: clock
36 | url: "https://pub.flutter-io.cn"
37 | source: hosted
38 | version: "1.1.0"
39 | collection:
40 | dependency: transitive
41 | description:
42 | name: collection
43 | url: "https://pub.flutter-io.cn"
44 | source: hosted
45 | version: "1.15.0"
46 | cupertino_icons:
47 | dependency: "direct main"
48 | description:
49 | name: cupertino_icons
50 | url: "https://pub.flutter-io.cn"
51 | source: hosted
52 | version: "1.0.3"
53 | fake_async:
54 | dependency: transitive
55 | description:
56 | name: fake_async
57 | url: "https://pub.flutter-io.cn"
58 | source: hosted
59 | version: "1.2.0"
60 | flt_video_player:
61 | dependency: "direct main"
62 | description:
63 | path: ".."
64 | relative: true
65 | source: path
66 | version: "0.0.1"
67 | flutter:
68 | dependency: "direct main"
69 | description: flutter
70 | source: sdk
71 | version: "0.0.0"
72 | flutter_lints:
73 | dependency: "direct dev"
74 | description:
75 | name: flutter_lints
76 | url: "https://pub.flutter-io.cn"
77 | source: hosted
78 | version: "1.0.4"
79 | flutter_test:
80 | dependency: "direct dev"
81 | description: flutter
82 | source: sdk
83 | version: "0.0.0"
84 | lints:
85 | dependency: transitive
86 | description:
87 | name: lints
88 | url: "https://pub.flutter-io.cn"
89 | source: hosted
90 | version: "1.0.1"
91 | matcher:
92 | dependency: transitive
93 | description:
94 | name: matcher
95 | url: "https://pub.flutter-io.cn"
96 | source: hosted
97 | version: "0.12.10"
98 | meta:
99 | dependency: transitive
100 | description:
101 | name: meta
102 | url: "https://pub.flutter-io.cn"
103 | source: hosted
104 | version: "1.7.0"
105 | orientation:
106 | dependency: "direct main"
107 | description:
108 | name: orientation
109 | url: "https://pub.flutter-io.cn"
110 | source: hosted
111 | version: "1.3.0"
112 | path:
113 | dependency: transitive
114 | description:
115 | name: path
116 | url: "https://pub.flutter-io.cn"
117 | source: hosted
118 | version: "1.8.0"
119 | sky_engine:
120 | dependency: transitive
121 | description: flutter
122 | source: sdk
123 | version: "0.0.99"
124 | source_span:
125 | dependency: transitive
126 | description:
127 | name: source_span
128 | url: "https://pub.flutter-io.cn"
129 | source: hosted
130 | version: "1.8.1"
131 | stack_trace:
132 | dependency: transitive
133 | description:
134 | name: stack_trace
135 | url: "https://pub.flutter-io.cn"
136 | source: hosted
137 | version: "1.10.0"
138 | stream_channel:
139 | dependency: transitive
140 | description:
141 | name: stream_channel
142 | url: "https://pub.flutter-io.cn"
143 | source: hosted
144 | version: "2.1.0"
145 | string_scanner:
146 | dependency: transitive
147 | description:
148 | name: string_scanner
149 | url: "https://pub.flutter-io.cn"
150 | source: hosted
151 | version: "1.1.0"
152 | term_glyph:
153 | dependency: transitive
154 | description:
155 | name: term_glyph
156 | url: "https://pub.flutter-io.cn"
157 | source: hosted
158 | version: "1.2.0"
159 | test_api:
160 | dependency: transitive
161 | description:
162 | name: test_api
163 | url: "https://pub.flutter-io.cn"
164 | source: hosted
165 | version: "0.4.2"
166 | typed_data:
167 | dependency: transitive
168 | description:
169 | name: typed_data
170 | url: "https://pub.flutter-io.cn"
171 | source: hosted
172 | version: "1.3.0"
173 | vector_math:
174 | dependency: transitive
175 | description:
176 | name: vector_math
177 | url: "https://pub.flutter-io.cn"
178 | source: hosted
179 | version: "2.1.0"
180 | sdks:
181 | dart: ">=2.12.0 <3.0.0"
182 | flutter: ">=2.0.1"
183 |
--------------------------------------------------------------------------------
/example/pubspec.yaml:
--------------------------------------------------------------------------------
1 | name: flt_video_player_example
2 | description: Demonstrates how to use the flt_video_player plugin.
3 |
4 | # The following line prevents the package from being accidentally published to
5 | # pub.dev using `flutter pub publish`. This is preferred for private packages.
6 | publish_to: 'none' # Remove this line if you wish to publish to pub.dev
7 |
8 | environment:
9 | sdk: ">=2.12.0 <3.0.0"
10 |
11 | # Dependencies specify other packages that your package needs in order to work.
12 | # To automatically upgrade your package dependencies to the latest versions
13 | # consider running `flutter pub upgrade --major-versions`. Alternatively,
14 | # dependencies can be manually updated by changing the version numbers below to
15 | # the latest version available on pub.dev. To see which dependencies have newer
16 | # versions available, run `flutter pub outdated`.
17 | dependencies:
18 | flutter:
19 | sdk: flutter
20 |
21 | flt_video_player:
22 | # When depending on this package from a real application you should use:
23 | # flt_video_player: ^x.y.z
24 | # See https://dart.dev/tools/pub/dependencies#version-constraints
25 | # The example app is bundled with the plugin so we use a path dependency on
26 | # the parent directory to use the current plugin's version.
27 | path: ../
28 |
29 | # The following adds the Cupertino Icons font to your application.
30 | # Use with the CupertinoIcons class for iOS style icons.
31 | cupertino_icons: ^1.0.2
32 | # 屏幕方向控制
33 | orientation: ^1.3.0
34 |
35 | dev_dependencies:
36 | flutter_test:
37 | sdk: flutter
38 |
39 | # The "flutter_lints" package below contains a set of recommended lints to
40 | # encourage good coding practices. The lint set provided by the package is
41 | # activated in the `analysis_options.yaml` file located at the root of your
42 | # package. See that file for information about deactivating specific lint
43 | # rules and activating additional ones.
44 | flutter_lints: ^1.0.0
45 |
46 | # For information on the generic Dart part of this file, see the
47 | # following page: https://dart.dev/tools/pub/pubspec
48 |
49 | # The following section is specific to Flutter.
50 | flutter:
51 |
52 | # The following line ensures that the Material Icons font is
53 | # included with your application, so that you can use the icons in
54 | # the material Icons class.
55 | uses-material-design: true
56 |
57 | # To add assets to your application, add an assets section, like this:
58 | assets:
59 | - resources/speaker.circle.png
60 | - resources/speaker.slash.circle.png
61 | - resources/arrow.down.right.and.arrow.up.left.circle.png
62 | - resources/arrow.up.backward.and.arrow.down.forward.circle.png
63 | - resources/pause_circle.png
64 | - resources/play_circle.png
65 |
66 | # An image asset can refer to one or more resolution-specific "variants", see
67 | # https://flutter.dev/assets-and-images/#resolution-aware.
68 |
69 | # For details regarding adding assets from package dependencies, see
70 | # https://flutter.dev/assets-and-images/#from-packages
71 |
72 | # To add custom fonts to your application, add a fonts section here,
73 | # in this "flutter" section. Each entry in this list should have a
74 | # "family" key with the font family name, and a "fonts" key with a
75 | # list giving the asset and other descriptors for the font. For
76 | # example:
77 | # fonts:
78 | # - family: Schyler
79 | # fonts:
80 | # - asset: fonts/Schyler-Regular.ttf
81 | # - asset: fonts/Schyler-Italic.ttf
82 | # style: italic
83 | # - family: Trajan Pro
84 | # fonts:
85 | # - asset: fonts/TrajanPro.ttf
86 | # - asset: fonts/TrajanPro_Bold.ttf
87 | # weight: 700
88 | #
89 | # For details regarding fonts from package dependencies,
90 | # see https://flutter.dev/custom-fonts/#from-packages
91 |
--------------------------------------------------------------------------------
/example/resources/arrow.down.right.and.arrow.up.left.circle.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RandyWei/flt_video_player/fa8d074a73c354362459d2106b68e0f0882088e4/example/resources/arrow.down.right.and.arrow.up.left.circle.png
--------------------------------------------------------------------------------
/example/resources/arrow.up.backward.and.arrow.down.forward.circle.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RandyWei/flt_video_player/fa8d074a73c354362459d2106b68e0f0882088e4/example/resources/arrow.up.backward.and.arrow.down.forward.circle.png
--------------------------------------------------------------------------------
/example/resources/pause_circle.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RandyWei/flt_video_player/fa8d074a73c354362459d2106b68e0f0882088e4/example/resources/pause_circle.png
--------------------------------------------------------------------------------
/example/resources/play_circle.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RandyWei/flt_video_player/fa8d074a73c354362459d2106b68e0f0882088e4/example/resources/play_circle.png
--------------------------------------------------------------------------------
/example/resources/speaker.circle.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RandyWei/flt_video_player/fa8d074a73c354362459d2106b68e0f0882088e4/example/resources/speaker.circle.png
--------------------------------------------------------------------------------
/example/resources/speaker.slash.circle.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RandyWei/flt_video_player/fa8d074a73c354362459d2106b68e0f0882088e4/example/resources/speaker.slash.circle.png
--------------------------------------------------------------------------------
/example/test/widget_test.dart:
--------------------------------------------------------------------------------
1 | // This is a basic Flutter widget test.
2 | //
3 | // To perform an interaction with a widget in your test, use the WidgetTester
4 | // utility that Flutter provides. For example, you can send tap and scroll
5 | // gestures. You can also use WidgetTester to find child widgets in the widget
6 | // tree, read text, and verify that the values of widget properties are correct.
7 |
8 | import 'package:flutter/material.dart';
9 | import 'package:flutter_test/flutter_test.dart';
10 |
11 | import 'package:flt_video_player_example/main.dart';
12 |
13 | void main() {
14 | testWidgets('Verify Platform version', (WidgetTester tester) async {
15 | // Build our app and trigger a frame.
16 | await tester.pumpWidget(const MyApp());
17 |
18 | // Verify that platform version is retrieved.
19 | expect(
20 | find.byWidgetPredicate(
21 | (Widget widget) => widget is Text &&
22 | widget.data!.startsWith('Running on:'),
23 | ),
24 | findsOneWidget,
25 | );
26 | });
27 | }
28 |
--------------------------------------------------------------------------------
/flt_video_player.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/ios/.gitignore:
--------------------------------------------------------------------------------
1 | .idea/
2 | .vagrant/
3 | .sconsign.dblite
4 | .svn/
5 |
6 | .DS_Store
7 | *.swp
8 | profile
9 |
10 | DerivedData/
11 | build/
12 | GeneratedPluginRegistrant.h
13 | GeneratedPluginRegistrant.m
14 |
15 | .generated/
16 |
17 | *.pbxuser
18 | *.mode1v3
19 | *.mode2v3
20 | *.perspectivev3
21 |
22 | !default.pbxuser
23 | !default.mode1v3
24 | !default.mode2v3
25 | !default.perspectivev3
26 |
27 | xcuserdata
28 |
29 | *.moved-aside
30 |
31 | *.pyc
32 | *sync/
33 | Icon?
34 | .tags*
35 |
36 | /Flutter/Generated.xcconfig
37 | /Flutter/ephemeral/
38 | /Flutter/flutter_export_environment.sh
--------------------------------------------------------------------------------
/ios/Assets/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RandyWei/flt_video_player/fa8d074a73c354362459d2106b68e0f0882088e4/ios/Assets/.gitkeep
--------------------------------------------------------------------------------
/ios/Classes/EventSinkQueue.h:
--------------------------------------------------------------------------------
1 | //
2 | // EventSinkQueue.h
3 | // flt_video_player
4 | //
5 | // Created by RandyWei on 2021/11/5.
6 | //
7 |
8 | #import
9 | #import
10 |
11 | NS_ASSUME_NONNULL_BEGIN
12 |
13 | @interface EventSinkQueue : NSObject
14 |
15 | -(void)success:(NSObject*) event;
16 |
17 | -(void)setDelegate:(_Nullable FlutterEventSink)sink;
18 |
19 | -(void)error:(NSString*)code message:(NSString *_Nullable)message details:(id _Nullable)details;
20 |
21 | @end
22 |
23 | NS_ASSUME_NONNULL_END
24 |
--------------------------------------------------------------------------------
/ios/Classes/EventSinkQueue.m:
--------------------------------------------------------------------------------
1 | //
2 | // EventSinkQueue.m
3 | // flt_video_player
4 | //
5 | // Created by RandyWei on 2021/11/5.
6 | //
7 |
8 | #import "EventSinkQueue.h"
9 |
10 | @interface EventSinkQueue()
11 |
12 | @property (nonatomic,strong) NSMutableArray *eventQueue;
13 | @property (nonatomic, copy) FlutterEventSink eventSink;
14 |
15 | @end
16 |
17 | @implementation EventSinkQueue
18 |
19 | - (void)success:(NSObject *)event{
20 | [self enqueue:event];
21 | [self flushIfNeed];
22 | }
23 |
24 | - (void)setDelegate:(FlutterEventSink)sink{
25 | self.eventSink = sink;
26 | }
27 |
28 | - (void)error:(NSString *)code message:(NSString *)message details:(id)details{
29 | [self enqueue:[FlutterError errorWithCode:code message:message details:details]];
30 | [self flushIfNeed];
31 | }
32 |
33 | -(instancetype)init{
34 | if (self = [super init]) {
35 | _eventQueue = @[].mutableCopy;
36 | }
37 | return self;
38 | }
39 |
40 | -(void)flushIfNeed{
41 | if (self.eventSink == nil) {
42 | return;
43 | }
44 |
45 | for (NSObject *obj in self.eventQueue) {
46 | self.eventSink(obj);
47 | }
48 | [self.eventQueue removeAllObjects];
49 | }
50 |
51 | -(void)enqueue:(NSObject*) event{
52 | [self.eventQueue addObject:event];
53 | }
54 |
55 | @end
56 |
--------------------------------------------------------------------------------
/ios/Classes/FltBasePlayer.h:
--------------------------------------------------------------------------------
1 | //
2 | // FltBasePlayer.h
3 | // flt_video_player
4 | //
5 | // Created by RandyWei on 2021/11/4.
6 | //
7 |
8 | #import
9 | #import
10 | #import
11 | #import "EventSinkQueue.h"
12 |
13 | NS_ASSUME_NONNULL_BEGIN
14 |
15 | @interface FltBasePlayer : NSObject{
16 | @protected
17 | TXVodPlayer *_vodPlayer;
18 |
19 | @protected
20 | int64_t _textureId;
21 |
22 |
23 |
24 | //通信通道
25 | @protected
26 | FlutterMethodChannel *_methodChannel;
27 |
28 | //播放事件回调通信通道
29 | @protected
30 | EventSinkQueue *_eventSink;
31 | @protected
32 | FlutterEventChannel *_eventChannel;
33 |
34 | //网络回调通信通道
35 | @protected
36 | EventSinkQueue *_netSink;
37 | @protected
38 | FlutterEventChannel *_netChannel;
39 | }
40 |
41 | @property(atomic, readonly) NSNumber *playerId;
42 |
43 | -(void)destory;
44 |
45 |
46 | ///
47 | ///开始播放
48 | ///
49 | -(int)startPlay:(NSString*)url;
50 |
51 | ///
52 | ///停止播放
53 | ///
54 | -(BOOL)stopPlay;
55 |
56 | ///
57 | ///播放状态
58 | ///
59 | - (BOOL) isPlaying;
60 |
61 |
62 | - (void) pause;
63 |
64 | - (void) resume;
65 |
66 | /**
67 | * 播放跳转到音视频流某个时间
68 | * @param time 流时间,单位为秒
69 | * @return 0 = OK
70 | */
71 | -(int) seek: (float) time;
72 |
73 | -(float) currentPlaybackTime;
74 |
75 | -(float) duration;
76 |
77 | -(float) playableDuration;
78 |
79 | -(void) setMute:(BOOL)bEnable;
80 | /**
81 | * 设置音量大小
82 | * @param volume 音量大小。范围:0 ~ 100。
83 | */
84 | -(void) setAudioPlayoutVolume:(int)volume;
85 |
86 | /**
87 | * 设置播放速率
88 | * @param rate 正常速度为1.0;小于为慢速;大于为快速。最大建议不超过2.0
89 | */
90 | - (void)setRate:(float)rate;
91 |
92 | /**
93 | * 设置画面镜像
94 | */
95 | - (void)setMirror:(BOOL)isMirror;
96 |
97 | - (void)setRenderRotation: (int)rotaion;
98 |
99 | - (void)handleMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result;
100 |
101 | - (void)createVodPlayer:(TXVodPlayConfig*) config;
102 |
103 | @end
104 |
105 | NS_ASSUME_NONNULL_END
106 |
--------------------------------------------------------------------------------
/ios/Classes/FltBasePlayer.m:
--------------------------------------------------------------------------------
1 | //
2 | // FltBasePlayer.m
3 | // flt_video_player
4 | //
5 | // Created by RandyWei on 2021/11/4.
6 | //
7 |
8 | #import "FltBasePlayer.h"
9 | #import
10 | #import
11 |
12 | static atomic_int atomicId = 0;
13 |
14 | static const int uninitialized = -1;
15 |
16 | @implementation FltBasePlayer
17 |
18 | -(instancetype) init{
19 | if (self = [super init]) {
20 | int pid = atomic_fetch_add(&atomicId, 1);
21 | _playerId = @(pid);
22 | }
23 | return self;
24 | }
25 |
26 |
27 | ///
28 | ///开始播放
29 | ///
30 | -(int)startPlay:(NSString*)url{
31 | if(_vodPlayer != nil){
32 | return [_vodPlayer startPlay:url];
33 | }
34 | return uninitialized;
35 | }
36 |
37 | ///
38 | ///停止播放
39 | ///
40 | -(BOOL)stopPlay{
41 | if (_vodPlayer != nil) {
42 | return [_vodPlayer stopPlay];
43 | }
44 | return NO;
45 | }
46 |
47 | ///
48 | ///播放状态
49 | ///
50 | - (BOOL) isPlaying{
51 | if (_vodPlayer != nil) {
52 | return [_vodPlayer isPlaying];
53 | }
54 | return NO;
55 | }
56 |
57 |
58 | - (void) pause{
59 | if (_vodPlayer != nil) {
60 | [_vodPlayer pause];
61 | }
62 | }
63 |
64 | - (void) resume{
65 | if (_vodPlayer != nil) {
66 | [_vodPlayer resume];
67 | }
68 | }
69 |
70 | /**
71 | * 播放跳转到音视频流某个时间
72 | * @param time 流时间,单位为秒
73 | * @return 0 = OK
74 | */
75 | -(int) seek: (float) time{
76 | if (_vodPlayer != nil) {
77 | return [_vodPlayer seek:time];
78 | }
79 | return -1;
80 | }
81 |
82 | -(void) setStartTime: (CGFloat) time{
83 | if (_vodPlayer != nil) {
84 | [_vodPlayer setStartTime:time];
85 | }
86 | }
87 |
88 | -(float) currentPlaybackTime {
89 | if (_vodPlayer != nil) {
90 | return [_vodPlayer currentPlaybackTime];
91 | }
92 | return 0;
93 | }
94 |
95 | -(float) duration {
96 | if (_vodPlayer != nil) {
97 | return [_vodPlayer duration];
98 | }
99 | return 0;
100 | }
101 |
102 | -(float) playableDuration {
103 | if (_vodPlayer != nil) {
104 | return [_vodPlayer playableDuration];
105 | }
106 | return 0;
107 | }
108 |
109 | -(void) setMute:(BOOL)bEnable {
110 | if (_vodPlayer != nil) {
111 | [_vodPlayer setMute:bEnable];
112 | }
113 | }
114 | /**
115 | * 设置音量大小
116 | * @param volume 音量大小。范围:0 ~ 100。
117 | */
118 | -(void) setAudioPlayoutVolume:(int)volume {
119 | if (_vodPlayer != nil) {
120 | [_vodPlayer setAudioPlayoutVolume:volume];
121 | }
122 | }
123 |
124 | /**
125 | * 设置播放速率
126 | * @param rate 正常速度为1.0;小于为慢速;大于为快速。最大建议不超过2.0
127 | */
128 | - (void)setRate:(float)rate{
129 | if (_vodPlayer != nil) {
130 | [_vodPlayer setRate:rate];
131 | }
132 | }
133 |
134 | /**
135 | * 设置画面镜像
136 | */
137 | - (void)setMirror:(BOOL)isMirror{
138 | if (_vodPlayer != nil) {
139 | [_vodPlayer setMirror:isMirror];
140 | }
141 | }
142 |
143 | -(void)setRenderRotation: (int)rotaion{
144 | if (_vodPlayer != nil) {
145 | [_vodPlayer setRenderRotation:rotaion];
146 | }
147 | }
148 |
149 |
150 | -(void)destory{
151 | //停止播放
152 | [self stopPlay];
153 |
154 | //移除视频视图
155 | [_vodPlayer removeVideoWidget];
156 |
157 | _vodPlayer = nil;
158 |
159 | [_methodChannel setMethodCallHandler:nil];
160 | _methodChannel = nil;
161 |
162 | [_eventSink setDelegate:nil];
163 | _eventSink = nil;
164 |
165 | [_eventChannel setStreamHandler:nil];
166 | _eventChannel = nil;
167 |
168 | [_netSink setDelegate:nil];
169 | _netSink = nil;
170 |
171 | [_netChannel setStreamHandler:nil];
172 | _netChannel = nil;
173 | }
174 |
175 |
176 | ///
177 | ///创建播放器
178 | ///
179 | - (void)createVodPlayer:(TXVodPlayConfig*) config{
180 | if (_vodPlayer == nil) {
181 | _vodPlayer = [TXVodPlayer new];
182 | _vodPlayer.config = config;
183 | _vodPlayer.vodDelegate = self;
184 | _vodPlayer.enableHWAcceleration = YES;
185 | [_vodPlayer setVideoProcessDelegate:self];
186 | _vodPlayer.enableHWAcceleration = YES;
187 | }
188 | }
189 |
190 |
191 | +(NSDictionary*) getParamsWithEvent:(int)EvtId withParams:(NSDictionary*) params{
192 | NSMutableDictionary *dict = [NSMutableDictionary dictionaryWithObject:@(EvtId) forKey:@"event"];
193 | if (params != nil && params.count > 0 ) {
194 | [dict addEntriesFromDictionary:params];
195 | }
196 | return dict;
197 | }
198 |
199 | #pragma mark - TXVodPlayListener
200 |
201 | -(void)onPlayEvent:(TXVodPlayer *)player event:(int)EvtID withParam:(NSDictionary *)param{
202 | [_eventSink success:[FltBasePlayer getParamsWithEvent:EvtID withParams:param]];
203 | }
204 |
205 | - (void)onNetStatus:(TXVodPlayer *)player withParam:(NSDictionary *)param {
206 | [_netSink success:param];
207 | }
208 |
209 |
210 | #pragma mark - Flutter Stream Handler
211 | -(FlutterError *)onListenWithArguments:(id)arguments eventSink:(FlutterEventSink)events{
212 | if ([arguments isKindOfClass:NSString.class]) {
213 | if ([arguments isEqualToString: @"event"]) {
214 | [_eventSink setDelegate:events];
215 | } else if ([arguments isEqualToString:@"net"]){
216 | [_netSink setDelegate:events];
217 | }
218 | }
219 | return nil;
220 | }
221 |
222 | - (FlutterError *)onCancelWithArguments:(id)arguments{
223 | if ([arguments isKindOfClass:NSString.class]) {
224 | if ([arguments isEqualToString: @"event"]) {
225 | [_eventSink setDelegate:nil];
226 | } else if ([arguments isEqualToString:@"net"]){
227 | [_netSink setDelegate:nil];
228 | }
229 | }
230 | return nil;
231 | }
232 |
233 |
234 |
235 |
236 | #pragma mark - Flutter Method Channel
237 | -(void)handleMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result{
238 | NSLog(@"method: %@",call.method);
239 | NSDictionary *args = call.arguments; //读取参数
240 | if ([@"init" isEqualToString:call.method]) {
241 | // BOOL onlyAudio = [args[@"onlyAudio"] boolValue];
242 |
243 | //读取 config
244 |
245 | NSDictionary *configMap = args[@"config"];
246 |
247 | TXVodPlayConfig *playerConfig = [[TXVodPlayConfig alloc]init];
248 | playerConfig.connectRetryCount = [configMap[@"connectRetryCount"] intValue];
249 | playerConfig.connectRetryInterval = [configMap[@"connectRetryInterval"] intValue];
250 | playerConfig.timeout = [configMap[@"timeout"] intValue];
251 | playerConfig.keepLastFrameWhenStop = [configMap[@"keepLastFrameWhenStop"] boolValue];
252 | [playerConfig setFirstStartPlayBufferTime: [configMap[@"firstStartPlayBufferTime"] intValue]];
253 | // playerConfig.firstStartPlayBufferTime = [configMap[@"firstStartPlayBufferTime"] intValue];
254 | [playerConfig setNextStartPlayBufferTime:[configMap[@"nextStartPlayBufferTime"] intValue]];
255 | // playerConfig.nextStartPlayBufferTime = [configMap[@"nextStartPlayBufferTime"] intValue];
256 |
257 | NSString *cacheFolderPath = [configMap[@"cacheFolderPath"] stringValue];
258 | if (cacheFolderPath != nil && cacheFolderPath.length > 0) {
259 | playerConfig.cacheFolderPath = cacheFolderPath;
260 | }
261 |
262 | playerConfig.maxCacheItems = [configMap[@"maxCacheItems"] intValue];
263 | playerConfig.headers = configMap[@"headers"];
264 |
265 | playerConfig.enableAccurateSeek = [configMap[@"enableAccurateSeek"] boolValue];
266 | playerConfig.progressInterval = [configMap[@"progressInterval"] doubleValue];
267 | playerConfig.maxBufferSize = [configMap[@"maxBufferSize"] intValue];
268 |
269 | NSString *overlayKey = [configMap[@"overlayKey"] stringValue];
270 |
271 | if (overlayKey != nil && overlayKey.length > 0) {
272 | // playerConfig.overlayKey = overlayKey;
273 | [playerConfig setOverlayKey:overlayKey];
274 | }
275 |
276 | NSString *overlayIv = [configMap[@"overlayIv"] stringValue];
277 |
278 | if (overlayIv != nil && overlayIv.length > 0) {
279 | // playerConfig.overlayIv = overlayIv;
280 | [playerConfig setOverlayIv:overlayIv];
281 | }
282 |
283 | [self createVodPlayer:playerConfig];
284 | NSNumber *textureId = [NSNumber numberWithLongLong:_textureId];
285 | result(textureId);
286 | } else if ([@"play" isEqualToString:call.method]) {
287 | NSString *url = args[@"url"];
288 | int r = [self startPlay:url];
289 | result(@(r));
290 | } else if ([@"stop" isEqualToString:call.method]){
291 | BOOL r = [self stopPlay];
292 | result([NSNumber numberWithBool:r]);
293 | } else if ([@"isPlaying" isEqualToString:call.method]){
294 | result([NSNumber numberWithBool:[self isPlaying]]);
295 | } else if ([@"pause" isEqualToString:call.method]) {
296 | [self pause];
297 | result(nil);
298 | } else if ([@"resume" isEqualToString:call.method]){
299 | [self resume];
300 | result(nil);
301 | } else if ([@"seek" isEqualToString:call.method]){
302 | float time = [args[@"time"] intValue];
303 | BOOL r = [self seek:time];
304 | result([NSNumber numberWithBool:r]);
305 | } else if ([@"setStartTime" isEqualToString:call.method]){
306 | float time = [args[@"time"] floatValue];
307 | [self setStartTime:time];
308 | result(nil);
309 | } else if([@"currentPlaybackTime" isEqualToString:call.method]){
310 | result(@([self currentPlaybackTime]));
311 | } else if ([@"duration" isEqualToString:call.method]){
312 | result(@([self duration]));
313 | } else if ([@"playableDuration" isEqualToString:call.method]){
314 | result(@([self playableDuration]));
315 | } else if ([@"setMute" isEqualToString:call.method]){
316 | BOOL enable = [args[@"enable"] boolValue];
317 | [self setMute:enable];
318 | result(nil);
319 | } else if([@"setAudioPlayoutVolume" isEqualToString:call.method]){
320 | int volume = [args[@"volume"] intValue];
321 | volume = MAX(0, volume);
322 | volume = MIN(100, volume);
323 | [self setAudioPlayoutVolume:volume];
324 | result(nil);
325 | } else if ([@"setRate" isEqualToString:call.method]){
326 | int rate = [args[@"rate"] floatValue];
327 | [self setRate:rate];
328 | result(nil);
329 | } else if([@"setMirror" isEqualToString:call.method]){
330 | BOOL mirror = [args[@"mirror"] boolValue];
331 | [self setMirror:mirror];
332 | result(nil);
333 | } else if([@"setLoop" isEqualToString:call.method]){
334 | if (_vodPlayer != nil) {
335 | BOOL loop = [args[@"loop"] boolValue];
336 | [_vodPlayer setLoop:loop];
337 | }
338 | result(nil);
339 | } else if([@"setRenderRotation" isEqualToString:call.method]){
340 | BOOL rotation = [args[@"rotation"] intValue];
341 | [self setRenderRotation:rotation];
342 | result(nil);
343 | }
344 | }
345 |
346 | @end
347 |
--------------------------------------------------------------------------------
/ios/Classes/FltVideoPlayerPlugin.h:
--------------------------------------------------------------------------------
1 | #import
2 | @interface FltVideoPlayerPlugin : NSObject
3 | @end
4 |
--------------------------------------------------------------------------------
/ios/Classes/FltVideoPlayerPlugin.m:
--------------------------------------------------------------------------------
1 | #import "FltVideoPlayerPlugin.h"
2 | #import "FltVodPlayer.h"
3 | #import "FltVideoViewFactory.h"
4 |
5 |
6 | @interface FltVideoPlayerPlugin()
7 |
8 | @property (nonatomic,strong) NSObject* registrar; //存储 flutter registrar
9 | @property (nonatomic,strong) NSMutableDictionary *players; //存储播放器对象,可能有多个
10 |
11 | @end
12 |
13 | @implementation FltVideoPlayerPlugin
14 |
15 | + (void)registerWithRegistrar:(NSObject*)registrar {
16 |
17 | FlutterMethodChannel* channel = [FlutterMethodChannel
18 | methodChannelWithName:@"plugins.bughub.icu/flt_video_player"
19 | binaryMessenger:[registrar messenger]];
20 |
21 | FltVideoPlayerPlugin* instance = [[FltVideoPlayerPlugin alloc] initWithRegistrar:registrar];
22 |
23 | [registrar addMethodCallDelegate:instance channel:channel];
24 |
25 | [registrar registerViewFactory:[[FltVideoViewFactory alloc]initWithRegistrar:registrar onViewCreate:^(int64_t viewId,FltBasePlayer *player) {
26 | [[instance players] setValue:player forKey:[NSString stringWithFormat:@"%lld",viewId]];
27 | }] withId:@"FltVideoView"];
28 | }
29 |
30 | - (instancetype) initWithRegistrar: (NSObject *) registrar{
31 | self = [super init];
32 | if (self) {
33 | _registrar = registrar;
34 | _players = @{}.mutableCopy;
35 | }
36 | return self;
37 | }
38 |
39 |
40 | - (void)handleMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result {
41 | if ([@"getPlatformVersion" isEqualToString:call.method]) {
42 | result([@"iOS " stringByAppendingString:[[UIDevice currentDevice] systemVersion]]);
43 | } else if ( [@"createVodPlayer" isEqualToString:call.method] ) { //初始化 vod player
44 | FltVodPlayer *vodPlayer = [[FltVodPlayer alloc]initWithRegistrar:self.registrar];
45 | NSNumber *playerId = vodPlayer.playerId;
46 | _players[[playerId stringValue]] = vodPlayer;
47 | result(playerId);
48 | } else if ([@"releaseVodPlayer" isEqualToString:call.method] ) {
49 | NSDictionary *args = call.arguments;
50 | NSNumber *playerId = args[@"playerId"];
51 | FltBasePlayer *player = [_players objectForKey:[playerId stringValue]];
52 | [player destory];
53 | if (player != nil) {
54 | [_players removeObjectForKey:playerId];
55 | }
56 | }
57 |
58 | else {
59 | result(FlutterMethodNotImplemented);
60 | }
61 | }
62 |
63 | @end
64 |
--------------------------------------------------------------------------------
/ios/Classes/FltVideoView.h:
--------------------------------------------------------------------------------
1 | //
2 | // FltVideoView.h
3 | // flt_video_player
4 | //
5 | // Created by RandyWei on 2021/11/17.
6 | //
7 |
8 | #import
9 | #import
10 | #import "FltBasePlayer.h"
11 |
12 | NS_ASSUME_NONNULL_BEGIN
13 |
14 | @interface FltVideoView : FltBasePlayer
15 |
16 | -(instancetype) initWithRegistrar:(id)registrar viewId:(int64_t)viewId;
17 |
18 | @end
19 |
20 | NS_ASSUME_NONNULL_END
21 |
--------------------------------------------------------------------------------
/ios/Classes/FltVideoView.m:
--------------------------------------------------------------------------------
1 | //
2 | // FltVideoView.m
3 | // flt_video_player
4 | //
5 | // Created by RandyWei on 2021/11/17.
6 | //
7 |
8 | #import "FltVideoView.h"
9 | #import
10 |
11 | @interface FltVideoView()
12 |
13 | @end
14 |
15 | @implementation FltVideoView{
16 | UIView *videoView;
17 | }
18 |
19 | -(instancetype)initWithRegistrar:(id)registrar viewId:(int64_t)viewId{
20 | NSLog(@"initWithRegistrar");
21 | if (self = [super init]) {
22 | videoView = [[UIView alloc]initWithFrame:CGRectMake(0, 0, 1, 1)];
23 |
24 | _eventSink = [EventSinkQueue new];
25 | _netSink = [EventSinkQueue new];
26 |
27 | __weak typeof(self) weakSelf = self;
28 |
29 | _methodChannel = [FlutterMethodChannel methodChannelWithName:[NSString stringWithFormat:@"plugins.bughub.icu/vodplayer/%lld",viewId] binaryMessenger:[registrar messenger]];
30 | [_methodChannel setMethodCallHandler:^(FlutterMethodCall * _Nonnull call, FlutterResult _Nonnull result) {
31 | [weakSelf handleMethodCall: call result:result];
32 | }];
33 |
34 | _eventChannel = [FlutterEventChannel eventChannelWithName:[@"plugins.bughub.icu/vodplayer/event/" stringByAppendingString:[NSString stringWithFormat:@"%lld",viewId]] binaryMessenger:[registrar messenger]];
35 | [_eventChannel setStreamHandler:self];
36 |
37 | _netChannel = [FlutterEventChannel eventChannelWithName:[@"plugins.bughub.icu/vodplayer/net/" stringByAppendingString:[NSString stringWithFormat:@"%lld",viewId]] binaryMessenger:[registrar messenger]];
38 | [_netChannel setStreamHandler:self];
39 |
40 | }
41 | return self;
42 | }
43 |
44 | - (void)createVodPlayer:(TXVodPlayConfig *)config{
45 | [super createVodPlayer:config];
46 | [_vodPlayer setupVideoWidget:videoView insertIndex:0];
47 |
48 | }
49 |
50 | - (nonnull UIView *)view {
51 | return videoView;
52 | }
53 |
54 |
55 | @end
56 |
--------------------------------------------------------------------------------
/ios/Classes/FltVideoViewFactory.h:
--------------------------------------------------------------------------------
1 | //
2 | // FltVideoViewFactory.h
3 | // flt_video_player
4 | //
5 | // Created by RandyWei on 2021/11/17.
6 | //
7 |
8 | #import
9 | #import
10 | #import "FltVideoView.h"
11 |
12 | NS_ASSUME_NONNULL_BEGIN
13 |
14 | @interface FltVideoViewFactory : NSObject
15 | -(instancetype) initWithRegistrar:(id)registrar onViewCreate:(void (^)(int64_t,FltVideoView*))onViewCreate;
16 | @end
17 |
18 | NS_ASSUME_NONNULL_END
19 |
--------------------------------------------------------------------------------
/ios/Classes/FltVideoViewFactory.m:
--------------------------------------------------------------------------------
1 | //
2 | // FltVideoViewFactory.m
3 | // flt_video_player
4 | //
5 | // Created by RandyWei on 2021/11/17.
6 | //
7 |
8 | #import "FltVideoViewFactory.h"
9 |
10 |
11 | @implementation FltVideoViewFactory{
12 | id _registrar;
13 | void (^_onViewCreate)(int64_t,FltVideoView*);
14 | }
15 |
16 | -(instancetype) initWithRegistrar:(id)registrar onViewCreate:(void (^)(int64_t,FltVideoView*))onViewCreate{
17 | if (self = [super init]) {
18 | _registrar = registrar;
19 | _onViewCreate = onViewCreate;
20 | }
21 | return self;
22 | }
23 |
24 | - (NSObject *)createArgsCodec{
25 | return FlutterStandardMessageCodec.sharedInstance;
26 | }
27 |
28 | - (nonnull NSObject *)createWithFrame:(CGRect)frame viewIdentifier:(int64_t)viewId arguments:(id _Nullable)args {
29 | FltVideoView *videoView = [[FltVideoView alloc]initWithRegistrar:_registrar viewId:viewId];
30 | _onViewCreate(viewId,videoView);
31 | return videoView;
32 | }
33 |
34 | @end
35 |
--------------------------------------------------------------------------------
/ios/Classes/FltVodPlayer.h:
--------------------------------------------------------------------------------
1 | //
2 | // FltVodPlayer.h
3 | // flt_video_player
4 | //
5 | // Created by RandyWei on 2021/11/4.
6 | //
7 |
8 |
9 | #import
10 | #import "FltBasePlayer.h"
11 |
12 | @protocol FlutterPluginRegistrar;
13 |
14 | NS_ASSUME_NONNULL_BEGIN
15 |
16 | @interface FltVodPlayer : FltBasePlayer
17 |
18 | -(instancetype) initWithRegistrar:(id)registrar;
19 |
20 | @end
21 |
22 | NS_ASSUME_NONNULL_END
23 |
--------------------------------------------------------------------------------
/ios/Classes/FltVodPlayer.m:
--------------------------------------------------------------------------------
1 | //
2 | // FltVodPlayer.m
3 | // flt_video_player
4 | //
5 | // Created by RandyWei on 2021/11/4.
6 | //
7 |
8 | #import "FltVodPlayer.h"
9 | #import
10 | #import
11 | #import
12 | #import
13 | #import "EventSinkQueue.h"
14 |
15 |
16 | @interface FltVodPlayer()
17 |
18 | @end
19 |
20 |
21 | @implementation FltVodPlayer{
22 |
23 | //最新一帧
24 | CVPixelBufferRef volatile _latestPixelBuffer;
25 | //旧的一帧
26 | CVPixelBufferRef _lastBuffer;
27 |
28 | id _registrar;
29 | id _textureRegistry;
30 | }
31 |
32 |
33 | -(instancetype)initWithRegistrar:(id)registrar{
34 | if (self = [self init]) {
35 | _registrar = registrar;
36 | _lastBuffer = nil;
37 | _latestPixelBuffer = nil;
38 | _textureId = -1;
39 |
40 | _eventSink = [EventSinkQueue new];
41 | _netSink = [EventSinkQueue new];
42 |
43 |
44 | __weak typeof(self) weakSelf = self;
45 |
46 | _methodChannel = [FlutterMethodChannel methodChannelWithName:[@"plugins.bughub.icu/vodplayer/" stringByAppendingString:[self.playerId stringValue]] binaryMessenger:[registrar messenger]];
47 | [_methodChannel setMethodCallHandler:^(FlutterMethodCall * _Nonnull call, FlutterResult _Nonnull result) {
48 | [weakSelf handleMethodCall: call result:result];
49 | }];
50 |
51 | _eventChannel = [FlutterEventChannel eventChannelWithName:[@"plugins.bughub.icu/vodplayer/event/" stringByAppendingString:[self.playerId stringValue]] binaryMessenger:[registrar messenger]];
52 | [_eventChannel setStreamHandler:self];
53 |
54 | _netChannel = [FlutterEventChannel eventChannelWithName:[@"plugins.bughub.icu/vodplayer/net/" stringByAppendingString:[self.playerId stringValue]] binaryMessenger:[registrar messenger]];
55 | [_netChannel setStreamHandler:self];
56 |
57 | }
58 |
59 | return self;
60 | }
61 |
62 |
63 |
64 | -(void)destory{
65 | [super destory];
66 |
67 | if (_textureId >= 0) {
68 | [_textureRegistry unregisterTexture:_textureId];
69 | _textureId = -1;
70 | _textureRegistry = nil;
71 | }
72 |
73 | CVPixelBufferRef old = _latestPixelBuffer;
74 | while (!OSAtomicCompareAndSwapPtrBarrier(old, nil, (void **)&_latestPixelBuffer)) {
75 | old = _latestPixelBuffer;
76 | }
77 |
78 | if (old) {
79 | CFRelease(old);
80 | }
81 |
82 | if (_lastBuffer) {
83 | CVPixelBufferRelease(_lastBuffer);
84 | _lastBuffer = nil;
85 | }
86 | }
87 |
88 |
89 | -(void)createVodPlayer:(TXVodPlayConfig*) config{
90 | [super createVodPlayer:config];
91 | [self setupTexture];
92 | }
93 | ///
94 | ///配置播放器
95 | ///
96 | - (void)setupTexture{
97 | if (_textureId < 0) {
98 | _textureRegistry = [_registrar textures];
99 | int64_t tId = [_textureRegistry registerTexture:self];
100 | _textureId = tId;
101 | }
102 | }
103 |
104 |
105 | #pragma mark - FlutterTexture
106 | - (CVPixelBufferRef _Nullable)copyPixelBuffer{
107 | CVPixelBufferRef pixelBuffer = _latestPixelBuffer;
108 | while (!OSAtomicCompareAndSwapPtrBarrier(pixelBuffer, nil, (void **)&_latestPixelBuffer)) {
109 | pixelBuffer = _latestPixelBuffer;
110 | }
111 | return pixelBuffer;
112 | }
113 |
114 | #pragma mark - TxVideoCustomProcessDelegate
115 |
116 | - (BOOL)onPlayerPixelBuffer:(CVPixelBufferRef)pixelBuffer{
117 | if (_lastBuffer == nil) {
118 | _lastBuffer = CVPixelBufferRetain(pixelBuffer);
119 | CFRetain(pixelBuffer);
120 | } else if (_lastBuffer != pixelBuffer) {
121 | CVPixelBufferRelease(_lastBuffer);
122 | _lastBuffer = CVPixelBufferRetain(pixelBuffer);
123 | CFRetain(pixelBuffer);
124 | }
125 |
126 | CVPixelBufferRef newBuffer = pixelBuffer;
127 |
128 | CVPixelBufferRef old = _latestPixelBuffer;
129 |
130 | while (!OSAtomicCompareAndSwapPtrBarrier(old, newBuffer, (void **)&_latestPixelBuffer)) {
131 | old = _latestPixelBuffer;
132 | }
133 |
134 | if (old && old != pixelBuffer) {
135 | CFRelease(old);
136 | }
137 |
138 | if (_textureId >= 0) {
139 | [_textureRegistry textureFrameAvailable:_textureId];
140 | }
141 |
142 | return NO;
143 | }
144 |
145 | @end
146 |
--------------------------------------------------------------------------------
/ios/flt_video_player.podspec:
--------------------------------------------------------------------------------
1 | #
2 | # To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html.
3 | # Run `pod lib lint flt_video_player.podspec` to validate before publishing.
4 | #
5 | Pod::Spec.new do |s|
6 | s.name = 'flt_video_player'
7 | s.version = '1.0.0'
8 | s.summary = 'A Video Player Flutter plugin based on TXVodPlayer'
9 | s.description = <<-DESC
10 | A Video Player Flutter plugin based on TXVodPlayer
11 | DESC
12 | s.homepage = 'http://www.bughub.icu:8888'
13 | s.license = { :file => '../LICENSE' }
14 | s.author = { 'Randy Wei' => 'smile561607154@163.com' }
15 | s.source = { :path => '.' }
16 | s.source_files = 'Classes/**/*'
17 | s.public_header_files = 'Classes/**/*.h'
18 | s.dependency 'Flutter'
19 | s.dependency 'TXLiteAVSDK_Player', '~> 9.2.10637' #集成腾讯原生播放器
20 | s.static_framework = true
21 | s.platform = :ios, '9.0'
22 |
23 | # Flutter.framework does not contain a i386 slice.
24 | s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES', 'EXCLUDED_ARCHS[sdk=iphonesimulator*]' => 'i386' }
25 |
26 | end
27 |
--------------------------------------------------------------------------------
/lib/flt_video_player.dart:
--------------------------------------------------------------------------------
1 | export 'src/plugin.dart';
2 | export 'src/vod/vod_player.dart';
3 | export 'src/vod/vodplayer_controller.dart';
4 | export 'src/vod/player_config.dart';
5 | export 'src/vod/player_define.dart';
6 | export 'src/vod/platform_video_view.dart';
7 |
--------------------------------------------------------------------------------
/lib/src/plugin.dart:
--------------------------------------------------------------------------------
1 | ///
2 | /// Created by wei on 2021/11/5.
3 | ///
4 | ///
5 | import 'dart:async';
6 | import 'dart:convert';
7 |
8 | import 'package:flutter/services.dart';
9 |
10 | class Plugin {
11 | static const methodChannelPrefix = "plugins.bughub.icu";
12 |
13 | static const MethodChannel _channel =
14 | MethodChannel('$methodChannelPrefix/flt_video_player');
15 |
16 | static Future get platformVersion async {
17 | final String? version = await _channel.invokeMethod('getPlatformVersion');
18 | return version;
19 | }
20 |
21 | static Future createVodPlayer(Map configJson) async {
22 | return await _channel
23 | .invokeMethod('createVodPlayer', {"config": configJson});
24 | }
25 |
26 | static Future releasePlayer(int playerId) async {
27 | return await _channel.invokeMethod("releaseVodPlayer", {"playerId": playerId});
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/lib/src/vod/platform_video_view.dart:
--------------------------------------------------------------------------------
1 | ///
2 | /// Created by wei on 2021/11/22.
3 | ///
4 | import 'dart:io';
5 |
6 | import 'package:flutter/services.dart';
7 | import "package:flutter/widgets.dart";
8 |
9 | class PlatformVideoView extends StatelessWidget {
10 | const PlatformVideoView({Key? key, this.onPlatformViewCreated})
11 | : super(key: key);
12 |
13 | final Function(int)? onPlatformViewCreated;
14 |
15 | @override
16 | Widget build(BuildContext context) {
17 | return Platform.isAndroid
18 | ? AndroidView(
19 | viewType: "FltVideoView",
20 | creationParamsCodec: const StandardMessageCodec(),
21 | onPlatformViewCreated: onPlatformViewCreated,
22 | )
23 | : UiKitView(
24 | viewType: "FltVideoView",
25 | creationParamsCodec: const StandardMessageCodec(),
26 | onPlatformViewCreated: onPlatformViewCreated,
27 | );
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/lib/src/vod/player_config.dart:
--------------------------------------------------------------------------------
1 | ///
2 | /// Created by wei on 2021/11/5.
3 | ///
4 | class PlayerConfig {
5 | /// 播放器连接重试次数:最小值为1,最大值为10,默认值为 3
6 | int connectRetryCount = 3;
7 |
8 | /// 播放器连接重试间隔:单位秒,最小值为3, 最大值为30,默认值为3
9 | int connectRetryInterval = 3;
10 |
11 | /// 超时时间:单位秒,默认10s
12 | int timeout = 10;
13 |
14 | /// stopPlay 的时候是否保留最后一帧画面,默认值为 NO
15 | bool keepLastFrameWhenStop = false;
16 |
17 | /// 首缓需要加载的数据时长,单位ms, 默认值为100ms
18 | int firstStartPlayBufferTime = 100;
19 |
20 | /// 缓冲时(缓冲数据不够引起的二次缓冲,或者seek引起的拖动缓冲)最少要缓存多长的数据才能结束缓冲,单位ms,默认值为250ms
21 | int nextStartPlayBufferTime = 250;
22 |
23 | /// 注意:缓存目录应该是单独的目录,SDK可能会清掉其中的文件
24 | ///< 视频缓存目录,点播MP4、HLS有效
25 | String cacheFolderPath = "";
26 |
27 | ///< 最多缓存文件个数
28 | int maxCacheItems = 0;
29 |
30 | ///< 自定义 HTTP Headers
31 | Map headers = {};
32 |
33 | ///< 是否精确 seek,默认YES。开启精确后seek,seek 的时间平均多出200ms
34 | bool enableAccurateSeek = true;
35 |
36 | /// 设置进度回调间隔时间
37 | /// 若不设置,SDK默认间隔0.5秒回调一次
38 | double progressInterval = 0.5;
39 |
40 | /// 最大预加载大小,单位 MB
41 | /// 此设置会影响playableDuration,设置越大,提前缓存的越多
42 | int maxBufferSize = 0;
43 |
44 | /// HLS EXT-X-KEY 加密key
45 | String overlayKey = "";
46 |
47 | /// HLS EXT-X-KEY 加密Iv
48 | String overlayIv = "";
49 |
50 | Map toJson() => {
51 | 'connectRetryCount': connectRetryCount,
52 | 'connectRetryInterval': connectRetryInterval,
53 | 'timeout': timeout,
54 | 'keepLastFrameWhenStop': keepLastFrameWhenStop,
55 | 'firstStartPlayBufferTime': firstStartPlayBufferTime,
56 | 'nextStartPlayBufferTime': nextStartPlayBufferTime,
57 | 'cacheFolderPath': cacheFolderPath,
58 | 'maxCacheItems': maxCacheItems,
59 | 'headers': headers,
60 | 'enableAccurateSeek': enableAccurateSeek,
61 | 'progressInterval': progressInterval,
62 | 'maxBufferSize': maxBufferSize,
63 | 'overlayKey': overlayKey,
64 | 'overlayIv': overlayIv
65 | };
66 | }
67 |
--------------------------------------------------------------------------------
/lib/src/vod/player_define.dart:
--------------------------------------------------------------------------------
1 | ///
2 | /// Created by wei on 2021/11/5.
3 | ///
4 |
5 | class PlayerValue {
6 | final PlayerState state;
7 |
8 | double duration = 0;
9 | double progress = 0;
10 |
11 | PlayerValue.uninitialized() : this(state: PlayerState.stopped);
12 |
13 | PlayerValue({required this.state});
14 |
15 | PlayerValue copyWith({PlayerState? state}) {
16 | return PlayerValue(state: state ?? this.state);
17 | }
18 | }
19 |
20 | enum PlayerState {
21 | paused, // 暂停播放
22 | failed, // 播放失败
23 | buffering, // 缓冲中
24 | playing, // 播放中
25 | stopped, // 停止播放
26 | disposed // 控件释放了
27 | }
28 |
29 | enum RenderRotation {
30 | homeOrientaionRight, //< HOME 键在右边,横屏模式
31 | homeOrientationDown, //< HOME 键在下面,手机直播中最常见的竖屏直播模式
32 | homeOrientationLeft, //< HOME 键在左边,横屏模式
33 | homeOrientationUp, //< HOME 键在上边,竖屏直播(适合小米 MIX2)
34 | }
35 |
36 | ///
37 | /// 渲染糊弄
38 | ///
39 | enum RenderType {
40 | texture, //外接纹理
41 | platformView
42 | }
43 |
--------------------------------------------------------------------------------
/lib/src/vod/vod_player.dart:
--------------------------------------------------------------------------------
1 | ///
2 | /// Created by wei on 2021/11/5.
3 | ///
4 | ///
5 | import 'package:flt_video_player/src/vod/platform_video_view.dart';
6 | import 'package:flt_video_player/src/vod/player_define.dart';
7 | import 'package:flt_video_player/src/vod/vodplayer_controller.dart';
8 | import 'package:flutter/widgets.dart';
9 |
10 | class VodPlayer extends StatefulWidget {
11 | final VodPlayerController controller;
12 |
13 | const VodPlayer({Key? key, required this.controller}) : super(key: key);
14 |
15 | @override
16 | _VodPlayerState createState() => _VodPlayerState();
17 | }
18 |
19 | class _VodPlayerState extends State {
20 | int _textureId = -1;
21 |
22 | @override
23 | void initState() {
24 | super.initState();
25 |
26 | if (widget.controller.renderType == RenderType.texture) {
27 | widget.controller.textureId.then((value) {
28 | setState(() {
29 | _textureId = value;
30 | });
31 | });
32 | }
33 | }
34 |
35 | @override
36 | Widget build(BuildContext context) {
37 | return widget.controller.renderType == RenderType.texture
38 | ? (_textureId == -1 ? Container() : Texture(textureId: _textureId))
39 | : PlatformVideoView(
40 | onPlatformViewCreated: widget.controller.onPlatformViewCreated,
41 | );
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/lib/src/vod/vodplayer_controller.dart:
--------------------------------------------------------------------------------
1 | import 'dart:async';
2 | import 'dart:core';
3 |
4 | import 'package:flt_video_player/flt_video_player.dart';
5 | import 'package:flt_video_player/src/plugin.dart';
6 | import 'package:flt_video_player/src/vod/player_define.dart';
7 | import 'package:flutter/cupertino.dart';
8 | import 'package:flutter/foundation.dart';
9 | import 'package:flutter/services.dart';
10 |
11 | ///
12 | /// Created by wei on 2021/11/5.
13 | ///
14 |
15 | class VodPlayerController extends ChangeNotifier
16 | implements ValueListenable {
17 | int _playerId = -1; //播放器 id
18 |
19 | //播放器调用通道
20 | MethodChannel? _channel;
21 |
22 | late final Completer _initPlayer;
23 | late final Completer _createTexture;
24 |
25 | bool _isDisposed = false;
26 | bool _isNeedDisposed = false;
27 |
28 | late PlayerValue _value;
29 | PlayerState? _state;
30 |
31 | //播放事件广播器
32 | final StreamController