Skip to content
项目
群组
代码片段
帮助
当前项目
正在载入...
登录 / 注册
切换导航面板
T
tx_player_fork
项目
项目
详情
活动
周期分析
仓库
仓库
文件
提交
分支
标签
贡献者
图表
比较
统计图
议题
0
议题
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
CI / CD
CI / CD
流水线
作业
日程
统计图
Wiki
Wiki
代码片段
代码片段
成员
成员
折叠边栏
关闭边栏
活动
图像
聊天
创建新问题
作业
提交
问题看板
Open sidebar
蒋俊
tx_player_fork
Commits
efb9316d
提交
efb9316d
authored
9月 02, 2022
作者:
kongdywang
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
SuperPlayer support live for Android&IOS platform
上级
2a99865e
隐藏空白字符变更
内嵌
并排
正在显示
36 个修改的文件
包含
1260 行增加
和
574 行删除
+1260
-574
README.md
Flutter/README.md
+2
-0
FTXLivePlayer.java
.../src/main/java/com/tencent/vod/flutter/FTXLivePlayer.java
+125
-40
FTXPIPManager.java
.../src/main/java/com/tencent/vod/flutter/FTXPIPManager.java
+47
-29
FTXTransformation.java
.../main/java/com/tencent/vod/flutter/FTXTransformation.java
+54
-20
FTXVodPlayer.java
...d/src/main/java/com/tencent/vod/flutter/FTXVodPlayer.java
+1
-2
SuperPlayerPlugin.java
.../main/java/com/tencent/vod/flutter/SuperPlayerPlugin.java
+5
-1
AndroidManifest.xml
Flutter/example/android/app/src/debug/AndroidManifest.xml
+0
-14
demo_superplayer.dart
Flutter/example/lib/demo_superplayer.dart
+102
-27
demo_txLiveplayer.dart
Flutter/example/lib/demo_txLiveplayer.dart
+6
-5
demo_txvodplayer.dart
Flutter/example/lib/demo_txvodplayer.dart
+3
-3
color_resource.dart
Flutter/example/lib/superplayer/common/color_resource.dart
+2
-0
superplayer_define.dart
...ter/example/lib/superplayer/model/superplayer_define.dart
+8
-1
superplayer_controller.dart
Flutter/example/lib/superplayer/superplayer_controller.dart
+315
-129
superplayer_observer.dart
Flutter/example/lib/superplayer/superplayer_observer.dart
+3
-3
superplayer_bottom_view.dart
...r/example/lib/superplayer/ui/superplayer_bottom_view.dart
+30
-30
superplayer_cover_view.dart
...er/example/lib/superplayer/ui/superplayer_cover_view.dart
+6
-2
superplayer_more_view.dart
...ter/example/lib/superplayer/ui/superplayer_more_view.dart
+20
-5
superplayer_title_view.dart
...er/example/lib/superplayer/ui/superplayer_title_view.dart
+2
-2
superplayer_video_slider.dart
.../example/lib/superplayer/ui/superplayer_video_slider.dart
+27
-15
superplayer_widget.dart
Flutter/example/lib/superplayer/ui/superplayer_widget.dart
+80
-103
pubspec.yaml
Flutter/example/pubspec.yaml
+1
-1
FTXLivePlayer.m
Flutter/ios/Classes/FTXLivePlayer.m
+41
-15
FTXTransformation.h
Flutter/ios/Classes/FTXTransformation.h
+3
-1
FTXTransformation.m
Flutter/ios/Classes/FTXTransformation.m
+20
-1
FTXVodPlayer.m
Flutter/ios/Classes/FTXVodPlayer.m
+1
-1
SuperPlayerPlugin.m
Flutter/ios/Classes/SuperPlayerPlugin.m
+4
-0
txplayer_holder.dart
Flutter/lib/Core/provider/txplayer_holder.dart
+15
-0
superplayer_plugin.dart
Flutter/lib/Core/superplayer_plugin.dart
+13
-0
txliveplayer_config.dart
Flutter/lib/Core/txliveplayer_config.dart
+65
-0
txliveplayer_controller.dart
Flutter/lib/Core/txliveplayer_controller.dart
+141
-76
txplayer_controller.dart
Flutter/lib/Core/txplayer_controller.dart
+15
-0
txplayer_define.dart
Flutter/lib/Core/txplayer_define.dart
+1
-1
txplayer_widget.dart
Flutter/lib/Core/txplayer_widget.dart
+61
-32
txvodplayer_controller.dart
Flutter/lib/Core/txvodplayer_controller.dart
+34
-11
super_player.dart
Flutter/lib/super_player.dart
+5
-2
pubspec.yaml
Flutter/pubspec.yaml
+2
-2
没有找到文件。
Flutter/README.md
浏览文件 @
efb9316d
...
...
@@ -47,6 +47,8 @@
### `pubspec.yaml`配置
**推荐flutter sdk 版本 3.0.0 及以上**
集成LiteAVSDK_Player版本,默认情况下也是集成此版本。在
`pubspec.yaml`
中增加配置
```
yaml
...
...
Flutter/android/src/main/java/com/tencent/vod/flutter/FTXLivePlayer.java
浏览文件 @
efb9316d
...
...
@@ -11,12 +11,11 @@ import androidx.annotation.NonNull;
import
com.tencent.rtmp.ITXLivePlayListener
;
import
com.tencent.rtmp.TXLiveBase
;
import
com.tencent.rtmp.TXLiveConstants
;
import
com.tencent.rtmp.TXLivePlayConfig
;
import
com.tencent.rtmp.TXLivePlayer
;
import
java.util.HashMap
;
import
java.util.Map
;
import
java.util.Set
;
import
io.flutter.embedding.engine.plugins.FlutterPlugin
;
import
io.flutter.plugin.common.EventChannel
;
...
...
@@ -44,16 +43,45 @@ public class FTXLivePlayer extends FTXBasePlayer implements MethodChannel.Method
private
TXLivePlayer
mLivePlayer
;
private
static
final
int
Uninitialized
=
-
101
;
private
boolean
mEnableHardwareDecode
=
true
;
private
boolean
mHardwareDecodeFail
=
false
;
private
TextureRegistry
.
SurfaceTextureEntry
mSurfaceTextureEntry
;
private
Activity
mActivity
;
public
FTXLivePlayer
(
FlutterPlugin
.
FlutterPluginBinding
flutterPluginBinding
,
Activity
activity
)
{
private
final
FTXPIPManager
mPipManager
;
private
FTXPIPManager
.
PipParams
mPipParams
;
private
final
FTXPIPManager
.
PipCallback
pipCallback
=
new
FTXPIPManager
.
PipCallback
()
{
@Override
public
void
onPlayBack
()
{
// pip not support playback
}
@Override
public
void
onResumeOrPlay
()
{
boolean
isPlaying
=
isPlaying
();
if
(
isPlaying
)
{
pause
();
}
else
{
resume
();
}
// isPlaying取反,点击暂停/播放之后,播放状态会变化
mPipManager
.
updatePipActions
(!
isPlaying
,
mPipParams
);
}
@Override
public
void
onPlayForward
()
{
// pip not support forward
}
};
public
FTXLivePlayer
(
FlutterPlugin
.
FlutterPluginBinding
flutterPluginBinding
,
Activity
activity
,
FTXPIPManager
pipManager
)
{
super
();
mFlutterPluginBinding
=
flutterPluginBinding
;
mActivity
=
activity
;
mPipManager
=
pipManager
;
mSurfaceTextureEntry
=
mFlutterPluginBinding
.
getTextureRegistry
().
createSurfaceTexture
();
mSurfaceTextureEntry
=
mFlutterPluginBinding
.
getTextureRegistry
().
createSurfaceTexture
();
mSurfaceTexture
=
mSurfaceTextureEntry
.
surfaceTexture
();
mSurface
=
new
Surface
(
mSurfaceTexture
);
...
...
@@ -115,8 +143,17 @@ public class FTXLivePlayer extends FTXBasePlayer implements MethodChannel.Method
}
@Override
public
void
onPlayEvent
(
int
i
,
Bundle
bundle
)
{
mEventSink
.
success
(
CommonUtil
.
getParams
(
i
,
bundle
));
public
void
onPlayEvent
(
int
event
,
Bundle
bundle
)
{
if
(
event
==
TXLiveConstants
.
PLAY_EVT_CHANGE_RESOLUTION
)
{
int
width
=
bundle
.
getInt
(
TXLiveConstants
.
EVT_PARAM1
,
0
);
int
height
=
bundle
.
getInt
(
TXLiveConstants
.
EVT_PARAM2
,
0
);
if
(!
mEnableHardwareDecode
||
mHardwareDecodeFail
)
{
setDefaultBufferSizeForSoftDecode
(
width
,
height
);
}
}
else
if
(
event
==
TXLiveConstants
.
PLAY_WARNING_HW_ACCELERATION_FAIL
)
{
mHardwareDecodeFail
=
true
;
}
mEventSink
.
success
(
CommonUtil
.
getParams
(
event
,
bundle
));
}
@Override
...
...
@@ -124,68 +161,100 @@ public class FTXLivePlayer extends FTXBasePlayer implements MethodChannel.Method
mNetStatusSink
.
success
(
CommonUtil
.
getParams
(
0
,
bundle
));
}
// surface 的大小默认是宽高为1,当硬解失败时或使用软解时,软解会依赖surface的窗口渲染,不更新会导致只有1px的内容
private
void
setDefaultBufferSizeForSoftDecode
(
int
width
,
int
height
)
{
if
(
mSurfaceTextureEntry
!=
null
&&
mSurfaceTextureEntry
.
surfaceTexture
()
!=
null
)
{
SurfaceTexture
surfaceTexture
=
mSurfaceTextureEntry
.
surfaceTexture
();
surfaceTexture
.
setDefaultBufferSize
(
width
,
height
);
if
(
mSurface
!=
null
)
{
mSurface
.
release
();
}
mSurface
=
new
Surface
(
surfaceTexture
);
mLivePlayer
.
setSurface
(
mSurface
);
}
}
@Override
public
void
onMethodCall
(
@NonNull
MethodCall
call
,
@NonNull
MethodChannel
.
Result
result
)
{
if
(
call
.
method
.
equals
(
"init"
))
{
if
(
call
.
method
.
equals
(
"init"
))
{
boolean
onlyAudio
=
call
.
argument
(
"onlyAudio"
);
long
id
=
init
(
onlyAudio
);
result
.
success
(
id
);
}
else
if
(
call
.
method
.
equals
(
"setIsAutoPlay"
))
{
}
else
if
(
call
.
method
.
equals
(
"setIsAutoPlay"
))
{
boolean
loop
=
call
.
argument
(
"isAutoPlay"
);
setIsAutoPlay
(
loop
);
result
.
success
(
null
);
}
else
if
(
call
.
method
.
equals
(
"play"
))
{
}
else
if
(
call
.
method
.
equals
(
"play"
))
{
String
url
=
call
.
argument
(
"url"
);
int
type
=
call
.
argument
(
"playType"
);
int
r
=
startPlay
(
url
,
type
);
int
r
=
startPlay
(
url
,
type
);
result
.
success
(
r
);
}
else
if
(
call
.
method
.
equals
(
"stop"
))
{
}
else
if
(
call
.
method
.
equals
(
"stop"
))
{
Boolean
isNeedClear
=
call
.
argument
(
"isNeedClear"
);
int
r
=
stopPlay
(
isNeedClear
);
result
.
success
(
r
);
}
else
if
(
call
.
method
.
equals
(
"isPlaying"
))
{
}
else
if
(
call
.
method
.
equals
(
"isPlaying"
))
{
boolean
r
=
isPlaying
();
result
.
success
(
r
);
}
else
if
(
call
.
method
.
equals
(
"pause"
))
{
}
else
if
(
call
.
method
.
equals
(
"pause"
))
{
pause
();
result
.
success
(
null
);
}
else
if
(
call
.
method
.
equals
(
"resume"
))
{
}
else
if
(
call
.
method
.
equals
(
"resume"
))
{
resume
();
result
.
success
(
null
);
}
else
if
(
call
.
method
.
equals
(
"setMute"
))
{
}
else
if
(
call
.
method
.
equals
(
"setMute"
))
{
boolean
mute
=
call
.
argument
(
"mute"
);
setMute
(
mute
);
result
.
success
(
null
);
}
else
if
(
call
.
method
.
equals
(
"seek"
))
{
}
else
if
(
call
.
method
.
equals
(
"seek"
))
{
result
.
notImplemented
();
}
else
if
(
call
.
method
.
equals
(
"setRate"
))
{
}
else
if
(
call
.
method
.
equals
(
"setRate"
))
{
result
.
notImplemented
();
}
else
if
(
call
.
method
.
equals
(
"setVolume"
))
{
}
else
if
(
call
.
method
.
equals
(
"setVolume"
))
{
Integer
volume
=
call
.
argument
(
"volume"
);
setVolume
(
volume
);
result
.
success
(
null
);
}
else
if
(
call
.
method
.
equals
(
"setRenderRotation"
))
{
}
else
if
(
call
.
method
.
equals
(
"setRenderRotation"
))
{
int
rotation
=
call
.
argument
(
"rotation"
);
setRenderRotation
(
rotation
);
result
.
success
(
null
);
}
else
if
(
call
.
method
.
equals
(
"setLiveMode"
))
{
}
else
if
(
call
.
method
.
equals
(
"setLiveMode"
))
{
int
type
=
call
.
argument
(
"type"
);
setLiveMode
(
type
);
result
.
success
(
null
);
}
else
if
(
call
.
method
.
equals
(
"switchStream"
))
{
}
else
if
(
call
.
method
.
equals
(
"switchStream"
))
{
String
url
=
call
.
argument
(
"url"
);
switchStream
(
url
);
result
.
success
(
null
);
}
else
if
(
call
.
method
.
equals
(
"setAppID"
))
{
int
switchResult
=
switchStream
(
url
);
result
.
success
(
switchResult
);
}
else
if
(
call
.
method
.
equals
(
"setAppID"
))
{
String
appId
=
call
.
argument
(
"appId"
);
setAppID
(
appId
);
result
.
success
(
null
);
}
else
if
(
call
.
method
.
equals
(
"prepareLiveSeek"
))
{
}
else
if
(
call
.
method
.
equals
(
"prepareLiveSeek"
))
{
result
.
notImplemented
();
}
else
if
(
call
.
method
.
equals
(
"resumeLive"
))
{
}
else
if
(
call
.
method
.
equals
(
"resumeLive"
))
{
int
r
=
resumeLive
();
result
.
success
(
r
);
}
else
{
}
else
if
(
call
.
method
.
equals
(
"enableHardwareDecode"
))
{
Boolean
enable
=
call
.
argument
(
"enable"
);
boolean
r
=
enableHardwareDecode
(
enable
);
result
.
success
(
r
);
}
else
if
(
call
.
method
.
equals
(
"enterPictureInPictureMode"
))
{
String
playBackAssetPath
=
call
.
argument
(
"backIcon"
);
String
playResumeAssetPath
=
call
.
argument
(
"playIcon"
);
String
playPauseAssetPath
=
call
.
argument
(
"pauseIcon"
);
String
playForwardAssetPath
=
call
.
argument
(
"forwardIcon"
);
mPipManager
.
addCallback
(
getPlayerId
(),
pipCallback
);
mPipParams
=
new
FTXPIPManager
.
PipParams
(
playBackAssetPath
,
playResumeAssetPath
,
playPauseAssetPath
,
playForwardAssetPath
,
getPlayerId
(),
false
,
false
,
true
);
int
pipResult
=
mPipManager
.
enterPip
(
isPlaying
(),
mPipParams
);
result
.
success
(
pipResult
);
}
else
if
(
call
.
method
.
equals
(
"setConfig"
))
{
Map
<
Object
,
Object
>
config
=
call
.
argument
(
"config"
);
setPlayConfig
(
config
);
result
.
success
(
null
);
}
else
{
result
.
notImplemented
();
}
}
...
...
@@ -199,7 +268,8 @@ public class FTXLivePlayer extends FTXBasePlayer implements MethodChannel.Method
return
mSurfaceTextureEntry
==
null
?
-
1
:
mSurfaceTextureEntry
.
id
();
}
private
int
mSurfaceWidth
,
mSurfaceHeight
=
0
;
private
int
mSurfaceWidth
,
mSurfaceHeight
=
0
;
int
startPlay
(
String
url
,
int
type
)
{
Log
.
d
(
TAG
,
"startPlay:"
);
if
(
mLivePlayer
!=
null
)
{
...
...
@@ -213,15 +283,15 @@ public class FTXLivePlayer extends FTXBasePlayer implements MethodChannel.Method
public
void
onRenderVideoFrame
(
TXLivePlayer
.
TXLiteAVTexture
texture
)
{
int
width
=
texture
.
width
;
int
height
=
texture
.
height
;
if
(
width
!=
mSurfaceWidth
||
height
!=
mSurfaceHeight
)
{
Log
.
d
(
TAG
,
"onRenderVideoFrame: width="
+
texture
.
width
+
",height="
+
texture
.
height
);
mLivePlayer
.
setSurfaceSize
(
width
,
height
);
mSurfaceTexture
.
setDefaultBufferSize
(
width
,
height
);
if
(
width
!=
mSurfaceWidth
||
height
!=
mSurfaceHeight
)
{
Log
.
d
(
TAG
,
"onRenderVideoFrame: width="
+
texture
.
width
+
",height="
+
texture
.
height
);
mLivePlayer
.
setSurfaceSize
(
width
,
height
);
mSurfaceTexture
.
setDefaultBufferSize
(
width
,
height
);
mSurfaceWidth
=
width
;
mSurfaceHeight
=
height
;
}
}
},
null
);
},
null
);
return
mLivePlayer
.
startPlay
(
url
,
type
);
}
return
Uninitialized
;
...
...
@@ -231,6 +301,7 @@ public class FTXLivePlayer extends FTXBasePlayer implements MethodChannel.Method
if
(
mLivePlayer
!=
null
)
{
return
mLivePlayer
.
stopPlay
(
isNeedClearLastImg
);
}
mHardwareDecodeFail
=
false
;
return
Uninitialized
;
}
...
...
@@ -297,12 +368,12 @@ public class FTXLivePlayer extends FTXBasePlayer implements MethodChannel.Method
config
.
setAutoAdjustCacheTime
(
true
);
config
.
setMinAutoAdjustCacheTime
(
1
);
config
.
setMaxAutoAdjustCacheTime
(
3
);
}
else
if
(
type
==
1
)
{
}
else
if
(
type
==
1
)
{
//极速模式
config
.
setAutoAdjustCacheTime
(
true
);
config
.
setMinAutoAdjustCacheTime
(
1
);
config
.
setMaxAutoAdjustCacheTime
(
1
);
}
else
{
}
else
{
//流畅模式
config
.
setAutoAdjustCacheTime
(
false
);
config
.
setCacheTime
(
5
);
...
...
@@ -312,10 +383,11 @@ public class FTXLivePlayer extends FTXBasePlayer implements MethodChannel.Method
}
}
void
switchStream
(
String
url
)
{
int
switchStream
(
String
url
)
{
if
(
mLivePlayer
!=
null
)
{
mLivePlayer
.
switchStream
(
url
);
return
mLivePlayer
.
switchStream
(
url
);
}
return
-
1
;
}
private
void
setAppID
(
String
appId
)
{
...
...
@@ -326,7 +398,6 @@ public class FTXLivePlayer extends FTXBasePlayer implements MethodChannel.Method
if
(
mLivePlayer
!=
null
)
{
return
mLivePlayer
.
prepareLiveSeek
(
domain
,
bizId
);
}
return
Uninitialized
;
}
...
...
@@ -334,13 +405,27 @@ public class FTXLivePlayer extends FTXBasePlayer implements MethodChannel.Method
if
(
mLivePlayer
!=
null
)
{
return
mLivePlayer
.
resumeLive
();
}
return
Uninitialized
;
}
private
boolean
enableHardwareDecode
(
Boolean
enable
)
{
if
(
mLivePlayer
!=
null
)
{
mEnableHardwareDecode
=
enable
;
return
mLivePlayer
.
enableHardwareDecode
(
enable
);
}
return
false
;
}
private
void
setRenderMode
(
int
renderMode
)
{
if
(
mLivePlayer
!=
null
)
{
mLivePlayer
.
setRenderMode
(
renderMode
);
}
}
void
setPlayConfig
(
Map
<
Object
,
Object
>
config
)
{
if
(
mLivePlayer
!=
null
)
{
TXLivePlayConfig
playConfig
=
FTXTransformation
.
transformToLiveConfig
(
config
);
mLivePlayer
.
setConfig
(
playConfig
);
}
}
}
Flutter/android/src/main/java/com/tencent/vod/flutter/FTXPIPManager.java
浏览文件 @
efb9316d
...
...
@@ -205,40 +205,45 @@ public class FTXPIPManager {
return
;
}
if
(
android
.
os
.
Build
.
VERSION
.
SDK_INT
>=
android
.
os
.
Build
.
VERSION_CODES
.
O
)
{
List
<
RemoteAction
>
actions
=
new
ArrayList
<>();
// play back
Bundle
backData
=
new
Bundle
();
backData
.
putInt
(
FTXEvent
.
EXTRA_NAME_PLAY_OP
,
FTXEvent
.
EXTRA_PIP_PLAY_BACK
);
backData
.
putInt
(
FTXEvent
.
EXTRA_NAME_PLAYER_ID
,
params
.
mCurrentPlayerId
);
Intent
backIntent
=
new
Intent
(
FTXEvent
.
ACTION_PIP_PLAY_CONTROL
).
putExtras
(
backData
);
PendingIntent
preIntent
=
PendingIntent
.
getBroadcast
(
mActivity
,
FTXEvent
.
EXTRA_PIP_PLAY_BACK
,
backIntent
,
PendingIntent
.
FLAG_UPDATE_CURRENT
|
PendingIntent
.
FLAG_IMMUTABLE
);
RemoteAction
preAction
=
new
RemoteAction
(
getBackIcon
(
params
),
"skipPre"
,
"skip pre"
,
preIntent
);
if
(
params
.
mIsNeedPlayBack
)
{
Bundle
backData
=
new
Bundle
();
backData
.
putInt
(
FTXEvent
.
EXTRA_NAME_PLAY_OP
,
FTXEvent
.
EXTRA_PIP_PLAY_BACK
);
backData
.
putInt
(
FTXEvent
.
EXTRA_NAME_PLAYER_ID
,
params
.
mCurrentPlayerId
);
Intent
backIntent
=
new
Intent
(
FTXEvent
.
ACTION_PIP_PLAY_CONTROL
).
putExtras
(
backData
);
PendingIntent
preIntent
=
PendingIntent
.
getBroadcast
(
mActivity
,
FTXEvent
.
EXTRA_PIP_PLAY_BACK
,
backIntent
,
PendingIntent
.
FLAG_UPDATE_CURRENT
|
PendingIntent
.
FLAG_IMMUTABLE
);
RemoteAction
preAction
=
new
RemoteAction
(
getBackIcon
(
params
),
"skipPre"
,
"skip pre"
,
preIntent
);
actions
.
add
(
preAction
);
}
// resume or pause
Bundle
playOrPauseData
=
new
Bundle
();
playOrPauseData
.
putInt
(
FTXEvent
.
EXTRA_NAME_PLAYER_ID
,
params
.
mCurrentPlayerId
);
playOrPauseData
.
putInt
(
FTXEvent
.
EXTRA_NAME_PLAY_OP
,
FTXEvent
.
EXTRA_PIP_PLAY_RESUME_OR_PAUSE
);
Intent
playOrPauseIntent
=
new
Intent
(
FTXEvent
.
ACTION_PIP_PLAY_CONTROL
).
putExtras
(
playOrPauseData
);
Icon
playIcon
=
isPlaying
?
getPauseIcon
(
params
)
:
getPlayIcon
(
params
);
PendingIntent
playIntent
=
PendingIntent
.
getBroadcast
(
mActivity
,
FTXEvent
.
EXTRA_PIP_PLAY_RESUME_OR_PAUSE
,
playOrPauseIntent
,
PendingIntent
.
FLAG_UPDATE_CURRENT
|
PendingIntent
.
FLAG_IMMUTABLE
);
RemoteAction
playOrPauseAction
=
new
RemoteAction
(
playIcon
,
"playOrPause"
,
"play Or Pause"
,
playIntent
);
if
(
params
.
mIsNeedPlayControl
)
{
Bundle
playOrPauseData
=
new
Bundle
();
playOrPauseData
.
putInt
(
FTXEvent
.
EXTRA_NAME_PLAYER_ID
,
params
.
mCurrentPlayerId
);
playOrPauseData
.
putInt
(
FTXEvent
.
EXTRA_NAME_PLAY_OP
,
FTXEvent
.
EXTRA_PIP_PLAY_RESUME_OR_PAUSE
);
Intent
playOrPauseIntent
=
new
Intent
(
FTXEvent
.
ACTION_PIP_PLAY_CONTROL
).
putExtras
(
playOrPauseData
);
Icon
playIcon
=
isPlaying
?
getPauseIcon
(
params
)
:
getPlayIcon
(
params
);
PendingIntent
playIntent
=
PendingIntent
.
getBroadcast
(
mActivity
,
FTXEvent
.
EXTRA_PIP_PLAY_RESUME_OR_PAUSE
,
playOrPauseIntent
,
PendingIntent
.
FLAG_UPDATE_CURRENT
|
PendingIntent
.
FLAG_IMMUTABLE
);
RemoteAction
playOrPauseAction
=
new
RemoteAction
(
playIcon
,
"playOrPause"
,
"play Or Pause"
,
playIntent
);
actions
.
add
(
playOrPauseAction
);
}
// forward
Bundle
forwardData
=
new
Bundle
();
forwardData
.
putInt
(
FTXEvent
.
EXTRA_NAME_PLAY_OP
,
FTXEvent
.
EXTRA_PIP_PLAY_FORWARD
);
forwardData
.
putInt
(
FTXEvent
.
EXTRA_NAME_PLAYER_ID
,
params
.
mCurrentPlayerId
);
Intent
forwardIntent
=
new
Intent
(
FTXEvent
.
ACTION_PIP_PLAY_CONTROL
).
putExtras
(
forwardData
);
PendingIntent
nextIntent
=
PendingIntent
.
getBroadcast
(
mActivity
,
FTXEvent
.
EXTRA_PIP_PLAY_FORWARD
,
forwardIntent
,
PendingIntent
.
FLAG_UPDATE_CURRENT
|
PendingIntent
.
FLAG_IMMUTABLE
);
RemoteAction
nextAction
=
new
RemoteAction
(
getForwardIcon
(
params
),
"skipNext"
,
"skip next"
,
nextIntent
);
List
<
RemoteAction
>
actions
=
new
ArrayList
<>();
actions
.
add
(
preAction
);
actions
.
add
(
playOrPauseAction
);
actions
.
add
(
nextAction
);
if
(
params
.
mIsNeedPlayForward
)
{
Bundle
forwardData
=
new
Bundle
();
forwardData
.
putInt
(
FTXEvent
.
EXTRA_NAME_PLAY_OP
,
FTXEvent
.
EXTRA_PIP_PLAY_FORWARD
);
forwardData
.
putInt
(
FTXEvent
.
EXTRA_NAME_PLAYER_ID
,
params
.
mCurrentPlayerId
);
Intent
forwardIntent
=
new
Intent
(
FTXEvent
.
ACTION_PIP_PLAY_CONTROL
).
putExtras
(
forwardData
);
PendingIntent
nextIntent
=
PendingIntent
.
getBroadcast
(
mActivity
,
FTXEvent
.
EXTRA_PIP_PLAY_FORWARD
,
forwardIntent
,
PendingIntent
.
FLAG_UPDATE_CURRENT
|
PendingIntent
.
FLAG_IMMUTABLE
);
RemoteAction
nextAction
=
new
RemoteAction
(
getForwardIcon
(
params
),
"skipNext"
,
"skip next"
,
nextIntent
);
actions
.
add
(
nextAction
);
}
params
.
mPipParams
.
setActions
(
actions
);
mActivity
.
setPictureInPictureParams
(
params
.
mPipParams
.
build
());
...
...
@@ -285,6 +290,9 @@ public class FTXPIPManager {
String
mPlayForwardAssetPath
;
int
mCurrentPlayerId
;
protected
PictureInPictureParams
.
Builder
mPipParams
;
private
boolean
mIsNeedPlayBack
=
true
;
private
boolean
mIsNeedPlayForward
=
true
;
private
boolean
mIsNeedPlayControl
=
true
;
/**
* @param mPlayBackAssetPath 回退按钮图片资源路径,传空则使用系统默认图标
...
...
@@ -295,11 +303,21 @@ public class FTXPIPManager {
*/
public
PipParams
(
String
mPlayBackAssetPath
,
String
mPlayResumeAssetPath
,
String
mPlayPauseAssetPath
,
String
mPlayForwardAssetPath
,
int
mCurrentPlayerId
)
{
this
(
mPlayBackAssetPath
,
mPlayResumeAssetPath
,
mPlayPauseAssetPath
,
mPlayForwardAssetPath
,
mCurrentPlayerId
,
true
,
true
,
true
);
}
public
PipParams
(
String
mPlayBackAssetPath
,
String
mPlayResumeAssetPath
,
String
mPlayPauseAssetPath
,
String
mPlayForwardAssetPath
,
int
mCurrentPlayerId
,
boolean
isNeedPlayBack
,
boolean
isNeedPlayForward
,
boolean
isNeedPlayControl
)
{
this
.
mPlayBackAssetPath
=
mPlayBackAssetPath
;
this
.
mPlayResumeAssetPath
=
mPlayResumeAssetPath
;
this
.
mPlayPauseAssetPath
=
mPlayPauseAssetPath
;
this
.
mPlayForwardAssetPath
=
mPlayForwardAssetPath
;
this
.
mCurrentPlayerId
=
mCurrentPlayerId
;
this
.
mIsNeedPlayBack
=
isNeedPlayBack
;
this
.
mIsNeedPlayForward
=
isNeedPlayForward
;
this
.
mIsNeedPlayControl
=
isNeedPlayControl
;
}
}
...
...
Flutter/android/src/main/java/com/tencent/vod/flutter/FTXTransformation.java
浏览文件 @
efb9316d
...
...
@@ -3,7 +3,7 @@ package com.tencent.vod.flutter;
import
android.text.TextUtils
;
import
com.tencent.rtmp.TX
PlayerGlobalSettin
g
;
import
com.tencent.rtmp.TX
LivePlayConfi
g
;
import
com.tencent.rtmp.TXVodPlayConfig
;
import
java.util.HashMap
;
...
...
@@ -15,75 +15,61 @@ import java.util.Map;
public
class
FTXTransformation
{
@SuppressWarnings
(
"unchecked"
)
public
static
TXVodPlayConfig
transformToConfig
(
Map
<
Object
,
Object
>
config
)
{
public
static
TXVodPlayConfig
transformTo
Vod
Config
(
Map
<
Object
,
Object
>
config
)
{
TXVodPlayConfig
playConfig
=
new
TXVodPlayConfig
();
Integer
connectRetryCount
=
(
Integer
)
config
.
get
(
"connectRetryCount"
);
if
(
intIsNotEmpty
(
connectRetryCount
))
{
playConfig
.
setConnectRetryCount
(
connectRetryCount
);
}
Integer
connectRetryInterval
=
(
Integer
)
config
.
get
(
"connectRetryInterval"
);
if
(
intIsNotEmpty
(
connectRetryInterval
))
{
playConfig
.
setConnectRetryInterval
(
connectRetryInterval
);
}
Integer
timeout
=
(
Integer
)
config
.
get
(
"timeout"
);
if
(
intIsNotEmpty
(
timeout
))
{
playConfig
.
setTimeout
(
timeout
);
}
Integer
playerType
=
(
Integer
)
config
.
get
(
"playerType"
);
if
(
null
!=
playerType
)
{
playConfig
.
setPlayerType
(
playerType
);
}
Map
<
String
,
String
>
headers
=
(
Map
<
String
,
String
>)
config
.
get
(
"headers"
);
if
(
null
==
headers
)
{
headers
=
new
HashMap
<>();
}
playConfig
.
setHeaders
(
headers
);
Boolean
enableAccurateSeek
=
(
Boolean
)
config
.
get
(
"enableAccurateSeek"
);
if
(
null
!=
enableAccurateSeek
)
{
playConfig
.
setEnableAccurateSeek
(
enableAccurateSeek
);
}
Boolean
autoRotate
=
(
Boolean
)
config
.
get
(
"autoRotate"
);
if
(
null
!=
autoRotate
)
{
playConfig
.
setAutoRotate
(
autoRotate
);
}
Boolean
smoothSwitchBitrate
=
(
Boolean
)
config
.
get
(
"smoothSwitchBitrate"
);
if
(
null
!=
smoothSwitchBitrate
)
{
playConfig
.
setSmoothSwitchBitrate
(
smoothSwitchBitrate
);
}
String
cacheMp4ExtName
=
(
String
)
config
.
get
(
"cacheMp4ExtName"
);
if
(!
TextUtils
.
isEmpty
(
cacheMp4ExtName
))
{
playConfig
.
setCacheMp4ExtName
(
cacheMp4ExtName
);
}
Integer
progressInterval
=
(
Integer
)
config
.
get
(
"progressInterval"
);
if
(
intIsNotEmpty
(
progressInterval
))
{
playConfig
.
setProgressInterval
(
progressInterval
);
}
Integer
maxBufferSize
=
(
Integer
)
config
.
get
(
"maxBufferSize"
);
if
(
intIsNotEmpty
(
maxBufferSize
))
{
playConfig
.
setMaxBufferSize
(
maxBufferSize
);
}
Integer
maxPreloadSize
=
(
Integer
)
config
.
get
(
"maxPreloadSize"
);
if
(
intIsNotEmpty
(
maxPreloadSize
))
{
playConfig
.
setMaxPreloadSize
(
maxPreloadSize
);
}
Integer
firstStartPlayBufferTime
=
(
Integer
)
config
.
get
(
"firstStartPlayBufferTime"
);
if
(
null
!=
firstStartPlayBufferTime
)
{
playConfig
.
setFirstStartPlayBufferTime
(
firstStartPlayBufferTime
);
}
Integer
nextStartPlayBufferTime
=
(
Integer
)
config
.
get
(
"nextStartPlayBufferTime"
);
if
(
null
!=
nextStartPlayBufferTime
)
{
playConfig
.
setNextStartPlayBufferTime
(
nextStartPlayBufferTime
);
...
...
@@ -93,23 +79,19 @@ public class FTXTransformation {
if
(!
TextUtils
.
isEmpty
(
overlayKey
))
{
playConfig
.
setOverlayKey
(
overlayKey
);
}
String
overlayIv
=
(
String
)
config
.
get
(
"overlayIv"
);
if
(!
TextUtils
.
isEmpty
(
overlayIv
))
{
playConfig
.
setOverlayIv
(
overlayIv
);
}
Map
<
String
,
Object
>
extInfoMap
=
(
Map
<
String
,
Object
>)
config
.
get
(
"extInfoMap"
);
if
(
null
==
extInfoMap
)
{
extInfoMap
=
new
HashMap
<>();
}
playConfig
.
setExtInfo
(
extInfoMap
);
Boolean
enableRenderProcess
=
(
Boolean
)
config
.
get
(
"enableRenderProcess"
);
if
(
null
!=
enableRenderProcess
)
{
playConfig
.
setEnableRenderProcess
(
enableRenderProcess
);
}
String
preferredResolutionStr
=
(
String
)
config
.
get
(
"preferredResolution"
);
if
(
null
!=
preferredResolutionStr
)
{
long
preferredResolution
=
Long
.
parseLong
(
preferredResolutionStr
);
...
...
@@ -119,8 +101,60 @@ public class FTXTransformation {
return
playConfig
;
}
public
static
TXLivePlayConfig
transformToLiveConfig
(
Map
<
Object
,
Object
>
config
)
{
TXLivePlayConfig
livePlayConfig
=
new
TXLivePlayConfig
();
Double
cacheTime
=
(
Double
)
config
.
get
(
"cacheTime"
);
if
(
doubleIsNotEmpty
(
cacheTime
))
{
livePlayConfig
.
setCacheTime
(
cacheTime
.
floatValue
());
}
Double
maxAutoAdjustCacheTime
=
(
Double
)
config
.
get
(
"maxAutoAdjustCacheTime"
);
if
(
doubleIsNotEmpty
(
maxAutoAdjustCacheTime
))
{
livePlayConfig
.
setMaxAutoAdjustCacheTime
(
maxAutoAdjustCacheTime
.
floatValue
());
}
Double
minAutoAdjustCacheTime
=
(
Double
)
config
.
get
(
"minAutoAdjustCacheTime"
);
if
(
doubleIsNotEmpty
(
minAutoAdjustCacheTime
))
{
livePlayConfig
.
setMinAutoAdjustCacheTime
(
minAutoAdjustCacheTime
.
floatValue
());
}
Integer
videoBlockThreshold
=
(
Integer
)
config
.
get
(
"videoBlockThreshold"
);
if
(
intIsNotEmpty
(
videoBlockThreshold
))
{
livePlayConfig
.
setVideoBlockThreshold
(
videoBlockThreshold
);
}
Integer
connectRetryCount
=
(
Integer
)
config
.
get
(
"connectRetryCount"
);
if
(
intIsNotEmpty
(
connectRetryCount
))
{
livePlayConfig
.
setConnectRetryCount
(
connectRetryCount
);
}
Integer
connectRetryInterval
=
(
Integer
)
config
.
get
(
"connectRetryInterval"
);
if
(
intIsNotEmpty
(
connectRetryInterval
))
{
livePlayConfig
.
setConnectRetryInterval
(
connectRetryInterval
);
}
Boolean
autoAdjustCacheTime
=
(
Boolean
)
config
.
get
(
"autoAdjustCacheTime"
);
if
(
null
!=
autoAdjustCacheTime
)
{
livePlayConfig
.
setAutoAdjustCacheTime
(
autoAdjustCacheTime
);
}
Boolean
enableAec
=
(
Boolean
)
config
.
get
(
"enableAec"
);
if
(
null
!=
enableAec
)
{
livePlayConfig
.
setEnableAEC
(
enableAec
);
}
Boolean
enableMessage
=
(
Boolean
)
config
.
get
(
"enableMessage"
);
if
(
null
!=
enableMessage
)
{
livePlayConfig
.
setEnableMessage
(
enableMessage
);
}
Boolean
enableMetaData
=
(
Boolean
)
config
.
get
(
"enableMetaData"
);
if
(
null
!=
enableMetaData
)
{
livePlayConfig
.
setEnableMetaData
(
enableMetaData
);
}
String
flvSessionKey
=
(
String
)
config
.
get
(
"flvSessionKey"
);
if
(!
TextUtils
.
isEmpty
(
flvSessionKey
))
{
livePlayConfig
.
setFlvSessionKey
(
flvSessionKey
);
}
return
livePlayConfig
;
}
private
static
boolean
intIsNotEmpty
(
Integer
value
)
{
return
null
!=
value
&&
value
>
0
;
}
private
static
boolean
doubleIsNotEmpty
(
Double
value
)
{
return
null
!=
value
&&
value
>
0
;
}
}
Flutter/android/src/main/java/com/tencent/vod/flutter/FTXVodPlayer.java
浏览文件 @
efb9316d
...
...
@@ -22,7 +22,6 @@ import java.util.ArrayList;
import
java.util.HashMap
;
import
java.util.List
;
import
java.util.Map
;
import
java.util.Set
;
import
io.flutter.embedding.engine.plugins.FlutterPlugin
;
import
io.flutter.plugin.common.EventChannel
;
...
...
@@ -510,7 +509,7 @@ public class FTXVodPlayer extends FTXBasePlayer implements MethodChannel.MethodC
void
setPlayConfig
(
Map
<
Object
,
Object
>
config
)
{
if
(
mVodPlayer
!=
null
)
{
TXVodPlayConfig
playConfig
=
FTXTransformation
.
transformToConfig
(
config
);
TXVodPlayConfig
playConfig
=
FTXTransformation
.
transformTo
Vod
Config
(
config
);
mVodPlayer
.
setConfig
(
playConfig
);
}
}
...
...
Flutter/android/src/main/java/com/tencent/vod/flutter/SuperPlayerPlugin.java
浏览文件 @
efb9316d
...
...
@@ -133,7 +133,7 @@ public class SuperPlayerPlugin implements FlutterPlugin, MethodCallHandler, Acti
mPlayers
.
append
(
playerId
,
player
);
result
.
success
(
playerId
);
}
else
if
(
call
.
method
.
equals
(
"createLivePlayer"
))
{
FTXLivePlayer
player
=
new
FTXLivePlayer
(
mFlutterPluginBinding
,
mActivityPluginBinding
.
getActivity
());
FTXLivePlayer
player
=
new
FTXLivePlayer
(
mFlutterPluginBinding
,
mActivityPluginBinding
.
getActivity
()
,
mTxPipManager
);
int
playerId
=
player
.
getPlayerId
();
mPlayers
.
append
(
playerId
,
player
);
result
.
success
(
playerId
);
...
...
@@ -210,6 +210,10 @@ public class SuperPlayerPlugin implements FlutterPlugin, MethodCallHandler, Acti
result
.
success
(
mTxPipManager
.
isSupportDevice
());
}
else
if
(
call
.
method
.
equals
(
"getLiteAVSDKVersion"
))
{
result
.
success
(
TXLiveBase
.
getSDKVersionStr
());
}
else
if
(
call
.
method
.
equals
(
"setGlobalEnv"
))
{
String
envConfig
=
call
.
argument
(
"envConfig"
);
int
setResult
=
TXLiveBase
.
setGlobalEnv
(
envConfig
);
result
.
success
(
setResult
);
}
else
{
result
.
notImplemented
();
}
...
...
Flutter/example/android/app/src/debug/AndroidManifest.xml
浏览文件 @
efb9316d
...
...
@@ -13,26 +13,12 @@
<uses-permission
android:name=
"android.permission.READ_EXTERNAL_STORAGE"
/>
<uses-feature
android:name=
"android.hardware.camera"
/>
<uses-feature
android:name=
"android.hardware.camera.autofocus"
/>
<uses-permission
android:name=
"android.permission.INTERNET"
/>
<uses-permission
android:name=
"android.permission.ACCESS_NETWORK_STATE"
/>
<uses-permission
android:name=
"android.permission.ACCESS_WIFI_STATE"
/>
<uses-permission
android:name=
"android.permission.READ_PHONE_STATE"
/>
<uses-permission
android:name=
"android.permission.CALL_PHONE"
/>
<uses-permission
android:name=
"android.permission.WRITE_EXTERNAL_STORAGE"
/>
<uses-permission
android:name=
"android.permission.READ_EXTERNAL_STORAGE"
/>
<uses-permission
android:name=
"android.permission.READ_LOGS"
/>
<uses-permission
android:name=
"android.permission.RECORD_AUDIO"
/>
<uses-permission
android:name=
"android.permission.CAMERA"
/>
<uses-permission
android:name=
"android.permission.CAPTURE_AUDIO_OUTPUT"
/>
<uses-permission
android:name=
"android.permission.CAPTURE_VIDEO_OUTPUT"
/>
<uses-permission
android:name=
"android.permission.MODIFY_AUDIO_SETTINGS"
/>
<uses-permission
android:name=
"android.permission.BLUETOOTH"
/>
<uses-permission
android:name=
"android.permission.SYSTEM_ALERT_WINDOW"
/>
<uses-permission
android:name=
"android.permission.CHANGE_CONFIGURATION"
/>
<uses-permission
android:name=
"android.permission.WRITE_SETTINGS"
/>
<!-- <android:uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/> -->
<uses-permission
android:name=
"android.permission.RECEIVE_BOOT_COMPLETED"
/>
</manifest>
Flutter/example/lib/demo_superplayer.dart
浏览文件 @
efb9316d
...
...
@@ -14,10 +14,20 @@ class DemoSuperplayer extends StatefulWidget {
}
class
_DemoSuperplayerState
extends
State
<
DemoSuperplayer
>
{
static
const
DEFAULT_PLACE_HOLDER
=
"http://xiaozhibo-10055601.file.myqcloud.com/coverImg.jpg"
;
List
<
SuperPlayerModel
>
videoModels
=
[];
bool
_isFullScreen
=
false
;
late
SuperPlayerController
_controller
;
StreamSubscription
?
simpleEventSubscription
;
int
tabSelectPos
=
0
;
TextStyle
_textStyleSelected
=
new
TextStyle
(
fontSize:
16
,
color:
Colors
.
white
);
TextStyle
_textStyleUnSelected
=
new
TextStyle
(
fontSize:
16
,
color:
Colors
.
grey
);
@override
void
initState
()
{
...
...
@@ -30,12 +40,14 @@ class _DemoSuperplayerState extends State<DemoSuperplayer> {
simpleEventSubscription
=
_controller
.
onSimplePlayerEventBroadcast
.
listen
((
event
)
{
String
evtName
=
event
[
"event"
];
if
(
evtName
==
SuperPlayerViewEvent
.
onStartFullScreenPlay
)
{
// enter fullscreen
}
else
if
(
evtName
==
SuperPlayerViewEvent
.
onStopFullScreenPlay
)
{
// exit fullscreen
}
else
{
print
(
evtName
);
}
});
ini
tData
();
_getLiveLis
tData
();
}
@override
...
...
@@ -69,6 +81,34 @@ class _DemoSuperplayerState extends State<DemoSuperplayer> {
return
!
_controller
.
onBackPress
();
}
Widget
getTabRow
()
{
return
new
Container
(
height:
40
,
child:
new
Row
(
mainAxisAlignment:
MainAxisAlignment
.
spaceEvenly
,
children:
[
new
GestureDetector
(
child:
new
Container
(
child:
Text
(
"直播"
,
style:
tabSelectPos
==
0
?
_textStyleSelected
:
_textStyleUnSelected
,
),
),
onTap:
_getLiveListData
,
),
new
GestureDetector
(
onTap:
_getVodListData
,
child:
new
Container
(
child:
Text
(
"点播"
,
style:
tabSelectPos
==
1
?
_textStyleSelected
:
_textStyleUnSelected
,
),
)),
],
),
);
}
Widget
getBody
()
{
return
Column
(
children:
[
_getPlayArea
(),
Expanded
(
flex:
1
,
child:
_getListArea
()),
_getAddArea
()],
...
...
@@ -85,32 +125,39 @@ class _DemoSuperplayerState extends State<DemoSuperplayer> {
Widget
_getListArea
()
{
return
Container
(
margin:
EdgeInsets
.
only
(
top:
10
),
child:
ListView
.
builder
(
shrinkWrap:
true
,
itemCount:
videoModels
.
length
,
itemBuilder:
(
context
,
i
)
=>
_buildVideoItem
(
videoModels
[
i
]),
),
);
margin:
EdgeInsets
.
only
(
top:
10
),
child:
Column
(
mainAxisAlignment:
MainAxisAlignment
.
start
,
children:
[
getTabRow
(),
Expanded
(
child:
ListView
.
builder
(
shrinkWrap:
true
,
itemCount:
videoModels
.
length
,
itemBuilder:
(
context
,
i
)
=>
_buildVideoItem
(
videoModels
[
i
]),
))
],
));
}
Widget
_buildVideoItem
(
SuperPlayerModel
playModel
)
{
return
Column
(
children:
[
ListTile
(
leading:
Image
.
network
(
playModel
.
coverUrl
,
width:
100
,
height:
60
,
fit:
BoxFit
.
cover
,
alignment:
Alignment
.
center
,
),
title:
new
Text
(
playModel
.
title
,
style:
TextStyle
(
color:
Colors
.
white
),
),
onTap:
()
=>
playCurrentModel
(
playModel
),
horizontalTitleGap:
10
,),
leading:
Image
.
network
(
playModel
.
coverUrl
.
isEmpty
?
DEFAULT_PLACE_HOLDER
:
playModel
.
coverUrl
,
width:
100
,
height:
60
,
fit:
BoxFit
.
cover
,
alignment:
Alignment
.
center
,
),
title:
new
Text
(
playModel
.
title
,
style:
TextStyle
(
color:
Colors
.
white
),
),
onTap:
()
=>
playCurrentModel
(
playModel
),
horizontalTitleGap:
10
,
),
Divider
()
],
);
...
...
@@ -124,6 +171,7 @@ class _DemoSuperplayerState extends State<DemoSuperplayer> {
}
void
onAddVideoTap
(
BuildContext
context
)
{
bool
isLive
=
tabSelectPos
==
0
;
showDialog
(
context:
context
,
builder:
(
context
)
{
...
...
@@ -144,7 +192,7 @@ class _DemoSuperplayerState extends State<DemoSuperplayer> {
}
playCurrentModel
(
model
);
},
needPisgn:
true
);
},
needPisgn:
!
isLive
,
showFileEdited:
!
isLive
,
);
});
}
...
...
@@ -152,7 +200,35 @@ class _DemoSuperplayerState extends State<DemoSuperplayer> {
_controller
.
playWithModel
(
model
);
}
void
initData
()
async
{
void
_getLiveListData
()
async
{
setState
(()
{
tabSelectPos
=
0
;
});
List
<
SuperPlayerModel
>
models
=
[];
int
playAction
=
SuperPlayerModel
.
PLAY_ACTION_AUTO_PLAY
;
SuperPlayerModel
model
=
SuperPlayerModel
();
model
.
title
=
"测试视频"
;
model
.
videoURL
=
"http://liteavapp.qcloud.com/live/liteavdemoplayerstreamid_demo1080p.flv"
;
model
.
coverUrl
=
"http://1500005830.vod2.myqcloud.com/6c9a5118vodcq1500005830/66bc542f387702300661648850/0RyP1rZfkdQA.png"
;
model
.
playAction
=
playAction
;
models
.
add
(
model
);
videoModels
.
clear
();
videoModels
.
addAll
(
models
);
setState
(()
{
if
(
videoModels
.
isNotEmpty
)
{
playCurrentModel
(
videoModels
[
0
]);
}
else
{
EasyLoading
.
showError
(
"video list request error"
);
}
});
}
void
_getVodListData
()
async
{
setState
(()
{
tabSelectPos
=
1
;
});
List
<
SuperPlayerModel
>
models
=
[];
int
playAction
=
SuperPlayerModel
.
PLAY_ACTION_AUTO_PLAY
;
...
...
@@ -196,13 +272,12 @@ class _DemoSuperplayerState extends State<DemoSuperplayer> {
List
<
Future
<
void
>>
requestList
=
[];
SuperVodDataLoader
loader
=
SuperVodDataLoader
();
for
(
SuperPlayerModel
tempModel
in
models
)
{
requestList
.
add
(
loader
.
getVideoData
(
tempModel
,
(
resultModel
)
{
videoModels
.
add
(
resultModel
);
}));
requestList
.
add
(
loader
.
getVideoData
(
tempModel
,
(
_
)
{}));
}
await
Future
.
wait
(
requestList
);
videoModels
.
clear
();
videoModels
.
addAll
(
models
);
setState
(()
{
if
(
videoModels
.
isNotEmpty
)
{
playCurrentModel
(
videoModels
[
0
]);
...
...
Flutter/example/lib/demo_txLiveplayer.dart
浏览文件 @
efb9316d
...
...
@@ -81,15 +81,16 @@ class _DemoTXLivelayerState extends State<DemoTXLivePlayer> with WidgetsBindingO
await
SuperPlayerPlugin
.
setConsoleEnabled
(
true
);
await
_controller
.
initialize
();
await
_controller
.
setConfig
(
FTXLivePlayConfig
());
// 安卓需要设置hls格式才可正常播放
await
_controller
.
p
lay
(
_url
,
playType:
TXPlayType
.
LIVE_FLV
);
await
_controller
.
startP
lay
(
_url
,
playType:
TXPlayType
.
LIVE_FLV
);
}
@override
void
initState
()
{
super
.
initState
();
init
();
WidgetsBinding
.
instance
?
.
addObserver
(
this
);
WidgetsBinding
.
instance
.
addObserver
(
this
);
EasyLoading
.
show
(
status:
'loading...'
);
}
...
...
@@ -207,7 +208,7 @@ class _DemoTXLivelayerState extends State<DemoTXLivePlayer> with WidgetsBindingO
),
new
GestureDetector
(
onTap:
()
async
{
_controller
.
p
lay
(
_url
,
playType:
TXPlayType
.
LIVE_FLV
);
_controller
.
startP
lay
(
_url
,
playType:
TXPlayType
.
LIVE_FLV
);
},
child:
Container
(
color:
Colors
.
transparent
,
...
...
@@ -290,7 +291,7 @@ class _DemoTXLivelayerState extends State<DemoTXLivePlayer> with WidgetsBindingO
playNetEventSubscription
?.
cancel
();
_controller
.
dispose
();
super
.
dispose
();
WidgetsBinding
.
instance
?
.
removeObserver
(
this
);
WidgetsBinding
.
instance
.
removeObserver
(
this
);
EasyLoading
.
dismiss
();
}
...
...
@@ -302,7 +303,7 @@ class _DemoTXLivelayerState extends State<DemoTXLivePlayer> with WidgetsBindingO
_url
=
url
;
_controller
.
stop
();
if
(
url
.
isNotEmpty
)
{
_controller
.
p
lay
(
url
);
_controller
.
startP
lay
(
url
);
}
},
showFileEdited:
false
);
});
...
...
Flutter/example/lib/demo_txvodplayer.dart
浏览文件 @
efb9316d
...
...
@@ -18,7 +18,7 @@ class DemoTXVodPlayer extends StatefulWidget {
class
_DemoTXVodlayerState
extends
State
<
DemoTXVodPlayer
>
with
WidgetsBindingObserver
{
late
TXVodPlayerController
_controller
;
double
_aspectRatio
=
0
;
double
_aspectRatio
=
16
/
9
;
double
_currentProgress
=
0.0
;
bool
_isMute
=
false
;
int
_volume
=
100
;
...
...
@@ -92,7 +92,7 @@ class _DemoTXVodlayerState extends State<DemoTXVodPlayer>
void
initState
()
{
super
.
initState
();
init
();
WidgetsBinding
.
instance
?
.
addObserver
(
this
);
WidgetsBinding
.
instance
.
addObserver
(
this
);
EasyLoading
.
show
(
status:
'loading...'
);
}
...
...
@@ -386,7 +386,7 @@ class _DemoTXVodlayerState extends State<DemoTXVodPlayer>
playEventSubscription
?.
cancel
();
_controller
.
dispose
();
super
.
dispose
();
WidgetsBinding
.
instance
?
.
removeObserver
(
this
);
WidgetsBinding
.
instance
.
removeObserver
(
this
);
EasyLoading
.
dismiss
();
}
...
...
Flutter/example/lib/superplayer/common/color_resource.dart
浏览文件 @
efb9316d
...
...
@@ -6,4 +6,5 @@ class ColorResource {
static
const
COLOR_GRAY
=
0xFFBBBBBB
;
static
const
COLOR_TRANS_BLACK
=
0xBB000000
;
static
const
COLOR_WHITE
=
0xFFFFFFFF
;
static
const
COLOR_TRANS_GRAY
=
0x11BBBBBB
;
}
\ No newline at end of file
Flutter/example/lib/superplayer/model/superplayer_define.dart
浏览文件 @
efb9316d
...
...
@@ -34,7 +34,7 @@ class SuperPlayerCode {
static
const
VOD_REQUEST_FILE_ID_FAIL
=
40002
;
}
abstract
class
SuperPlayerViewEvent
{
class
SuperPlayerViewEvent
{
static
const
onStartFullScreenPlay
=
"onStartFullScreenPlay"
;
//进入全屏播放
static
const
onStopFullScreenPlay
=
"onStopFullScreenPlay"
;
//退出全屏播放
static
const
onSuperPlayerDidStart
=
"onSuperPlayerDidStart"
;
//播放开始通知
...
...
@@ -43,4 +43,11 @@ abstract class SuperPlayerViewEvent {
static
const
onSuperPlayerBackAction
=
"onSuperPlayerBackAction"
;
//返回事件
}
/// 播放器插件当前所处的布局状态
class
SuperPlayerUIStatus
{
static
const
WINDOW_MODE
=
0
;
static
const
FULLSCREEN_MODE
=
1
;
static
const
PIP_MODE
=
2
;
}
Flutter/example/lib/superplayer/superplayer_controller.dart
浏览文件 @
efb9316d
...
...
@@ -5,9 +5,7 @@ part of demo_super_player_lib;
class
SuperPlayerController
{
static
const
TAG
=
"SuperPlayerController"
;
final
StreamController
<
Map
<
dynamic
,
dynamic
>>
_eventStreamController
=
StreamController
.
broadcast
();
final
StreamController
<
Map
<
dynamic
,
dynamic
>>
_simpleEventStreamController
=
StreamController
.
broadcast
();
final
StreamController
<
Map
<
dynamic
,
dynamic
>>
_playStateStreamController
=
StreamController
.
broadcast
();
final
StreamController
<
Map
<
dynamic
,
dynamic
>>
_playerNetStatusStreamController
=
StreamController
.
broadcast
();
/// simple event,see SuperPlayerViewEvent
...
...
@@ -16,16 +14,24 @@ class SuperPlayerController {
/// player net status,see TXVodNetEvent
Stream
<
Map
<
dynamic
,
dynamic
>>
get
onPlayerNetStatusBroadcast
=>
_playerNetStatusStreamController
.
stream
;
TXVodPlayerController
?
_vodPlayerController
;
late
TXVodPlayerController
_vodPlayerController
;
late
TXLivePlayerController
_livePlayerController
;
SuperPlayerModel
?
videoModel
;
int
_playAction
=
SuperPlayerModel
.
PLAY_ACTION_AUTO_PLAY
;
PlayInfoProtocol
?
_currentProtocol
;
_SuperPlayerObserver
?
_observer
;
VideoQuality
?
currentQuality
;
List
<
VideoQuality
>?
currentQualiyList
;
StreamController
<
TXPlayerHolder
>
playerStreamController
=
StreamController
.
broadcast
();
SuperPlayerState
playerState
=
SuperPlayerState
.
INIT
;
SuperPlayerType
playerType
=
SuperPlayerType
.
VOD
;
FTXVodPlayConfig
_vodConfig
=
FTXVodPlayConfig
();
FTXLivePlayConfig
_liveConfig
=
FTXLivePlayConfig
();
StreamSubscription
?
_vodPlayEventListener
;
StreamSubscription
?
_vodNetEventListener
;
StreamSubscription
?
_livePlayEventListener
;
StreamSubscription
?
_liveNetEventListener
;
String
_currentPlayUrl
=
""
;
...
...
@@ -34,12 +40,13 @@ class SuperPlayerController {
bool
_needToPause
=
false
;
bool
_isMultiBitrateStream
=
false
;
// 是否是多码流url播放
bool
_changeHWAcceleration
=
false
;
// 切换硬解后接收到第一个关键帧前的标记位
bool
_isFullScreen
=
false
;
bool
_isOpenHWAcceleration
=
true
;
int
_playerUIStatus
=
SuperPlayerUIStatus
.
WINDOW_MODE
;
final
BuildContext
_context
;
int
currentDuration
=
0
;
int
videoDuration
=
0
;
double
currentDuration
=
0
;
double
videoDuration
=
0
;
int
_maxLiveProgressTime
=
0
;
double
_seekPos
=
0
;
// 记录切换硬解时的播放时间
/// 该值会改变新播放视频的播放开始时间点
...
...
@@ -50,18 +57,31 @@ class SuperPlayerController {
SuperPlayerController
(
this
.
_context
)
{
_initVodPlayer
();
_initLivePlayer
();
}
void
_initVodPlayer
()
async
{
_vodPlayerController
=
new
TXVodPlayerController
();
await
_vodPlayerController
?.
initialize
();
_vodPlayerController
?.
onPlayerEventBroadcast
.
listen
((
event
)
async
{
await
_vodPlayerController
.
initialize
();
_setVodListener
();
}
void
_initLivePlayer
()
async
{
_livePlayerController
=
TXLivePlayerController
();
await
_livePlayerController
.
initialize
();
_setLiveListener
();
}
void
_setVodListener
()
{
_vodPlayEventListener
?.
cancel
();
_vodNetEventListener
?.
cancel
();
_vodPlayEventListener
=
_vodPlayerController
.
onPlayerEventBroadcast
.
listen
((
event
)
async
{
int
eventCode
=
event
[
'event'
];
switch
(
eventCode
)
{
case
TXVodPlayEvent
.
PLAY_EVT_VOD_PLAY_PREPARED
:
// vodPrepared
isPrepared
=
true
;
if
(
_isMultiBitrateStream
)
{
List
<
dynamic
>?
bitrateListTemp
=
await
_vodPlayerController
!
.
getSupportedBitrates
();
List
<
dynamic
>?
bitrateListTemp
=
await
_vodPlayerController
.
getSupportedBitrates
();
List
<
FTXBitrateItem
>
bitrateList
=
[];
if
(
null
!=
bitrateListTemp
)
{
for
(
Map
<
dynamic
,
dynamic
>
map
in
bitrateListTemp
)
{
...
...
@@ -78,7 +98,7 @@ class SuperPlayerController {
videoQualities
.
add
(
quality
);
}
int
?
bitrateIndex
=
await
_vodPlayerController
?
.
getBitrateIndex
();
//获取默认码率的index
int
?
bitrateIndex
=
await
_vodPlayerController
.
getBitrateIndex
();
//获取默认码率的index
for
(
VideoQuality
quality
in
videoQualities
)
{
if
(
quality
.
index
==
bitrateIndex
)
{
defaultQuality
=
quality
;
...
...
@@ -88,9 +108,9 @@ class SuperPlayerController {
}
if
(
_needToPause
)
{
_vodPlayerController
?
.
pause
();
_vodPlayerController
.
pause
();
}
else
if
(
_needToResume
)
{
_vodPlayerController
?
.
resume
();
_vodPlayerController
.
resume
();
}
break
;
case
TXVodPlayEvent
.
PLAY_EVT_PLAY_LOADING
:
// PLAY_EVT_PLAY_LOADING
...
...
@@ -111,8 +131,7 @@ class SuperPlayerController {
}
_updatePlayerState
(
SuperPlayerState
.
PLAYING
);
break
;
case
TXVodPlayEvent
.
PLAY_EVT_RCV_FIRST_I_FRAME
:
// PLAY_EVT_RCV_FIRST_I_FRAME
case
TXVodPlayEvent
.
PLAY_EVT_RCV_FIRST_I_FRAME
:
// PLAY_EVT_RCV_FIRST_I_FRAME
if
(
_needToPause
)
{
return
;
}
...
...
@@ -132,19 +151,18 @@ class SuperPlayerController {
dynamic
progress
=
event
[
TXVodPlayEvent
.
EVT_PLAY_PROGRESS
];
dynamic
duration
=
event
[
TXVodPlayEvent
.
EVT_PLAY_DURATION
];
if
(
null
!=
progress
)
{
currentDuration
=
progress
.
to
Int
();
// 当前时间,转换后的单位 秒
currentDuration
=
progress
.
to
Double
();
// 当前时间,转换后的单位 秒
}
if
(
null
!=
duration
)
{
videoDuration
=
duration
.
to
Int
();
// 总播放时长,转换后的单位 秒
videoDuration
=
duration
.
to
Double
();
// 总播放时长,转换后的单位 秒
}
if
(
videoDuration
!=
0
)
{
_observer
?.
onPlayProgress
(
currentDuration
,
videoDuration
,
await
getPlayableDuration
());
}
break
;
}
_eventStreamController
.
add
(
event
);
});
_vod
PlayerController
?
.
onPlayerNetStatusBroadcast
.
listen
((
event
)
{
_vod
NetEventListener
=
_vodPlayerController
.
onPlayerNetStatusBroadcast
.
listen
((
event
)
{
dynamic
wd
=
(
event
[
"VIDEO_WIDTH"
]);
dynamic
hd
=
(
event
[
"VIDEO_HEIGHT"
]);
if
(
null
!=
wd
&&
null
!=
hd
)
{
...
...
@@ -163,26 +181,83 @@ class SuperPlayerController {
});
}
void
_checkVodPlayerIsInit
()
{
if
(
null
==
_vodPlayerController
)
{
_initVodPlayer
();
}
void
_setLiveListener
()
{
_livePlayEventListener
?.
cancel
();
_liveNetEventListener
?.
cancel
();
_livePlayEventListener
=
_livePlayerController
.
onPlayerEventBroadcast
.
listen
((
event
)
async
{
int
eventCode
=
event
[
'event'
];
switch
(
eventCode
)
{
case
TXVodPlayEvent
.
PLAY_EVT_VOD_PLAY_PREPARED
:
// vodPrepared
case
TXVodPlayEvent
.
PLAY_EVT_PLAY_BEGIN
:
_updatePlayerState
(
SuperPlayerState
.
PLAYING
);
break
;
case
TXVodPlayEvent
.
PLAY_ERR_NET_DISCONNECT
:
case
TXVodPlayEvent
.
PLAY_EVT_PLAY_END
:
if
(
playerType
==
SuperPlayerType
.
LIVE_SHIFT
)
{
_livePlayerController
.
resumeLive
();
_observer
?.
onError
(
SuperPlayerCode
.
LIVE_SHIFT_FAIL
,
"时移失败,返回直播"
);
_updatePlayerState
(
SuperPlayerState
.
PLAYING
);
}
else
{
_stop
();
if
(
eventCode
==
TXVodPlayEvent
.
PLAY_ERR_NET_DISCONNECT
)
{
_observer
?.
onError
(
SuperPlayerCode
.
NET_ERROR
,
"网络不给力,点击重试"
);
}
else
{
_observer
?.
onError
(
SuperPlayerCode
.
LIVE_PLAY_END
,
event
[
TXVodPlayEvent
.
EVT_DESCRIPTION
]);
}
}
break
;
case
TXVodPlayEvent
.
PLAY_EVT_PLAY_LOADING
:
_updatePlayerState
(
SuperPlayerState
.
LOADING
);
break
;
case
TXVodPlayEvent
.
PLAY_EVT_RCV_FIRST_I_FRAME
:
_updatePlayerState
(
SuperPlayerState
.
PLAYING
);
_observer
?.
onRcvFirstIframe
();
break
;
case
TXVodPlayEvent
.
PLAY_EVT_STREAM_SWITCH_SUCC
:
_observer
?.
onSwitchStreamEnd
(
true
,
SuperPlayerType
.
LIVE
,
currentQuality
);
break
;
case
TXVodPlayEvent
.
PLAY_ERR_STREAM_SWITCH_FAIL
:
_observer
?.
onSwitchStreamEnd
(
false
,
SuperPlayerType
.
LIVE
,
currentQuality
);
break
;
case
TXVodPlayEvent
.
PLAY_EVT_PLAY_PROGRESS
:
int
progress
=
event
[
TXVodPlayEvent
.
EVT_PLAY_PROGRESS_MS
];
_maxLiveProgressTime
=
progress
>
_maxLiveProgressTime
?
progress
:
_maxLiveProgressTime
;
_observer
?.
onPlayProgress
(
progress
/
1000
,
_maxLiveProgressTime
/
1000
,
await
getPlayableDuration
());
break
;
}
});
_liveNetEventListener
=
_livePlayerController
.
onPlayerNetStatusBroadcast
.
listen
((
event
)
{
dynamic
wd
=
(
event
[
"VIDEO_WIDTH"
]);
dynamic
hd
=
(
event
[
"VIDEO_HEIGHT"
]);
if
(
null
!=
wd
&&
null
!=
hd
)
{
double
w
=
wd
.
toDouble
();
double
h
=
hd
.
toDouble
();
if
(
w
>
0
&&
h
>
0
)
{
if
(
w
!=
videoWidth
)
{
videoWidth
=
w
;
}
if
(
h
!=
videoHeight
)
{
videoHeight
=
h
;
}
}
}
_playerNetStatusStreamController
.
add
(
event
);
});
}
/// 播放视频
void
playWithModel
(
SuperPlayerModel
videoModel
)
{
Future
<
void
>
playWithModel
(
SuperPlayerModel
videoModel
)
async
{
this
.
videoModel
=
videoModel
;
_playAction
=
videoModel
.
playAction
;
resetPlayer
();
await
resetPlayer
();
if
(
_playAction
==
SuperPlayerModel
.
PLAY_ACTION_AUTO_PLAY
||
_playAction
==
SuperPlayerModel
.
PLAY_ACTION_PRELOAD
)
{
_playWithModelInner
(
videoModel
);
await
_playWithModelInner
(
videoModel
);
}
else
{
_observer
?.
onNewVideoPlay
();
}
}
Future
<
void
>
_playWithModelInner
(
SuperPlayerModel
videoModel
)
async
{
_checkVodPlayerIsInit
();
this
.
videoModel
=
videoModel
;
_playAction
=
videoModel
.
playAction
;
_observer
?.
onVideoImageSpriteAndKeyFrameChanged
(
null
,
null
);
...
...
@@ -206,7 +281,7 @@ class SuperPlayerController {
}
_playModeVideo
(
protocol
);
_updatePlayerType
(
SuperPlayerType
.
VOD
);
_observer
?.
onPlayProgress
(
0
,
resultModel
.
duration
,
await
getPlayableDuration
());
_observer
?.
onPlayProgress
(
0
,
resultModel
.
duration
.
toDouble
()
,
await
getPlayableDuration
());
_observer
?.
onVideoImageSpriteAndKeyFrameChanged
(
protocol
.
getImageSpriteInfo
(),
protocol
.
getKeyFrameDescInfo
());
},
(
errCode
,
message
)
{
// onError
...
...
@@ -216,8 +291,7 @@ class SuperPlayerController {
}
Future
<
double
>
getPlayableDuration
()
async
{
double
?
playableDuration
=
await
_vodPlayerController
?.
getPlayableDuration
();
return
playableDuration
??
0
;
return
await
_vodPlayerController
.
getPlayableDuration
();
}
void
_playModeVideo
(
PlayInfoProtocol
protocol
)
{
...
...
@@ -250,7 +324,7 @@ class SuperPlayerController {
void
_playWithUrl
(
SuperPlayerModel
model
)
{
List
<
VideoQuality
>
videoQualities
=
[];
VideoQuality
?
defaultVideoQuality
;
late
String
videoUrl
;
String
?
videoUrl
;
if
(
model
.
multiVideoURLs
.
isNotEmpty
)
{
int
i
=
0
;
for
(
SuperPlayerUrl
superPlayerUrl
in
model
.
multiVideoURLs
)
{
...
...
@@ -267,8 +341,19 @@ class SuperPlayerController {
_observer
?.
onError
(
SuperPlayerCode
.
PLAY_URL_EMPTY
,
"播放视频失败,播放链接为空"
);
return
;
}
_playVodUrl
(
videoUrl
);
_updatePlayerType
(
SuperPlayerType
.
VOD
);
if
(
_isRTMPPlay
(
videoUrl
))
{
_playLiveURL
(
videoUrl
,
TXPlayType
.
LIVE_RTMP
);
}
else
if
(
_isFLVPlay
(
videoUrl
))
{
_playTimeShiftLiveURL
(
model
.
appId
,
videoUrl
);
if
(
model
.
multiVideoURLs
.
isNotEmpty
)
{
_startMultiStreamLiveURL
(
videoUrl
);
}
}
else
{
_playVodUrl
(
videoUrl
);
}
bool
isLivePlay
=
(
_isRTMPPlay
(
videoUrl
)
||
_isFLVPlay
(
videoUrl
));
_observer
?.
onPlayProgress
(
0
,
model
.
duration
.
toDouble
(),
0
);
_updatePlayerType
(
isLivePlay
?
SuperPlayerType
.
LIVE
:
SuperPlayerType
.
VOD
);
_updateVideoQualityList
(
videoQualities
,
defaultVideoQuality
);
}
...
...
@@ -278,44 +363,43 @@ class SuperPlayerController {
}
_isMultiBitrateStream
=
url
.
contains
(
".m3u8"
);
_currentPlayUrl
=
url
;
if
(
null
!=
_vodPlayerController
)
{
await
_vodPlayerController
?.
setStartTime
(
startPos
);
if
(
_playAction
==
SuperPlayerModel
.
PLAY_ACTION_PRELOAD
)
{
await
_vodPlayerController
?.
setAutoPlay
(
isAutoPlay:
false
);
_playAction
=
SuperPlayerModel
.
PLAY_ACTION_AUTO_PLAY
;
}
else
if
(
_playAction
==
SuperPlayerModel
.
PLAY_ACTION_AUTO_PLAY
||
_playAction
==
SuperPlayerModel
.
PLAY_ACTION_MANUAL_PLAY
)
{
await
_vodPlayerController
?.
setAutoPlay
(
isAutoPlay:
true
);
await
_vodPlayerController
.
setStartTime
(
startPos
);
if
(
_playAction
==
SuperPlayerModel
.
PLAY_ACTION_PRELOAD
)
{
await
_vodPlayerController
.
setAutoPlay
(
isAutoPlay:
false
);
_playAction
=
SuperPlayerModel
.
PLAY_ACTION_AUTO_PLAY
;
}
else
if
(
_playAction
==
SuperPlayerModel
.
PLAY_ACTION_AUTO_PLAY
||
_playAction
==
SuperPlayerModel
.
PLAY_ACTION_MANUAL_PLAY
)
{
await
_vodPlayerController
.
setAutoPlay
(
isAutoPlay:
true
);
}
_setVodListener
();
String
drmType
=
"plain"
;
if
(
_currentProtocol
!=
null
)
{
LogUtils
.
d
(
TAG
,
"TOKEN:
${_currentProtocol!.getToken()}
"
);
await
_vodPlayerController
.
setToken
(
_currentProtocol
!.
getToken
());
if
(
_currentProtocol
!.
getDRMType
()
!=
null
&&
_currentProtocol
!.
getDRMType
()!.
isNotEmpty
)
{
drmType
=
_currentProtocol
!.
getDRMType
()!;
}
String
drmType
=
"plain"
;
if
(
_currentProtocol
!=
null
)
{
LogUtils
.
d
(
TAG
,
"TOKEN:
${_currentProtocol!.getToken()}
"
);
await
_vodPlayerController
?.
setToken
(
_currentProtocol
!.
getToken
());
if
(
_currentProtocol
!.
getDRMType
()
!=
null
&&
_currentProtocol
!.
getDRMType
()!.
isNotEmpty
)
{
drmType
=
_currentProtocol
!.
getDRMType
()!;
}
}
else
{
await
_vodPlayerController
.
setToken
(
null
);
}
if
(
videoModel
!.
videoId
!=
null
&&
videoModel
!.
appId
!=
0
)
{
Uri
uri
=
Uri
.
parse
(
url
);
String
query
=
uri
.
query
;
if
(
query
==
null
||
query
.
isEmpty
)
{
query
=
""
;
}
else
{
await
_vodPlayerController
?.
setToken
(
null
);
}
if
(
videoModel
!.
videoId
!=
null
&&
videoModel
!.
appId
!=
0
)
{
Uri
uri
=
Uri
.
parse
(
url
);
String
query
=
uri
.
query
;
if
(
query
==
null
||
query
.
isEmpty
)
{
query
=
""
;
}
else
{
query
=
query
+
"&"
;
if
(
query
.
contains
(
"spfileid"
)
||
query
.
contains
(
"spdrmtype"
)
||
query
.
contains
(
"spappid"
))
{
LogUtils
.
d
(
TAG
,
"url contains superplay key.
$query
"
);
}
query
=
query
+
"&"
;
if
(
query
.
contains
(
"spfileid"
)
||
query
.
contains
(
"spdrmtype"
)
||
query
.
contains
(
"spappid"
))
{
LogUtils
.
d
(
TAG
,
"url contains superplay key.
$query
"
);
}
query
+=
"spfileid=
${videoModel!.videoId!.fileId}
"
"&spdrmtype=
$drmType
&spappid=
${videoModel!.appId}
"
;
Uri
newUri
=
Uri
(
path:
url
,
query:
query
);
LogUtils
.
d
(
TAG
,
'playVodURL: newurl =
${Uri.decodeFull(newUri.toString())}
;url=
$url
'
);
await
_vodPlayerController
?.
startPlay
(
Uri
.
decodeFull
(
newUri
.
toString
()));
}
else
{
LogUtils
.
d
(
TAG
,
"playVodURL url:
$url
"
);
await
_vodPlayerController
?.
startPlay
(
url
);
}
query
+=
"spfileid=
${videoModel!.videoId!.fileId}
"
"&spdrmtype=
$drmType
&spappid=
${videoModel!.appId}
"
;
Uri
newUri
=
Uri
(
path:
url
,
query:
query
);
LogUtils
.
d
(
TAG
,
'playVodURL: newurl =
${Uri.decodeFull(newUri.toString())}
;url=
$url
'
);
await
_vodPlayerController
.
startPlay
(
Uri
.
decodeFull
(
newUri
.
toString
()));
}
else
{
LogUtils
.
d
(
TAG
,
"playVodURL url:
$url
"
);
await
_vodPlayerController
.
startPlay
(
url
);
}
}
...
...
@@ -324,10 +408,10 @@ class SuperPlayerController {
void
pause
()
{
if
(
playerType
==
SuperPlayerType
.
VOD
)
{
if
(
isPrepared
)
{
_vodPlayerController
?
.
pause
();
_vodPlayerController
.
pause
();
}
}
else
{
// todo implements live player
_livePlayerController
.
pause
();
}
_updatePlayerState
(
SuperPlayerState
.
PAUSE
);
_needToPause
=
true
;
...
...
@@ -338,10 +422,10 @@ class SuperPlayerController {
if
(
playerType
==
SuperPlayerType
.
VOD
)
{
_needToResume
=
true
;
if
(
isPrepared
)
{
_vodPlayerController
?
.
resume
();
_vodPlayerController
.
resume
();
}
}
else
{
// todo implements live player
_livePlayerController
.
resume
();
}
_needToPause
=
false
;
_updatePlayerState
(
SuperPlayerState
.
PLAYING
);
...
...
@@ -350,17 +434,74 @@ class SuperPlayerController {
/// 重新开始播放视频
Future
<
void
>
reStart
()
async
{
if
(
playerType
==
SuperPlayerType
.
LIVE
||
playerType
==
SuperPlayerType
.
LIVE_SHIFT
)
{
// todo implements live player
if
(
_isRTMPPlay
(
_currentPlayUrl
))
{
_playLiveURL
(
_currentPlayUrl
,
TXPlayType
.
LIVE_RTMP
);
}
else
if
(
_isFLVPlay
(
_currentPlayUrl
)
&&
null
!=
videoModel
)
{
_playTimeShiftLiveURL
(
videoModel
!.
appId
,
_currentPlayUrl
);
if
(
videoModel
!.
multiVideoURLs
.
isNotEmpty
)
{
_startMultiStreamLiveURL
(
_currentPlayUrl
);
}
}
}
else
{
await
_playVodUrl
(
_currentPlayUrl
);
}
}
void
_startMultiStreamLiveURL
(
String
url
)
{
_liveConfig
.
autoAdjustCacheTime
=
false
;
_liveConfig
.
maxAutoAdjustCacheTime
=
5.0
;
_liveConfig
.
minAutoAdjustCacheTime
=
5.0
;
_livePlayerController
.
setConfig
(
_liveConfig
);
_observer
?.
onPlayTimeShiftLive
(
_livePlayerController
,
url
);
}
/// 播放直播URL
void
_playLiveURL
(
String
url
,
int
playType
)
async
{
_currentPlayUrl
=
url
;
_setLiveListener
();
bool
result
=
await
_livePlayerController
.
startPlay
(
url
,
playType:
playType
);
if
(
result
)
{
_updatePlayerState
(
SuperPlayerState
.
PLAYING
);
}
else
{
LogUtils
.
e
(
TAG
,
"playLiveURL videoURL:
$url
,result:
$result
"
);
}
}
/// 播放时移直播url
void
_playTimeShiftLiveURL
(
int
appId
,
String
url
)
{
String
bizid
=
url
.
substring
(
url
.
indexOf
(
"//"
)
+
2
,
url
.
indexOf
(
"."
));
String
streamid
=
url
.
substring
(
url
.
lastIndexOf
(
"/"
)
+
1
,
url
.
lastIndexOf
(
"."
));
LogUtils
.
i
(
TAG
,
"bizid:
$bizid
,streamid:
$streamid
,appid:
$appId
"
);
_playLiveURL
(
url
,
TXPlayType
.
LIVE_FLV
);
}
void
_updatePlayerType
(
SuperPlayerType
type
)
{
if
(
playerType
!=
type
)
{
playerType
=
type
;
updatePlayerView
();
_observer
?.
onPlayerTypeChange
(
type
);
}
}
void
updatePlayerView
()
async
{
TXPlayerController
controller
=
getCurrentController
();
TXPlayerHolder
model
=
TXPlayerHolder
(
controller
);
playerStreamController
.
sink
.
add
(
model
);
}
Stream
<
TXPlayerHolder
>
getPlayerStream
()
{
return
playerStreamController
.
stream
;
}
/// 获得当前正在使用的controller
TXPlayerController
getCurrentController
()
{
TXPlayerController
controller
;
if
(
playerType
==
SuperPlayerType
.
VOD
)
{
controller
=
_vodPlayerController
;
}
else
{
controller
=
_livePlayerController
;
}
_observer
?.
onPlayerTypeChange
(
type
)
;
return
controller
;
}
void
_updatePlayerState
(
SuperPlayerState
state
)
{
...
...
@@ -384,9 +525,6 @@ class SuperPlayerController {
_addSimpleEvent
(
SuperPlayerViewEvent
.
onSuperPlayerDidEnd
);
break
;
}
Map
<
String
,
SuperPlayerState
>
eventMap
=
new
Map
();
eventMap
[
'event'
]
=
state
;
_playStateStreamController
.
add
(
eventMap
);
}
String
_getPlayName
()
{
...
...
@@ -413,22 +551,30 @@ class SuperPlayerController {
_simpleEventStreamController
.
add
(
eventMap
);
}
void
_updateFullScreenState
(
bool
fullScreen
)
{
_isFullScreen
=
fullScreen
;
if
(
fullScreen
)
{
_addSimpleEvent
(
SuperPlayerViewEvent
.
onStartFullScreenPlay
);
}
else
{
_addSimpleEvent
(
SuperPlayerViewEvent
.
onStopFullScreenPlay
);
void
_updatePlayerUIStatus
(
int
status
)
{
if
(
_playerUIStatus
!=
status
)
{
_playerUIStatus
=
status
;
if
(
status
==
SuperPlayerUIStatus
.
FULLSCREEN_MODE
)
{
_addSimpleEvent
(
SuperPlayerViewEvent
.
onStartFullScreenPlay
);
}
else
{
_addSimpleEvent
(
SuperPlayerViewEvent
.
onStopFullScreenPlay
);
}
}
}
/// just for inner invoke
Future
<
void
>
_stopPlay
()
async
{
await
_vodPlayerController
?.
stop
();
/// 是否是RTMP协议
bool
_isRTMPPlay
(
String
?
videoURL
)
{
return
null
!=
videoURL
&&
videoURL
.
startsWith
(
"rtmp"
);
}
/// 是否是HTTP-FLV协议
bool
_isFLVPlay
(
String
?
videoURL
)
{
return
(
null
!=
videoURL
&&
videoURL
.
startsWith
(
"http://"
)
||
videoURL
!.
startsWith
(
"https://"
))
&&
videoURL
.
contains
(
".flv"
);
}
/// 重置播放器状态
void
resetPlayer
()
async
{
Future
<
void
>
resetPlayer
()
async
{
isPrepared
=
false
;
_needToResume
=
false
;
_needToPause
=
false
;
...
...
@@ -437,24 +583,35 @@ class SuperPlayerController {
currentQuality
=
null
;
currentQualiyList
?.
clear
();
_currentProtocol
=
null
;
await
_vodPlayerController
?.
stop
(
isNeedClear:
true
);
// 移除所有事件
_vodPlayEventListener
?.
cancel
();
_vodNetEventListener
?.
cancel
();
_livePlayEventListener
?.
cancel
();
_liveNetEventListener
?.
cancel
();
await
_vodPlayerController
.
stop
();
await
_livePlayerController
.
stop
();
_updatePlayerState
(
SuperPlayerState
.
INIT
);
}
void
_stop
()
async
{
resetPlayer
();
_updatePlayerState
(
SuperPlayerState
.
END
);
}
/// 释放播放器,播放器释放之后,将不能再使用
Future
<
void
>
releasePlayer
()
async
{
// 先移除widget的事件监听
_observer
?.
onDispose
();
resetPlayer
();
_vodPlayerController
?.
dispose
();
_vodPlayerController
=
null
;
playerStreamController
.
close
();
_vodPlayerController
.
dispose
();
_livePlayerController
.
dispose
();
}
/// return true : 执行了退出全屏等操作,消耗了返回事件 false:未消耗事件
bool
onBackPress
()
{
if
(
null
!=
_vodPlayerController
&&
_isFullScreen
)
{
if
(
_playerUIStatus
==
SuperPlayerUIStatus
.
FULLSCREEN_MODE
)
{
_observer
?.
onSysBackPress
();
return
true
;
}
...
...
@@ -469,43 +626,59 @@ class SuperPlayerController {
void
switchStream
(
VideoQuality
videoQuality
)
async
{
currentQuality
=
videoQuality
;
if
(
playerType
==
SuperPlayerType
.
VOD
)
{
if
(
null
!=
_vodPlayerController
)
{
if
(
videoQuality
.
url
.
isNotEmpty
)
{
// url stream need manual seek
double
currentTime
=
await
_vodPlayerController
!.
getCurrentPlaybackTime
();
await
_vodPlayerController
?.
stop
(
isNeedClear:
false
);
LogUtils
.
d
(
TAG
,
"onQualitySelect quality.url:
${videoQuality.url}
"
);
await
_vodPlayerController
?.
setStartTime
(
currentTime
);
await
_vodPlayerController
?.
startPlay
(
videoQuality
.
url
);
}
else
{
LogUtils
.
d
(
TAG
,
"setBitrateIndex quality.index:
${videoQuality.index}
"
);
await
_vodPlayerController
?.
setBitrateIndex
(
videoQuality
.
index
);
}
_observer
?.
onSwitchStreamStart
(
true
,
SuperPlayerType
.
VOD
,
videoQuality
);
if
(
videoQuality
.
url
.
isNotEmpty
)
{
// url stream need manual seek
double
currentTime
=
await
_vodPlayerController
.
getCurrentPlaybackTime
();
await
_vodPlayerController
.
stop
(
isNeedClear:
false
);
LogUtils
.
d
(
TAG
,
"onQualitySelect quality.url:
${videoQuality.url}
"
);
await
_vodPlayerController
.
setStartTime
(
currentTime
);
await
_vodPlayerController
.
startPlay
(
videoQuality
.
url
);
}
else
{
LogUtils
.
d
(
TAG
,
"setBitrateIndex quality.index:
${videoQuality.index}
"
);
await
_vodPlayerController
.
setBitrateIndex
(
videoQuality
.
index
);
}
_observer
?.
onSwitchStreamStart
(
true
,
SuperPlayerType
.
VOD
,
videoQuality
);
}
else
{
// todo implements live player
bool
success
=
false
;
if
(
videoQuality
.
url
.
isNotEmpty
)
{
int
result
=
await
_livePlayerController
.
switchStream
(
videoQuality
.
url
);
success
=
result
>=
0
;
}
_observer
?.
onSwitchStreamStart
(
success
,
SuperPlayerType
.
LIVE
,
videoQuality
);
}
}
/// seek 到需要的时间点进行播放
Future
<
void
>
seek
(
double
progress
)
async
{
if
(
playerType
==
SuperPlayerType
.
VOD
)
{
await
_vodPlayerController
?
.
seek
(
progress
);
bool
?
isPlaying
=
await
_vodPlayerController
?
.
isPlaying
();
await
_vodPlayerController
.
seek
(
progress
);
bool
isPlaying
=
await
_vodPlayerController
.
isPlaying
();
// resume when not playing.if isPlaying is null,not resume
if
(!
(
isPlaying
??
true
)
)
{
if
(!
isPlaying
)
{
resume
();
}
}
else
{
// todo implements live player
_updatePlayerType
(
SuperPlayerType
.
LIVE_SHIFT
);
_livePlayerController
.
seek
(
progress
);
bool
isPlaying
=
await
_livePlayerController
.
isPlaying
();
// resume when not playing.if isPlaying is null,not resume
if
(!
isPlaying
)
{
resume
();
}
}
_observer
?.
onSeek
(
progress
);
}
/// 配置播放器
/// 配置
点播
播放器
Future
<
void
>
setPlayConfig
(
FTXVodPlayConfig
config
)
async
{
await
_vodPlayerController
?.
setConfig
(
config
);
_vodConfig
=
config
;
await
_vodPlayerController
.
setConfig
(
config
);
}
/// 配置直播播放器
Future
<
void
>
setLiveConfig
(
FTXLivePlayConfig
config
)
async
{
_liveConfig
=
config
;
await
_livePlayerController
.
setConfig
(
config
);
}
/// 进入画中画模式,进入画中画模式,需要适配画中画模式的界面,安卓只支持7.0以上机型
...
...
@@ -514,39 +687,52 @@ class SuperPlayerController {
/// </h1>
/// @param backIcon playIcon pauseIcon forwardIcon 为播放后退、播放、暂停、前进的图标,如果赋值的话,将会使用传递的图标,否则
/// 使用系统默认图标,只支持flutter本地资源图片,传递的时候,与flutter使用图片资源一致,例如: images/back_icon.png
Future
<
int
?
>
enterPictureInPictureMode
(
Future
<
int
>
enterPictureInPictureMode
(
{
String
?
backIcon
,
String
?
playIcon
,
String
?
pauseIcon
,
String
?
forwardIcon
})
async
{
return
_vodPlayerController
?.
enterPictureInPictureMode
(
backIcon:
backIcon
,
playIcon:
playIcon
,
pauseIcon:
pauseIcon
,
forwardIcon:
forwardIcon
);
if
(
_playerUIStatus
==
SuperPlayerUIStatus
.
WINDOW_MODE
)
{
if
(
playerType
==
SuperPlayerType
.
VOD
)
{
return
_vodPlayerController
.
enterPictureInPictureMode
(
backIconForAndroid:
backIcon
,
playIconForAndroid:
playIcon
,
pauseIconForAndroid:
pauseIcon
,
forwardIconForAndroid:
forwardIcon
);
}
else
{
return
_livePlayerController
.
enterPictureInPictureMode
(
backIconForAndroid:
backIcon
,
playIconForAndroid:
playIcon
,
pauseIconForAndroid:
pauseIcon
,
forwardIconForAndroid:
forwardIcon
);
}
}
return
TXVodPlayEvent
.
ERROR_PIP_CAN_NOT_ENTER
;
}
/// 开关硬解编码播放
Future
<
void
>
enableHardwareDecode
(
bool
enable
)
async
{
_isOpenHWAcceleration
=
enable
;
if
(
playerType
==
SuperPlayerType
.
VOD
)
{
if
(
null
!=
_vodPlayerController
)
{
await
_vodPlayerController
?.
enableHardwareDecode
(
enable
);
if
(
playerState
!=
SuperPlayerState
.
END
)
{
_changeHWAcceleration
=
true
;
_seekPos
=
await
_vodPlayerController
!.
getCurrentPlaybackTime
();
LogUtils
.
d
(
TAG
,
"seek pos
$_seekPos
"
);
resetPlayer
();
// 当protocol为空时,则说明当前播放视频为非v2和v4视频
if
(
_currentProtocol
==
null
)
{
_playUrlVideo
(
videoModel
);
}
else
{
_playModeVideo
(
_currentProtocol
!);
}
await
_vodPlayerController
.
enableHardwareDecode
(
enable
);
if
(
playerState
!=
SuperPlayerState
.
END
)
{
_changeHWAcceleration
=
true
;
_seekPos
=
await
_vodPlayerController
.
getCurrentPlaybackTime
();
LogUtils
.
d
(
TAG
,
"seek pos
$_seekPos
"
);
resetPlayer
();
// 当protocol为空时,则说明当前播放视频为非v2和v4视频
if
(
_currentProtocol
==
null
)
{
_playUrlVideo
(
videoModel
);
}
else
{
_playModeVideo
(
_currentProtocol
!);
}
}
}
else
{
// todo implements live player
await
_vodPlayerController
.
enableHardwareDecode
(
enable
);
await
playWithModel
(
videoModel
!);
}
}
Future
<
void
>
setPlayRate
(
double
rate
)
async
{
currentPlayRate
=
rate
;
_vodPlayerController
?
.
setRate
(
rate
);
_vodPlayerController
.
setRate
(
rate
);
}
/// 获得当前播放器状态
...
...
Flutter/example/lib/superplayer/superplayer_observer.dart
浏览文件 @
efb9316d
...
...
@@ -8,10 +8,10 @@ class _SuperPlayerObserver {
Function
onPlayPause
;
Function
onPlayStop
;
Function
onPlayLoading
;
Function
(
int
current
,
int
duration
,
double
playableDuration
)
onPlayProgress
;
Function
(
double
current
,
double
duration
,
double
playableDuration
)
onPlayProgress
;
Function
(
double
position
)
onSeek
;
Function
(
bool
success
,
SuperPlayerType
playerType
,
VideoQuality
quality
)
onSwitchStreamStart
;
Function
(
bool
success
,
SuperPlayerType
playerType
,
VideoQuality
quality
)
onSwitchStreamEnd
;
Function
(
bool
success
,
SuperPlayerType
playerType
,
VideoQuality
?
quality
)
onSwitchStreamStart
;
Function
(
bool
success
,
SuperPlayerType
playerType
,
VideoQuality
?
quality
)
onSwitchStreamEnd
;
Function
(
int
code
,
String
msg
)
onError
;
Function
(
SuperPlayerType
playerType
)
onPlayerTypeChange
;
Function
(
TXLivePlayerController
controller
,
String
url
)
onPlayTimeShiftLive
;
...
...
Flutter/example/lib/superplayer/ui/superplayer_bottom_view.dart
浏览文件 @
efb9316d
...
...
@@ -17,15 +17,15 @@ class VideoBottomView extends StatefulWidget {
class
_VideoBottomViewState
extends
State
<
VideoBottomView
>
{
static
const
TAG
=
"VideoBottomView"
;
double
_currentProgress
=
0
;
double
_playableProgress
=
0
;
int
_currentDuration
=
0
;
int
_videoDuration
=
0
;
double
_currentDuration
=
0
;
double
_videoDuration
=
0
;
double
_bufferedDuration
=
0
;
bool
_showFullScreenBtn
=
true
;
bool
_isPlayMode
=
false
;
bool
_isShowQuality
=
false
;
// only showed on fullscreen mode
bool
_isOnDraging
=
false
;
SuperPlayerType
_playerType
=
SuperPlayerType
.
VOD
;
VideoQuality
?
_currentQuality
;
@override
...
...
@@ -34,13 +34,15 @@ class _VideoBottomViewState extends State<VideoBottomView> {
_videoDuration
=
widget
.
_playerController
.
videoDuration
;
_currentDuration
=
widget
.
_playerController
.
currentDuration
;
}
else
if
(
null
!=
widget
.
_playerController
.
videoModel
)
{
_videoDuration
=
widget
.
_playerController
.
videoModel
!.
duration
;
_videoDuration
=
widget
.
_playerController
.
videoModel
!.
duration
.
toDouble
()
;
}
_isPlayMode
=
(
widget
.
_playerController
.
playerState
==
SuperPlayerState
.
PLAYING
);
_showFullScreenBtn
=
!
widget
.
_playerController
.
_isFullScreen
;
_isShowQuality
=
widget
.
_playerController
.
_isFullScreen
;
bool
isFullScreen
=
widget
.
_playerController
.
_playerUIStatus
==
SuperPlayerUIStatus
.
FULLSCREEN_MODE
;
_showFullScreenBtn
=
!
isFullScreen
;
_isShowQuality
=
isFullScreen
;
_currentQuality
=
widget
.
_playerController
.
currentQuality
;
_playerType
=
widget
.
_playerController
.
playerType
;
_fixProgress
();
super
.
initState
();
...
...
@@ -128,8 +130,8 @@ class _VideoBottomViewState extends State<VideoBottomView> {
return
Expanded
(
child:
VideoSlider
(
min:
0
,
max:
1
,
value:
_current
Progress
,
max:
_videoDuration
,
value:
_current
Duration
,
bufferedValue:
_playableProgress
,
activeColor:
Color
(
ColorResource
.
COLOR_MAIN_THEME
),
inactiveColor:
Color
(
ColorResource
.
COLOR_GRAY
),
...
...
@@ -138,16 +140,18 @@ class _VideoBottomViewState extends State<VideoBottomView> {
progressHeight:
2
,
sliderRadius:
4
,
sliderOutterRadius:
10
,
// 直播禁止时移
canDrag:
_playerType
==
SuperPlayerType
.
VOD
,
onDragUpdate:
(
value
)
{
_isOnDraging
=
true
;
},
onDragEnd:
(
value
)
{
setState
(()
{
_isOnDraging
=
false
;
_current
Progress
=
value
;
widget
.
_playerController
.
seek
(
_current
Progress
*
_video
Duration
);
_current
Duration
=
value
*
_videoDuration
;
widget
.
_playerController
.
seek
(
_currentDuration
);
LogUtils
.
d
(
TAG
,
"_current
Progress:
$_currentProgress
,_videoDuration:
$_videoDuration
,currentDuration:
${_currentProgress * _videoDuration}
"
);
"_current
Duration:
$_currentDuration
,_videoDuration:
$_videoDuration
"
);
});
},
),
...
...
@@ -166,7 +170,7 @@ class _VideoBottomViewState extends State<VideoBottomView> {
widget
.
_controller
.
onTapQuality
();
}
void
updateDuration
(
int
duration
,
int
videoDuration
,
double
bufferedDration
)
{
void
updateDuration
(
double
duration
,
double
videoDuration
,
double
bufferedDration
)
{
if
(
_isOnDraging
)
{
return
;
}
...
...
@@ -183,19 +187,6 @@ class _VideoBottomViewState extends State<VideoBottomView> {
}
void
_fixProgress
()
{
// provent division zero problem
if
(
_videoDuration
==
0
)
{
_currentProgress
=
0
;
}
else
{
_currentProgress
=
_currentDuration
/
_videoDuration
;
}
if
(
_currentProgress
<
0
)
{
_currentProgress
=
0
;
}
if
(
_currentProgress
>
1
)
{
_currentProgress
=
1
;
}
if
(
_bufferedDuration
==
0
)
{
_playableProgress
=
0
;
}
else
{
...
...
@@ -218,15 +209,24 @@ class _VideoBottomViewState extends State<VideoBottomView> {
}
}
void
updateFullScreen
(
bool
showFullScreen
)
{
void
updatePlayerType
(
SuperPlayerType
type
)
{
if
(
_playerType
!=
type
)
{
setState
(()
{
_playerType
=
type
;
});
}
}
void
updateUIStatus
(
int
status
)
{
setState
(()
{
_showFullScreenBtn
=
!
showFullScreen
;
_isShowQuality
=
showFullScreen
;
bool
isFullScreen
=
widget
.
_playerController
.
_playerUIStatus
==
SuperPlayerUIStatus
.
FULLSCREEN_MODE
;
_showFullScreenBtn
=
!
isFullScreen
;
_isShowQuality
=
isFullScreen
;
});
}
String
_buildTextString
(
int
time
)
{
Duration
duration
=
Duration
(
seconds:
time
);
String
_buildTextString
(
double
time
)
{
Duration
duration
=
Duration
(
seconds:
time
.
toInt
()
);
// 返回此持续时间跨越的整秒数。
String
inSeconds
=
(
duration
.
inSeconds
%
60
).
toString
().
padLeft
(
2
,
"0"
);
// 返回此持续时间跨越的整分钟数。
...
...
Flutter/example/lib/superplayer/ui/superplayer_cover_view.dart
浏览文件 @
efb9316d
...
...
@@ -43,7 +43,7 @@ class _SuperPlayerCoverViewState extends State<SuperPlayerCoverView> {
}
return
Visibility
(
visible:
_isShowCover
,
visible:
_isShowCover
&&
hasCover
,
child:
Positioned
.
fill
(
top:
topBottomOffset
,
bottom:
topBottomOffset
,
...
...
@@ -53,7 +53,11 @@ class _SuperPlayerCoverViewState extends State<SuperPlayerCoverView> {
onDoubleTap:
_onDoubleTapVideo
,
onTap:
_onSingleTapVideo
,
child:
Container
(
child:
hasCover
?
Image
.
network
(
coverUrl
,
fit:
BoxFit
.
cover
,)
:
Container
(),
decoration:
BoxDecoration
(
// 增加一个半透明背景,防止透明封面图的出现
color:
Color
(
ColorResource
.
COLOR_TRANS_GRAY
)
),
child:
Image
.
network
(
coverUrl
,
fit:
BoxFit
.
cover
),
)
)),
);
...
...
Flutter/example/lib/superplayer/ui/superplayer_more_view.dart
浏览文件 @
efb9316d
...
...
@@ -18,6 +18,7 @@ class _SuperPlayerMoreViewState extends State<SuperPlayerMoreView> {
double
_currentVolumn
=
0
;
bool
_isShowMoreView
=
false
;
bool
_isOpenAccelerate
=
true
;
bool
_isVodPlay
=
false
;
String
_currentRate
=
""
;
Map
<
String
,
double
>
playRateStr
=
{
"1.0x"
:
1.0
,
"1.25x"
:
1.25
,
"1.5x"
:
1.5
,
"2.0x"
:
2.0
};
StreamSubscription
?
volumeSubscription
;
...
...
@@ -25,6 +26,7 @@ class _SuperPlayerMoreViewState extends State<SuperPlayerMoreView> {
@override
void
initState
()
{
super
.
initState
();
_isVodPlay
=
widget
.
controller
.
getIsVodPlay
();
double
playerPlayRate
=
widget
.
controller
.
getPlayRate
();
for
(
String
rateStr
in
playRateStr
.
keys
)
{
if
(
playerPlayRate
==
playRateStr
[
rateStr
])
{
...
...
@@ -132,10 +134,13 @@ class _SuperPlayerMoreViewState extends State<SuperPlayerMoreView> {
),
));
}
return
Container
(
margin:
EdgeInsets
.
only
(
top:
10
,
bottom:
10
),
child:
Row
(
children:
playRateChild
,
return
Visibility
(
visible:
_isVodPlay
,
child:
Container
(
margin:
EdgeInsets
.
only
(
top:
10
,
bottom:
10
),
child:
Row
(
children:
playRateChild
,
),
),
);
}
...
...
@@ -243,6 +248,15 @@ class _SuperPlayerMoreViewState extends State<SuperPlayerMoreView> {
}
}
void
updatePlayerType
(
SuperPlayerType
playerType
)
{
bool
isVodPlay
=
playerType
==
SuperPlayerType
.
VOD
;
if
(
isVodPlay
!=
_isVodPlay
)
{
setState
(()
{
_isVodPlay
=
isVodPlay
;
});
}
}
@override
void
dispose
()
{
super
.
dispose
();
...
...
@@ -255,6 +269,7 @@ class _MoreViewController {
DoubleFunction
getPlayRate
;
Function
(
bool
value
)
siwtchAccelerate
;
Function
(
double
playRate
)
onChangedPlayRate
;
BoolFunction
getIsVodPlay
;
_MoreViewController
(
this
.
getAccelerateIsOpen
,
this
.
getPlayRate
,
this
.
siwtchAccelerate
,
this
.
onChangedPlayRate
);
_MoreViewController
(
this
.
getAccelerateIsOpen
,
this
.
getPlayRate
,
this
.
siwtchAccelerate
,
this
.
onChangedPlayRate
,
this
.
getIsVodPlay
);
}
Flutter/example/lib/superplayer/ui/superplayer_title_view.dart
浏览文件 @
efb9316d
...
...
@@ -76,9 +76,9 @@ class _VideoTitleViewState extends State<_VideoTitleView> {
}
}
void
update
FullScreen
(
bool
showFullScreen
)
{
void
update
UIStatus
(
int
status
)
{
setState
(()
{
_isFullScreen
=
s
howFullScreen
;
_isFullScreen
=
s
tatus
==
SuperPlayerUIStatus
.
FULLSCREEN_MODE
;
});
}
}
...
...
Flutter/example/lib/superplayer/ui/superplayer_video_slider.dart
浏览文件 @
efb9316d
...
...
@@ -17,6 +17,7 @@ class VideoSlider extends StatefulWidget {
final
Color
?
bufferedColor
;
final
Color
?
sliderColor
;
final
Color
?
sliderOutterColor
;
bool
?
canDrag
=
true
;
// calback
final
Function
?
onDragStart
;
...
...
@@ -38,12 +39,16 @@ class VideoSlider extends StatefulWidget {
this
.
sliderOutterColor
,
this
.
onDragStart
,
this
.
onDragUpdate
,
this
.
onDragEnd
})
{
this
.
onDragEnd
,
this
.
canDrag
})
{
double
range
=
(
max
-
min
);
_checkRange
(
range
,
valueName:
"max - min"
);
double
currentProgress
=
value
/
range
;
double
?
bufferedProgress
=
bufferedValue
!=
null
?
bufferedValue
!
/
range
:
null
;
controller
=
_VideoSliderController
(
currentProgress
,
bufferedProgress:
bufferedProgress
);
if
(
range
<=
0
)
{
controller
=
_VideoSliderController
(
1
,
bufferedProgress:
1
);
}
else
{
double
currentProgress
=
value
/
range
;
double
?
bufferedProgress
=
bufferedValue
!=
null
?
bufferedValue
!
/
range
:
null
;
controller
=
_VideoSliderController
(
currentProgress
,
bufferedProgress:
bufferedProgress
);
}
}
@override
...
...
@@ -77,24 +82,31 @@ class VideoSliderState extends State<VideoSlider> {
double
rightPadding
=
overlayRadius
;
return
GestureDetector
(
onHorizontalDragStart:
(
DragStartDetails
details
)
{
isDraging
=
true
;
widget
.
onDragStart
?.
call
();
if
(
widget
.
canDrag
!)
{
isDraging
=
true
;
widget
.
onDragStart
?.
call
();
}
},
onHorizontalDragUpdate:
(
DragUpdateDetails
details
)
{
isDraging
=
true
;
_seekToPosition
(
details
.
globalPosition
);
widget
.
onDragUpdate
?.
call
(
widget
.
controller
.
progress
);
if
(
widget
.
canDrag
!)
{
isDraging
=
true
;
_seekToPosition
(
details
.
globalPosition
);
widget
.
onDragUpdate
?.
call
(
widget
.
controller
.
progress
);}
},
onHorizontalDragEnd:
(
DragEndDetails
details
)
{
isDraging
=
false
;
widget
.
onDragEnd
?.
call
(
widget
.
controller
.
progress
);
if
(
widget
.
canDrag
!)
{
isDraging
=
false
;
widget
.
onDragEnd
?.
call
(
widget
.
controller
.
progress
);}
},
onHorizontalDragCancel:
()
{
isDraging
=
false
;
widget
.
onDragEnd
?.
call
(
widget
.
controller
.
progress
);
if
(
widget
.
canDrag
!)
{
isDraging
=
false
;
widget
.
onDragEnd
?.
call
(
widget
.
controller
.
progress
);}
},
onTapDown:
(
TapDownDetails
details
)
{
_seekToPosition
(
details
.
globalPosition
);
if
(
widget
.
canDrag
!)
{
_seekToPosition
(
details
.
globalPosition
);
}
},
child:
Center
(
child:
Container
(
...
...
Flutter/example/lib/superplayer/ui/superplayer_widget.dart
浏览文件 @
efb9316d
...
...
@@ -20,7 +20,6 @@ class SuperPlayerViewState extends State<SuperPlayerView> with WidgetsBindingObs
static
const
TAG
=
"SuperPlayerViewState"
;
late
SuperPlayerController
_playController
;
bool
_isFullScreen
=
false
;
bool
_isFloatingMode
=
false
;
bool
_isPlaying
=
false
;
bool
_isLoading
=
true
;
...
...
@@ -28,8 +27,6 @@ class SuperPlayerViewState extends State<SuperPlayerView> with WidgetsBindingObs
double
_radioWidth
=
0
;
double
_radioHeight
=
0
;
double
_topAndBottomMargin
=
0
;
double
_leftAndRightMargin
=
0
;
double
_aspectRatio
=
16.0
/
9.0
;
double
_videoWidth
=
0
;
double
_videoHeight
=
0
;
...
...
@@ -56,6 +53,7 @@ class SuperPlayerViewState extends State<SuperPlayerView> with WidgetsBindingObs
GlobalKey
<
_VideoTitleViewState
>
_videoTitleKey
=
GlobalKey
();
GlobalKey
<
_SuperPlayerCoverViewState
>
_coverViewKey
=
GlobalKey
();
GlobalKey
<
_SuperPlayerMoreViewState
>
_moreViewKey
=
GlobalKey
();
GlobalKey
<
_SuperPlayerFloatState
>
floatPlayerKey
=
GlobalKey
();
@override
void
initState
()
{
...
...
@@ -74,7 +72,8 @@ class SuperPlayerViewState extends State<SuperPlayerView> with WidgetsBindingObs
()
=>
_playController
.
_isOpenHWAcceleration
,
()
=>
_playController
.
currentPlayRate
,
(
value
)
=>
_playController
.
enableHardwareDecode
(
value
),
(
playRate
)
=>
_playController
.
setPlayRate
(
playRate
));
(
playRate
)
=>
_playController
.
setPlayRate
(
playRate
),
()
=>
_playController
.
playerType
==
SuperPlayerType
.
VOD
);
_playController
.
onPlayerNetStatusBroadcast
.
listen
((
event
)
{
dynamic
wd
=
(
event
[
"VIDEO_WIDTH"
]);
dynamic
hd
=
(
event
[
"VIDEO_HEIGHT"
]);
...
...
@@ -94,7 +93,7 @@ class SuperPlayerViewState extends State<SuperPlayerView> with WidgetsBindingObs
Navigator
.
of
(
context
).
pop
();
if
(
_isPlaying
)
{
// pause play when exit PIP, prevent user just close PIP, but not back to app
_playController
.
_vodPlayerController
?
.
pause
();
_playController
.
getCurrentController
()
.
pause
();
}
}
else
if
(
Platform
.
isIOS
)
{
EasyLoading
.
dismiss
();
...
...
@@ -106,7 +105,7 @@ class SuperPlayerViewState extends State<SuperPlayerView> with WidgetsBindingObs
// enter floatingMode
if
(
Platform
.
isAndroid
)
{
Navigator
.
of
(
context
).
push
(
MaterialPageRoute
(
builder:
(
context
)
{
return
SuperPlayerFloatView
(
_playController
,
_aspectRatio
);
return
SuperPlayerFloatView
(
_playController
,
_aspectRatio
,
key:
floatPlayerKey
);
}));
}
else
if
(
Platform
.
isIOS
)
{
EasyLoading
.
showToast
(
StringResource
.
OPEN_PIP
);
...
...
@@ -138,6 +137,7 @@ class SuperPlayerViewState extends State<SuperPlayerView> with WidgetsBindingObs
_onResume
();
}
}
// 画中画模式下,不进行旋转屏幕操作
if
(
eventCode
==
TXVodPlayEvent
.
EVENT_ORIENTATION_CHANGED
&&
!
_isFloatingMode
)
{
int
orientation
=
event
[
TXVodPlayEvent
.
EXTRA_NAME_ORIENTATION
];
...
...
@@ -151,11 +151,12 @@ class SuperPlayerViewState extends State<SuperPlayerView> with WidgetsBindingObs
void
_registerObserver
()
{
_playController
.
_observer
=
_SuperPlayerObserver
(()
{
// onNewVideoPlay
_isFullScreen
=
false
;
_isPlaying
=
false
;
_isShowControlView
=
false
;
_isShowCover
=
true
;
_isLoading
=
true
;
setState
(()
{
_isPlaying
=
false
;
_isShowControlView
=
false
;
_isShowCover
=
true
;
_isLoading
=
true
;
});
},
()
{
// onPlayPrepare
_isShowCover
=
true
;
...
...
@@ -186,6 +187,7 @@ class SuperPlayerViewState extends State<SuperPlayerView> with WidgetsBindingObs
},
(
current
,
duration
,
playableDuration
)
{
// onPlayProgress
_videoBottomKey
.
currentState
?.
updateDuration
(
current
,
duration
,
playableDuration
);
floatPlayerKey
.
currentState
?.
updateDuration
(
current
,
duration
);
},
(
position
)
{
// onSeek
},
(
success
,
playerType
,
quality
)
{
...
...
@@ -197,6 +199,8 @@ class SuperPlayerViewState extends State<SuperPlayerView> with WidgetsBindingObs
_togglePlayUIState
(
false
);
},
(
playerType
)
{
// onPlayerTypeChange
_videoBottomKey
.
currentState
?.
updatePlayerType
(
playerType
);
_moreViewKey
.
currentState
?.
updatePlayerType
(
playerType
);
},
(
controller
,
url
)
{
// onPlayTimeShiftLive
},
(
qualityList
,
defaultQuality
)
{
...
...
@@ -207,9 +211,7 @@ class SuperPlayerViewState extends State<SuperPlayerView> with WidgetsBindingObs
// onVideoImageSpriteAndKeyFrameChanged
},
()
{
// onSysBackPress
if
(
_isFullScreen
)
{
_onControlFullScreen
();
}
_onControlFullScreen
();
},
()
{
// onDispose
_playController
.
_observer
=
null
;
// close observer
...
...
@@ -218,23 +220,23 @@ class SuperPlayerViewState extends State<SuperPlayerView> with WidgetsBindingObs
Navigator
.
of
(
context
).
push
(
MaterialPageRoute
(
builder:
(
context
)
{
return
SuperPlayerFullScreenView
(
_playController
,
_superPlayerFullUIController
);
}));
WidgetsBinding
.
instance
?.
removeObserver
(
this
);
_videoBottomKey
.
currentState
?.
updateFullScreen
(
true
);
_videoTitleKey
.
currentState
?.
updateFullScreen
(
true
);
_playController
.
_updateFullScreenState
(
true
);
WidgetsBinding
.
instance
.
removeObserver
(
this
);
_videoBottomKey
.
currentState
?.
updateUIStatus
(
SuperPlayerUIStatus
.
FULLSCREEN_MODE
);
_videoTitleKey
.
currentState
?.
updateUIStatus
(
SuperPlayerUIStatus
.
FULLSCREEN_MODE
);
_playController
.
_updatePlayerUIStatus
(
SuperPlayerUIStatus
.
FULLSCREEN_MODE
);
hideControlView
();
},
()
{
Navigator
.
of
(
context
).
pop
();
_videoBottomKey
.
currentState
?.
update
FullScreen
(
false
);
_videoTitleKey
.
currentState
?.
update
FullScreen
(
false
);
_playController
.
_update
FullScreenState
(
false
);
_videoBottomKey
.
currentState
?.
update
UIStatus
(
SuperPlayerUIStatus
.
WINDOW_MODE
);
_videoTitleKey
.
currentState
?.
update
UIStatus
(
SuperPlayerUIStatus
.
WINDOW_MODE
);
_playController
.
_update
PlayerUIStatus
(
SuperPlayerUIStatus
.
WINDOW_MODE
);
hideControlView
();
});
WidgetsBinding
.
instance
?
.
addObserver
(
this
);
WidgetsBinding
.
instance
.
addObserver
(
this
);
}
void
_initPlayerState
()
{
_isFullScreen
=
_playController
.
_isFullScreen
;
SuperPlayerState
superPlayerState
=
_playController
.
getPlayerState
();
switch
(
superPlayerState
)
{
case
SuperPlayerState
.
PLAYING
:
...
...
@@ -261,7 +263,7 @@ class SuperPlayerViewState extends State<SuperPlayerView> with WidgetsBindingObs
_registerObserver
();
// 由于pop之后,无法触发任何回调,并且fulscreen的controller调用setState无效,
// 所以这里向UI线程添加一个任务,这个任务会在回到这个界面之后触发,来保证播放状态正确。
WidgetsBinding
.
instance
?
.
addPostFrameCallback
((
timeStamp
)
=>
setState
(()
{
WidgetsBinding
.
instance
.
addPostFrameCallback
((
timeStamp
)
=>
setState
(()
{
_initPlayerState
();
_resizeVideo
();
}));
...
...
@@ -273,9 +275,10 @@ class SuperPlayerViewState extends State<SuperPlayerView> with WidgetsBindingObs
if
(
state
==
AppLifecycleState
.
resumed
)
{
// 页面从后台回来
// 不更新状态,直接resume
_playController
.
_vodPlayerController
?
.
resume
();
_playController
.
getCurrentController
()
.
resume
();
// 从后台回来之后,如果手机横竖屏状态发生更改,被改为竖屏,那么这里根据判断切换横屏
if
(
_isFullScreen
&&
defaultTargetPlatform
==
TargetPlatform
.
iOS
)
{
if
(
_playController
.
_playerUIStatus
==
SuperPlayerUIStatus
.
FULLSCREEN_MODE
&&
defaultTargetPlatform
==
TargetPlatform
.
iOS
)
{
Orientation
currentOrientation
=
MediaQuery
.
of
(
context
).
orientation
;
bool
isLandscape
=
currentOrientation
==
Orientation
.
landscape
;
if
(!
isLandscape
)
{
...
...
@@ -285,13 +288,14 @@ class SuperPlayerViewState extends State<SuperPlayerView> with WidgetsBindingObs
}
else
if
(
state
==
AppLifecycleState
.
inactive
)
{
// 页面退到后台
// 不更新状态,直接pause
_playController
.
_vodPlayerController
?
.
pause
();
_playController
.
getCurrentController
()
.
pause
();
}
}
}
void
_calculateSize
(
double
videoWidth
,
double
videoHeight
)
{
if
((
0
!=
videoWidth
&&
0
!=
videoHeight
)
&&
(
_videoWidth
!=
videoWidth
&&
_videoHeight
!=
videoHeight
))
{
floatPlayerKey
.
currentState
?.
_calculateSize
(
videoWidth
,
videoHeight
);
_videoWidth
=
videoWidth
;
_videoHeight
=
videoHeight
;
_resizeVideo
();
...
...
@@ -306,7 +310,7 @@ class SuperPlayerViewState extends State<SuperPlayerView> with WidgetsBindingObs
// 当有视频宽高数据的时候,按照 全屏:高度为基准,计算宽度。 非全屏:宽度为基准,计算高度的方式进行宽高计算
// 当没有视频宽高数据的时候,按照 全屏:等于屏幕宽高。 非全屏:16:9 的方式进行宽高计算
if
(
_videoWidth
<=
0
||
_videoHeight
<=
0
)
{
if
(
_
isFullScreen
)
{
if
(
_
playController
.
_playerUIStatus
==
SuperPlayerUIStatus
.
FULLSCREEN_MODE
)
{
_radioWidth
=
isLandscape
?
size
.
width
:
size
.
height
;
_radioHeight
=
isLandscape
?
size
.
height
:
size
.
width
;
_aspectRatio
=
_radioWidth
/
_radioHeight
;
...
...
@@ -316,7 +320,7 @@ class SuperPlayerViewState extends State<SuperPlayerView> with WidgetsBindingObs
_aspectRatio
=
16.0
/
9.0
;
}
}
else
{
if
(
_
isFullScreen
)
{
if
(
_
playController
.
_playerUIStatus
==
SuperPlayerUIStatus
.
FULLSCREEN_MODE
)
{
double
playerHeight
=
isLandscape
?
size
.
width
:
size
.
height
;
// remain height
double
videoRadio
=
_videoWidth
/
_videoHeight
;
...
...
@@ -359,20 +363,23 @@ class SuperPlayerViewState extends State<SuperPlayerView> with WidgetsBindingObs
Widget
_getPipEnterView
()
{
return
Visibility
(
visible:
_isShowControlView
&&
!
_isFullScreen
,
visible:
_isShowControlView
&&
_playController
.
_playerUIStatus
==
SuperPlayerUIStatus
.
WINDOW_MODE
,
child:
Positioned
(
right:
10
,
top:
0
,
bottom:
0
,
child:
Center
(
child:
InkWell
(
onTap:
_onEnterPipMode
,
child:
Image
(
width:
30
,
height:
30
,
image:
AssetImage
(
"images/ic_pip_play_icon.png"
),
),
),
onTap:
_onEnterPipMode
,
child:
Container
(
padding:
EdgeInsets
.
all
(
5
),
// expand click area
child:
Image
(
width:
30
,
height:
30
,
image:
AssetImage
(
"images/ic_pip_play_icon.png"
),
),
)),
),
),
);
...
...
@@ -449,7 +456,9 @@ class SuperPlayerViewState extends State<SuperPlayerView> with WidgetsBindingObs
splashColor:
Colors
.
transparent
,
child:
Center
(
child:
AspectRatio
(
aspectRatio:
_aspectRatio
,
child:
TXPlayerVideo
(
controller:
_playController
.
_vodPlayerController
!)),
aspectRatio:
_aspectRatio
,
child:
TXPlayerVideo
(
controller:
_playController
.
getCurrentController
(),
playerStream:
_playController
.
getPlayerStream
())),
),
);
}
...
...
@@ -461,34 +470,34 @@ class SuperPlayerViewState extends State<SuperPlayerView> with WidgetsBindingObs
top:
topBottomOffset
,
left:
0
,
right:
0
,
child:
_VideoTitleView
(
_titleViewController
,
_isFullScreen
,
_playController
.
_getPlayName
(),
_videoTitleKey
),
child:
_VideoTitleView
(
_titleViewController
,
_playController
.
_playerUIStatus
==
SuperPlayerUIStatus
.
FULLSCREEN_MODE
,
_playController
.
_getPlayName
(),
_videoTitleKey
),
),
);
}
void
_onEnterPipMode
()
async
{
if
(!
_isFloatingMode
)
{
int
?
result
=
await
_playController
.
enterPictureInPictureMode
(
int
result
=
await
_playController
.
enterPictureInPictureMode
(
backIcon:
"images/ic_pip_play_replay.png"
,
playIcon:
"images/ic_pip_play_normal.png"
,
pauseIcon:
"images/ic_pip_play_pause.png"
,
forwardIcon:
"images/ic_pip_play_forward.png"
);
if
(
null
!=
result
)
{
String
failedStr
=
""
;
if
(
result
!=
TXVodPlayEvent
.
NO_ERROR
)
{
if
(
result
==
TXVodPlayEvent
.
ERROR_PIP_LOWER_VERSION
)
{
failedStr
=
"enterPip failed,because android version is too low,Minimum supported version is android 24"
;
}
else
if
(
result
==
TXVodPlayEvent
.
ERROR_PIP_DENIED_PERMISSION
)
{
failedStr
=
"enterPip failed,because PIP feature is disabled or device not support"
;
}
else
if
(
result
==
TXVodPlayEvent
.
ERROR_PIP_ACTIVITY_DESTROYED
)
{
failedStr
=
"enterPip failed,because activity is destroyed"
;
}
else
{
failedStr
=
"enterPip failed,unkonw error"
;
}
LogUtils
.
e
(
TAG
,
failedStr
);
String
failedStr
=
""
;
if
(
result
!=
TXVodPlayEvent
.
NO_ERROR
)
{
if
(
result
==
TXVodPlayEvent
.
ERROR_PIP_LOWER_VERSION
)
{
failedStr
=
"enterPip failed,because android version is too low,Minimum supported version is android 24"
;
}
else
if
(
result
==
TXVodPlayEvent
.
ERROR_PIP_DENIED_PERMISSION
)
{
failedStr
=
"enterPip failed,because PIP feature is disabled or device not support"
;
}
else
if
(
result
==
TXVodPlayEvent
.
ERROR_PIP_ACTIVITY_DESTROYED
)
{
failedStr
=
"enterPip failed,because activity is destroyed"
;
}
else
{
failedStr
=
"enterPip failed,unkonw error"
;
}
}
else
{
LogUtils
.
e
(
TAG
,
"enterPip failed, vodPlayer is release"
);
LogUtils
.
e
(
TAG
,
failedStr
);
}
}
}
...
...
@@ -508,7 +517,7 @@ class SuperPlayerViewState extends State<SuperPlayerView> with WidgetsBindingObs
}
void
_onTapBack
()
async
{
if
(
_
isFullScreen
)
{
if
(
_
playController
.
_playerUIStatus
==
SuperPlayerUIStatus
.
FULLSCREEN_MODE
)
{
_onControlFullScreen
();
}
_playController
.
_onBackTap
();
...
...
@@ -563,9 +572,10 @@ class SuperPlayerViewState extends State<SuperPlayerView> with WidgetsBindingObs
}
void
_onControlFullScreen
()
{
_isFullScreen
=
!
_isFullScreen
;
_handleFullScreen
(
_isFullScreen
);
_playController
.
_updateFullScreenState
(
_isFullScreen
);
if
(
_playController
.
_playerUIStatus
!=
SuperPlayerUIStatus
.
PIP_MODE
)
{
bool
toSwitchFullScreen
=
_playController
.
_playerUIStatus
==
SuperPlayerUIStatus
.
WINDOW_MODE
;
_handleFullScreen
(
toSwitchFullScreen
);
}
}
void
_onControlQualityListView
()
{
...
...
@@ -623,7 +633,7 @@ class SuperPlayerViewState extends State<SuperPlayerView> with WidgetsBindingObs
@override
void
dispose
()
{
WidgetsBinding
.
instance
?
.
removeObserver
(
this
);
WidgetsBinding
.
instance
.
removeObserver
(
this
);
_pipSubscription
?.
cancel
();
_volumeSubscription
?.
cancel
();
super
.
dispose
();
...
...
@@ -687,42 +697,24 @@ class SuperPlayerFloatView extends StatefulWidget {
final
SuperPlayerController
_controller
;
final
double
initAspectRatio
;
SuperPlayerFloatView
(
this
.
_controller
,
this
.
initAspectRatio
);
SuperPlayerFloatView
(
this
.
_controller
,
this
.
initAspectRatio
,
{
Key
?
key
})
:
super
(
key:
key
);
@override
State
<
StatefulWidget
>
createState
()
=>
_SuperPlayerFloatState
();
}
class
_SuperPlayerFloatState
extends
State
<
SuperPlayerFloatView
>
{
double
_currentProgress
=
0
;
int
_videoDuration
=
0
;
int
_currentDuration
=
0
;
double
_videoDuration
=
0
;
double
_currentDuration
=
0
;
double
_aspectRatio
=
16.0
/
9.0
;
double
_videoWidth
=
0
;
double
_videoHeight
=
0
;
StreamSubscription
?
streamSubscription
;
StreamSubscription
?
sizeStreamSubscription
;
@override
void
initState
()
{
super
.
initState
();
_aspectRatio
=
widget
.
initAspectRatio
;
streamSubscription
=
widget
.
_controller
.
_vodPlayerController
?.
onPlayerEventBroadcast
.
listen
((
event
)
{
int
eventCode
=
event
[
'event'
];
if
(
eventCode
==
TXVodPlayEvent
.
PLAY_EVT_PLAY_PROGRESS
)
{
updateDuration
(
widget
.
_controller
.
currentDuration
,
widget
.
_controller
.
videoDuration
);
}
});
sizeStreamSubscription
=
widget
.
_controller
.
onPlayerNetStatusBroadcast
.
listen
((
event
)
{
dynamic
wd
=
(
event
[
"VIDEO_WIDTH"
]);
dynamic
hd
=
(
event
[
"VIDEO_HEIGHT"
]);
if
(
null
!=
wd
&&
null
!=
hd
)
{
double
w
=
wd
.
toDouble
();
double
h
=
hd
.
toDouble
();
_calculateSize
(
w
,
h
);
}
});
}
void
_calculateSize
(
double
videoWidth
,
double
videoHeight
)
{
...
...
@@ -743,33 +735,17 @@ class _SuperPlayerFloatState extends State<SuperPlayerFloatView> {
}
}
void
updateDuration
(
int
duration
,
int
videoDuration
)
{
void
updateDuration
(
double
duration
,
double
videoDuration
)
{
if
(
duration
!=
_currentDuration
||
_videoDuration
!=
videoDuration
)
{
if
(
duration
<=
videoDuration
)
{
setState
(()
{
_currentDuration
=
duration
;
_videoDuration
=
videoDuration
;
_fixProgress
();
});
}
}
}
void
_fixProgress
()
{
// provent division zero problem
if
(
_videoDuration
==
0
)
{
_currentProgress
=
0
;
}
else
{
_currentProgress
=
_currentDuration
/
_videoDuration
;
}
if
(
_currentProgress
<
0
)
{
_currentProgress
=
0
;
}
if
(
_currentProgress
>
1
)
{
_currentProgress
=
1
;
}
}
@override
Widget
build
(
BuildContext
context
)
{
return
Scaffold
(
...
...
@@ -782,7 +758,9 @@ class _SuperPlayerFloatState extends State<SuperPlayerFloatView> {
Center
(
child:
AspectRatio
(
aspectRatio:
_aspectRatio
,
child:
TXPlayerVideo
(
controller:
widget
.
_controller
.
_vodPlayerController
!),
child:
TXPlayerVideo
(
controller:
widget
.
_controller
.
getCurrentController
(),
),
),
),
Positioned
(
...
...
@@ -801,11 +779,11 @@ class _SuperPlayerFloatState extends State<SuperPlayerFloatView> {
data:
ThemeResource
.
getMiniSliderTheme
(),
child:
Slider
(
min:
0
,
max:
1
,
value:
_current
Progress
,
max:
_videoDuration
,
value:
_current
Duration
,
onChanged:
(
double
value
)
{
setState
(()
{
_current
Progress
=
value
;
_current
Duration
=
value
;
});
},
));
...
...
@@ -815,7 +793,6 @@ class _SuperPlayerFloatState extends State<SuperPlayerFloatView> {
void
dispose
()
{
super
.
dispose
();
// 移除的时候,解除对进度事件的订阅
streamSubscription
?.
cancel
();
sizeStreamSubscription
?.
cancel
();
}
}
...
...
Flutter/example/pubspec.yaml
浏览文件 @
efb9316d
...
...
@@ -7,7 +7,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev
environment
:
sdk
:
'
>=2.12.0
<3.0.0'
flutter
:
"
>=
1.2
0.0"
flutter
:
"
>=
2.
0.0"
dependencies
:
flutter
:
...
...
Flutter/ios/Classes/FTXLivePlayer.m
浏览文件 @
efb9316d
...
...
@@ -2,6 +2,7 @@
#import "FTXLivePlayer.h"
#import "FTXPlayerEventSinkQueue.h"
#import "FTXTransformation.h"
#import <TXLiteAVSDK_Professional/TXLiteAVSDK.h>
#import <Flutter/Flutter.h>
#import <stdatomic.h>
...
...
@@ -25,7 +26,7 @@ static const int uninitialized = -1;
// 旧的一帧
CVPixelBufferRef
_lastBuffer
;
int64_t
_textureId
;
id
<
FlutterPluginRegistrar
>
_registrar
;
id
<
FlutterTextureRegistry
>
_textureRegistry
;
}
...
...
@@ -39,20 +40,20 @@ static const int uninitialized = -1;
_textureId
=
-
1
;
_eventSink
=
[
FTXPlayerEventSinkQueue
new
];
_netStatusSink
=
[
FTXPlayerEventSinkQueue
new
];
__weak
typeof
(
self
)
weakSelf
=
self
;
_methodChannel
=
[
FlutterMethodChannel
methodChannelWithName
:[
@"cloud.tencent.com/txliveplayer/"
stringByAppendingString
:[
self
.
playerId
stringValue
]]
binaryMessenger
:[
registrar
messenger
]];
[
_methodChannel
setMethodCallHandler
:
^
(
FlutterMethodCall
*
_Nonnull
call
,
FlutterResult
_Nonnull
result
)
{
[
weakSelf
handleMethodCall
:
call
result
:
result
];
}];
_eventChannel
=
[
FlutterEventChannel
eventChannelWithName
:[
@"cloud.tencent.com/txliveplayer/event/"
stringByAppendingString
:[
self
.
playerId
stringValue
]]
binaryMessenger
:[
registrar
messenger
]];
[
_eventChannel
setStreamHandler
:
self
];
_netStatusChannel
=
[
FlutterEventChannel
eventChannelWithName
:[
@"cloud.tencent.com/txliveplayer/net/"
stringByAppendingString
:[
self
.
playerId
stringValue
]]
binaryMessenger
:[
registrar
messenger
]];
[
_netStatusChannel
setStreamHandler
:
self
];
}
return
self
;
}
...
...
@@ -61,7 +62,7 @@ static const int uninitialized = -1;
[
self
stopPlay
];
[
_txLivePlayer
removeVideoWidget
];
_txLivePlayer
=
nil
;
if
(
_textureId
>=
0
)
{
[
_textureRegistry
unregisterTexture
:
_textureId
];
_textureId
=
-
1
;
...
...
@@ -81,7 +82,7 @@ static const int uninitialized = -1;
CVPixelBufferRelease
(
_lastBuffer
);
_lastBuffer
=
nil
;
}
[
_methodChannel
setMethodCallHandler
:
nil
];
_methodChannel
=
nil
;
...
...
@@ -101,7 +102,7 @@ static const int uninitialized = -1;
int64_t
tId
=
[
_textureRegistry
registerTexture
:
self
];
_textureId
=
tId
;
}
if
(
_txLivePlayer
!=
nil
)
{
TXLivePlayConfig
*
config
=
[
TXLivePlayConfig
new
];
[
config
setPlayerPixelFormatType
:
kCVPixelFormatType_420YpCbCr8BiPlanarFullRange
];
...
...
@@ -137,11 +138,12 @@ static const int uninitialized = -1;
}
}
-
(
void
)
switchStream
:
(
NSString
*
)
url
-
(
int
)
switchStream
:
(
NSString
*
)
url
{
if
(
_txLivePlayer
!=
nil
)
{
[
_txLivePlayer
switchStream
:
url
];
return
[
_txLivePlayer
switchStream
:
url
];
}
return
-
1
;
}
-
(
int
)
seek
:
(
float
)
progress
...
...
@@ -207,7 +209,7 @@ static const int uninitialized = -1;
-
(
void
)
setLiveMode
:
(
int
)
type
{
TXLivePlayConfig
*
config
=
_txLivePlayer
.
config
;
if
(
type
==
0
)
{
//自动模式
config
.
bAutoAdjustCacheTime
=
YES
;
...
...
@@ -238,7 +240,7 @@ static const int uninitialized = -1;
if
(
_txLivePlayer
!=
nil
)
{
return
[
_txLivePlayer
prepareLiveSeek
:
domain
bizId
:
bizId
];
}
return
uninitialized
;
}
...
...
@@ -246,7 +248,7 @@ static const int uninitialized = -1;
if
(
_txLivePlayer
!=
nil
)
{
return
[
_txLivePlayer
resumeLive
];
}
return
uninitialized
;
}
...
...
@@ -262,12 +264,26 @@ static const int uninitialized = -1;
}
}
-
(
BOOL
)
enableHardwareDecode
:
(
BOOL
)
enable
{
if
(
_txLivePlayer
!=
nil
)
{
_txLivePlayer
.
enableHWAcceleration
=
enable
;
}
return
false
;
}
-
(
void
)
setPlayConfig
:
(
NSDictionary
*
)
args
{
if
(
_txLivePlayer
!=
nil
&&
[
args
[
@"config"
]
isKindOfClass
:[
NSDictionary
class
]])
{
_txLivePlayer
.
config
=
[
FTXTransformation
transformToLiveConfig
:
args
];
}
}
#pragma mark -
-
(
void
)
handleMethodCall
:
(
FlutterMethodCall
*
)
call
result
:
(
FlutterResult
)
result
{
NSDictionary
*
args
=
call
.
arguments
;
if
([
@"init"
isEqualToString
:
call
.
method
]){
BOOL
onlyAudio
=
[
args
[
@"onlyAudio"
]
boolValue
];
NSNumber
*
textureId
=
[
self
createPlayer
:
onlyAudio
];
...
...
@@ -309,12 +325,15 @@ static const int uninitialized = -1;
result
(
nil
);
}
else
if
([
@"destory"
isEqualToString
:
call
.
method
])
{
[
self
destory
];
result
(
nil
);
}
else
if
([
@"setRenderRotation"
isEqualToString
:
call
.
method
])
{
int
rotation
=
[
args
[
@"rotation"
]
intValue
];
[
self
setRenderRotation
:
rotation
];
result
(
nil
);
}
else
if
([
@"switchStream"
isEqualToString
:
call
.
method
])
{
NSString
*
url
=
args
[
@"url"
];
[
self
switchStream
:
url
];
int
switchResult
=
[
self
switchStream
:
url
];
result
(
@
(
switchResult
));
}
else
if
([
@"seek"
isEqualToString
:
call
.
method
])
{
result
(
FlutterMethodNotImplemented
);
}
else
if
([
@"setAppID"
isEqualToString
:
call
.
method
])
{
...
...
@@ -327,6 +346,13 @@ static const int uninitialized = -1;
}
else
if
([
@"resumeLive"
isEqualToString
:
call
.
method
])
{
int
r
=
[
self
resumeLive
];
result
(
@
(
r
));
}
else
if
([
@"enableHardwareDecode"
isEqualToString
:
call
.
method
])
{
BOOL
enable
=
[
args
[
@"enable"
]
boolValue
];
int
r
=
[
self
enableHardwareDecode
:
enable
];
result
(
@
(
r
));
}
else
if
([
@"setConfig"
isEqualToString
:
call
.
method
]){
[
self
setPlayConfig
:
args
];
result
(
nil
);
}
else
{
result
(
FlutterMethodNotImplemented
);
}
...
...
Flutter/ios/Classes/FTXTransformation.h
浏览文件 @
efb9316d
...
...
@@ -6,6 +6,8 @@ static NSString* cacheFolder = nil;
static
int
maxCacheItems
=
-
1
;
@interface
FTXTransformation
:
NSObject
+
(
TXVodPlayConfig
*
)
transformToConfig
:(
NSDictionary
*
)
map
;
+
(
TXVodPlayConfig
*
)
transformToVodConfig
:(
NSDictionary
*
)
map
;
+
(
TXLivePlayConfig
*
)
transformToLiveConfig
:(
NSDictionary
*
)
map
;
@end
Flutter/ios/Classes/FTXTransformation.m
浏览文件 @
efb9316d
...
...
@@ -4,7 +4,7 @@
@implementation
FTXTransformation
+
(
TXVodPlayConfig
*
)
transformToConfig
:(
NSDictionary
*
)
args
+
(
TXVodPlayConfig
*
)
transformTo
Vod
Config
:(
NSDictionary
*
)
args
{
TXVodPlayConfig
*
playConfig
=
[[
TXVodPlayConfig
alloc
]
init
];
playConfig
.
connectRetryCount
=
[
args
[
@"config"
][
@"connectRetryCount"
]
intValue
];
...
...
@@ -48,6 +48,25 @@
return
playConfig
;
}
+
(
TXLivePlayConfig
*
)
transformToLiveConfig
:(
NSDictionary
*
)
args
{
TXLivePlayConfig
*
playConfig
=
[[
TXLivePlayConfig
alloc
]
init
];
playConfig
.
cacheTime
=
[
args
[
@"config"
][
@"cacheTime"
]
floatValue
];
playConfig
.
maxAutoAdjustCacheTime
=
[
args
[
@"config"
][
@"maxAutoAdjustCacheTime"
]
floatValue
];
playConfig
.
minAutoAdjustCacheTime
=
[
args
[
@"config"
][
@"minAutoAdjustCacheTime"
]
floatValue
];
playConfig
.
videoBlockThreshold
=
[
args
[
@"config"
][
@"videoBlockThreshold"
]
intValue
];
playConfig
.
connectRetryCount
=
[
args
[
@"config"
][
@"connectRetryCount"
]
intValue
];
playConfig
.
connectRetryInterval
=
[
args
[
@"config"
][
@"connectRetryInterval"
]
intValue
];
playConfig
.
bAutoAdjustCacheTime
=
[
args
[
@"config"
][
@"autoAdjustCacheTime"
]
boolValue
];
playConfig
.
enableAEC
=
[
args
[
@"config"
][
@"enableAec"
]
boolValue
];
playConfig
.
enableMessage
=
[
args
[
@"config"
][
@"enableMessage"
]
intValue
];
playConfig
.
enableMetaData
=
[
args
[
@"config"
][
@"enableMetaData"
]
intValue
];
playConfig
.
flvSessionKey
=
[
args
[
@"config"
][
@"flvSessionKey"
]
stringValue
];
return
playConfig
;
}
@end
Flutter/ios/Classes/FTXVodPlayer.m
浏览文件 @
efb9316d
...
...
@@ -583,7 +583,7 @@ BOOL volatile isStop = false;
-
(
void
)
setPlayConfig
:
(
NSDictionary
*
)
args
{
if
(
_txVodPlayer
!=
nil
&&
[
args
[
@"config"
]
isKindOfClass
:[
NSDictionary
class
]])
{
_txVodPlayer
.
config
=
[
FTXTransformation
transformToConfig
:
args
];
_txVodPlayer
.
config
=
[
FTXTransformation
transformTo
Vod
Config
:
args
];
}
}
...
...
Flutter/ios/Classes/SuperPlayerPlugin.m
浏览文件 @
efb9316d
...
...
@@ -187,6 +187,10 @@ SuperPlayerPlugin* instance;
}
else
if
([
@"isDeviceSupportPip"
isEqualToString
:
call
.
method
])
{
BOOL
isSupport
=
[
TXVodPlayer
isSupportPictureInPicture
];
result
([
NSNumber
numberWithBool
:
isSupport
]);
}
else
if
([
@"setGlobalEnv"
isEqualToString
:
call
.
method
])
{
NSString
*
envConfig
=
call
.
arguments
[
@"envConfig"
];
int
setResult
=
[
TXLiveBase
setGlobalEnv
:[
envConfig
UTF8String
]];
result
(
@
(
setResult
));
}
else
{
result
(
FlutterMethodNotImplemented
);
}
...
...
Flutter/lib/Core/provider/txplayer_holder.dart
0 → 100644
浏览文件 @
efb9316d
// Copyright (c) 2022 Tencent. All rights reserved.
part of
SuperPlayer
;
abstract
class
TXModel
{
}
class
TXPlayerHolder
extends
TXModel
{
TXPlayerController
controller
;
TXPlayerHolder
(
this
.
controller
);
void
updateController
(
TXPlayerController
playerController
)
{
controller
=
playerController
;
}
}
Flutter/lib/Core/superplayer_plugin.dart
浏览文件 @
efb9316d
...
...
@@ -19,6 +19,7 @@ class SuperPlayerPlugin {
/// 原生交互,通用事件监听,来自插件的事件,例如 声音变化等事件
Stream
<
Map
<
dynamic
,
dynamic
>>
get
onEventBroadcast
=>
_eventStreamController
.
stream
;
/// 原生交互,通用事件监听,来自原生容器的事件,例如 PIP事件、activity/controller 生命周期变化
Stream
<
Map
<
dynamic
,
dynamic
>>
get
onExtraEventBroadcast
=>
_eventPipStreamController
.
stream
;
...
...
@@ -153,4 +154,16 @@ class SuperPlayerPlugin {
static
Future
<
String
?>
getLiteAVSDKVersion
()
async
{
return
await
_channel
.
invokeMethod
(
'getLiteAVSDKVersion'
);
}
///
/// 设置 liteav SDK 接入的环境。
/// 腾讯云在全球各地区部署的环境,按照各地区政策法规要求,需要接入不同地区接入点。
///
/// @param envConfig 需要接入的环境,SDK 默认接入的环境是:默认正式环境。
/// @return 0:成功;其他:错误
/// @note 目标市场为中国大陆的客户请不要调用此接口,如果目标市场为海外用户,请通过技术支持联系我们,了解 env_config 的配置方法,以确保 App 遵守 GDPR 标准。
///
static
Future
<
int
>
setGlobalEnv
(
String
envConfig
)
async
{
return
await
_channel
.
invokeMethod
(
"setGlobalEnv"
,
{
"envConfig"
:
envConfig
});
}
}
Flutter/lib/Core/txliveplayer_config.dart
0 → 100644
浏览文件 @
efb9316d
// Copyright (c) 2022 Tencent. All rights reserved.
part of
SuperPlayer
;
/// TXLivePlayer config
class
FTXLivePlayConfig
{
// 播放器缓存时间,单位秒,取值需要大于0,默认值:5
double
cacheTime
=
5.0
;
// 播放器缓存自动调整的最大时间,单位秒,取值需要大于0,默认值:5
double
maxAutoAdjustCacheTime
=
5.0
;
// 播放器缓存自动调整的最小时间,单位秒,取值需要大于0,默认值为1
double
minAutoAdjustCacheTime
=
1.0
;
// 播放器视频卡顿报警阈值,单位毫秒,只有渲染间隔超过这个阈值的卡顿才会有 PLAY_WARNING_VIDEO_PLAY_LAG 通知
int
videoBlockThreshold
=
800
;
// 播放器遭遇网络连接断开时 SDK 默认重试的次数,取值范围1 - 10,默认值:3。
int
connectRetryCount
=
3
;
// 网络重连的时间间隔,单位秒,取值范围3 - 30,默认值:3。
int
connectRetryInterval
=
3
;
// 是否自动调整播放器缓存时间,默认值:true
// true:启用自动调整,自动调整的最大值和最小值可以分别通过修改 maxCacheTime 和 minCacheTime 来设置
// false:关闭自动调整,采用默认的指定缓存时间(1s),可以通过修改 cacheTime 来调整缓存时间
bool
autoAdjustCacheTime
=
true
;
// 是否开启回声消除, 默认值为 false
bool
enableAec
=
false
;
// 是否开启消息通道, 默认值为 true
bool
enableMessage
=
true
;
// 是否开启 MetaData 数据回调,默认值为 NO。
// true:SDK 通过 EVT_PLAY_GET_METADATA 消息抛出视频流的 MetaData 数据;
// false:SDK 不抛出视频流的 MetaData 数据。
// 标准直播流都会在最开始的阶段有一个 MetaData 数据头,该数据头支持定制。
// 您可以通过 TXLivePushConfig 中的 metaData 属性设置一些自定义数据,再通过 TXLivePlayListener 中的
// onPlayEvent(EVT_PLAY_GET_METADATA) 消息接收到这些数据。
//【特别说明】每条音视频流中只能设置一个 MetaData 数据头,除非断网重连,否则 TXLivePlayer 的
// EVT_PLAY_GET_METADATA 消息也只会收到一次。
bool
enableMetaData
=
false
;
// 是否开启 HTTP 头信息回调,默认值为 “”
// HTTP
// 响应头中除了“content-length”、“content-type”等标准字段,不同云服务商还可能会添加一些非标准字段。
// 比如腾讯云会在直播 CDN 的 HTTP-FLV 格式的直播流中增加 “X-Tlive-SpanId”
// 响应头,并在其中设置一个随机字符串,用来唯一标识一次直播。
//
// 如果您在使用腾讯云的直播 CDN,可以设置 flvSessionKey 为 “X-Tlive-SpanId”,SDK 会在 HTTP
// 响应头里解析这个字段, 并通过 TXLivePlayListener 中的 onPlayEvent(EVT_PLAY_GET_FLVSESSIONKEY)
// 事件通知给您的 App。
//
//【特别说明】每条音视频流中只能解析一个 flvSessionKey,除非断网重连,否则
// EVT_PLAY_GET_FLVSESSIONKEY 只会抛送一次。
String
flvSessionKey
=
""
;
Map
<
String
,
dynamic
>
toJson
()
{
Map
<
String
,
dynamic
>
json
=
{};
json
[
"cacheTime"
]
=
cacheTime
;
json
[
"maxAutoAdjustCacheTime"
]
=
maxAutoAdjustCacheTime
;
json
[
"minAutoAdjustCacheTime"
]
=
minAutoAdjustCacheTime
;
json
[
"videoBlockThreshold"
]
=
videoBlockThreshold
;
json
[
"connectRetryCount"
]
=
connectRetryCount
;
json
[
"connectRetryInterval"
]
=
connectRetryInterval
;
json
[
"autoAdjustCacheTime"
]
=
autoAdjustCacheTime
;
json
[
"enableAec"
]
=
enableAec
;
json
[
"enableMessage"
]
=
enableMessage
;
json
[
"enableMetaData"
]
=
enableMetaData
;
json
[
"flvSessionKey"
]
=
flvSessionKey
;
return
json
;
}
}
\ No newline at end of file
Flutter/lib/Core/txliveplayer_controller.dart
浏览文件 @
efb9316d
...
...
@@ -16,18 +16,17 @@ class TXLivePlayerController extends ChangeNotifier implements ValueListenable<T
StreamSubscription
?
_eventSubscription
;
StreamSubscription
?
_netSubscription
;
final
StreamController
<
TXPlayerState
?>
_stateStreamController
=
StreamController
.
broadcast
();
final
StreamController
<
TXPlayerState
?>
_stateStreamController
=
StreamController
.
broadcast
();
final
StreamController
<
Map
<
dynamic
,
dynamic
>
>
_eventStreamController
=
StreamController
.
broadcast
();
final
StreamController
<
Map
<
dynamic
,
dynamic
>>
_eventStreamController
=
StreamController
.
broadcast
();
final
StreamController
<
Map
<
dynamic
,
dynamic
>
>
_netStatusStreamController
=
StreamController
.
broadcast
();
final
StreamController
<
Map
<
dynamic
,
dynamic
>>
_netStatusStreamController
=
StreamController
.
broadcast
();
Stream
<
TXPlayerState
?>
get
onPlayerState
=>
_stateStreamController
.
stream
;
Stream
<
Map
<
dynamic
,
dynamic
>
>
get
onPlayerEventBroadcast
=>
_eventStreamController
.
stream
;
Stream
<
Map
<
dynamic
,
dynamic
>
>
get
onPlayerNetStatusBroadcast
=>
_netStatusStreamController
.
stream
;
Stream
<
Map
<
dynamic
,
dynamic
>>
get
onPlayerEventBroadcast
=>
_eventStreamController
.
stream
;
Stream
<
Map
<
dynamic
,
dynamic
>>
get
onPlayerNetStatusBroadcast
=>
_netStatusStreamController
.
stream
;
TXLivePlayerController
()
:
_initPlayer
=
Completer
(),
...
...
@@ -40,14 +39,12 @@ class TXLivePlayerController extends ChangeNotifier implements ValueListenable<T
Future
<
void
>
_create
()
async
{
_playerId
=
await
SuperPlayerPlugin
.
createLivePlayer
();
_channel
=
MethodChannel
(
"cloud.tencent.com/txliveplayer/
$_playerId
"
);
_eventSubscription
=
EventChannel
(
"cloud.tencent.com/txliveplayer/event/
$_playerId
"
)
.
receiveBroadcastStream
(
"event"
)
.
listen
(
_eventHandler
,
onError:
_errorHandler
);
_netSubscription
=
EventChannel
(
"cloud.tencent.com/txliveplayer/net/
$_playerId
"
)
.
receiveBroadcastStream
(
"net"
)
.
listen
(
_netHandler
,
onError:
_errorHandler
);
_eventSubscription
=
EventChannel
(
"cloud.tencent.com/txliveplayer/event/
$_playerId
"
)
.
receiveBroadcastStream
(
"event"
)
.
listen
(
_eventHandler
,
onError:
_errorHandler
);
_netSubscription
=
EventChannel
(
"cloud.tencent.com/txliveplayer/net/
$_playerId
"
)
.
receiveBroadcastStream
(
"net"
)
.
listen
(
_netHandler
,
onError:
_errorHandler
);
_initPlayer
.
complete
(
_playerId
);
}
...
...
@@ -56,21 +53,21 @@ class TXLivePlayerController extends ChangeNotifier implements ValueListenable<T
/// see:https://cloud.tencent.com/document/product/454/7886#.E6.92.AD.E6.94.BE.E4.BA.8B.E4.BB.B6
///
_eventHandler
(
event
)
{
if
(
event
==
null
)
return
;
if
(
event
==
null
)
return
;
final
Map
<
dynamic
,
dynamic
>
map
=
event
;
//debugPrint("= event = ${map.toString()}");
switch
(
map
[
"event"
])
{
switch
(
map
[
"event"
])
{
case
TXVodPlayEvent
.
PLAY_EVT_RTMP_STREAM_BEGIN
:
break
;
case
TXVodPlayEvent
.
PLAY_EVT_RCV_FIRST_I_FRAME
:
if
(
_isNeedDisposed
)
return
;
if
(
_state
==
TXPlayerState
.
buffering
)
_changeState
(
TXPlayerState
.
playing
);
if
(
_isNeedDisposed
)
return
;
if
(
_state
==
TXPlayerState
.
buffering
)
_changeState
(
TXPlayerState
.
playing
);
break
;
case
TXVodPlayEvent
.
PLAY_EVT_PLAY_BEGIN
:
if
(
_isNeedDisposed
)
return
;
if
(
_state
==
TXPlayerState
.
buffering
)
_changeState
(
TXPlayerState
.
playing
);
if
(
_isNeedDisposed
)
return
;
if
(
_state
==
TXPlayerState
.
buffering
)
_changeState
(
TXPlayerState
.
playing
);
break
;
case
TXVodPlayEvent
.
PLAY_EVT_PLAY_PROGRESS
:
//EVT_PLAY_PROGRESS
case
TXVodPlayEvent
.
PLAY_EVT_PLAY_PROGRESS
:
//EVT_PLAY_PROGRESS
break
;
case
TXVodPlayEvent
.
PLAY_EVT_PLAY_END
:
_changeState
(
TXPlayerState
.
stopped
);
...
...
@@ -78,32 +75,34 @@ class TXLivePlayerController extends ChangeNotifier implements ValueListenable<T
case
TXVodPlayEvent
.
PLAY_EVT_PLAY_LOADING
:
_changeState
(
TXPlayerState
.
buffering
);
break
;
case
TXVodPlayEvent
.
PLAY_EVT_CHANGE_RESOLUTION
:
//下行视频分辨率改变
case
TXVodPlayEvent
.
PLAY_EVT_CHANGE_RESOLUTION
:
//下行视频分辨率改变
if
(
defaultTargetPlatform
==
TargetPlatform
.
android
)
{
resizeVideoWidth
=
(
event
[
"videoWidth"
]).
toDouble
();
resizeVideoHeight
=
(
event
[
"videoHeight"
]).
toDouble
();
if
(
resizeVideoWidth
!
>
0
&&
resizeVideoHeight
!
>
0
)
{
videoLeft
=
(
event
[
"videoLeft"
]).
toDouble
();
videoTop
=
(
event
[
"videoTop"
]).
toDouble
();
videoRight
=
(
event
[
"videoRight"
]).
toDouble
();
videoBottom
=
(
event
[
"videoBottom"
]).
toDouble
();
double
?
videoWidth
=
(
event
[
"videoWidth"
]);
double
?
videoHeight
=
(
event
[
"videoHeight"
]);
if
((
videoWidth
!=
null
&&
videoWidth
>
0
)
&&
(
videoHeight
!=
null
&&
videoHeight
>
0
))
{
resizeVideoWidth
=
videoWidth
;
resizeVideoHeight
=
videoHeight
;
videoLeft
=
event
[
"videoLeft"
];
videoTop
=
event
[
"videoTop"
];
videoRight
=
event
[
"videoRight"
];
videoBottom
=
event
[
"videoBottom"
];
}
}
break
;
case
TXVodPlayEvent
.
PLAY_EVT_STREAM_SWITCH_SUCC
:
//直播,切流成功(切流可以播放不同画面大小的视频)
case
TXVodPlayEvent
.
PLAY_EVT_STREAM_SWITCH_SUCC
:
//直播,切流成功(切流可以播放不同画面大小的视频)
break
;
case
TXVodPlayEvent
.
PLAY_ERR_NET_DISCONNECT
:
//disconnect
case
TXVodPlayEvent
.
PLAY_ERR_NET_DISCONNECT
:
//disconnect
_changeState
(
TXPlayerState
.
failed
);
break
;
case
TXVodPlayEvent
.
PLAY_WARNING_RECONNECT
:
//reconnect
case
TXVodPlayEvent
.
PLAY_WARNING_RECONNECT
:
//reconnect
break
;
case
TXVodPlayEvent
.
PLAY_WARNING_DNS_FAIL
:
//dnsFail
case
TXVodPlayEvent
.
PLAY_WARNING_DNS_FAIL
:
//dnsFail
break
;
case
TXVodPlayEvent
.
PLAY_WARNING_SEVER_CONN_FAIL
:
//severConnFail
case
TXVodPlayEvent
.
PLAY_WARNING_SEVER_CONN_FAIL
:
//severConnFail
break
;
case
TXVodPlayEvent
.
PLAY_WARNING_SHAKE_FAIL
:
//shakeFail
case
TXVodPlayEvent
.
PLAY_WARNING_SHAKE_FAIL
:
//shakeFail
break
;
case
TXVodPlayEvent
.
PLAY_ERR_STREAM_SWITCH_FAIL
:
//failed
case
TXVodPlayEvent
.
PLAY_ERR_STREAM_SWITCH_FAIL
:
//failed
_changeState
(
TXPlayerState
.
failed
);
break
;
default
:
...
...
@@ -117,12 +116,12 @@ class TXLivePlayerController extends ChangeNotifier implements ValueListenable<T
}
_netHandler
(
event
)
{
if
(
event
==
null
)
return
;
if
(
event
==
null
)
return
;
final
Map
<
dynamic
,
dynamic
>
map
=
event
;
_netStatusStreamController
.
add
(
map
);
}
_changeState
(
TXPlayerState
playerState
){
_changeState
(
TXPlayerState
playerState
)
{
value
=
_value
!.
copyWith
(
state:
playerState
);
_state
=
value
!.
state
;
_stateStreamController
.
add
(
_state
);
...
...
@@ -131,21 +130,29 @@ class TXLivePlayerController extends ChangeNotifier implements ValueListenable<T
///
/// 当设置[LivePlayer] 类型播放器时,需要参数[playType]
/// 参考: [PlayType.LIVE_RTMP] ...
///
@deprecated
Future
<
bool
>
play
(
String
url
,
{
int
?
playType
})
async
{
return
await
startPlay
(
url
,
playType:
playType
);
}
///
/// 当设置[LivePlayer] 类型播放器时,需要参数[playType]
/// 参考: [PlayType.LIVE_RTMP] ...
@override
Future
<
bool
>
startPlay
(
String
url
,
{
int
?
playType
})
async
{
await
_initPlayer
.
future
;
await
_createTexture
.
future
;
_changeState
(
TXPlayerState
.
buffering
);
final
result
=
await
_channel
.
invokeMethod
(
"play"
,
{
"url"
:
url
,
"playType"
:
playType
});
final
result
=
await
_channel
.
invokeMethod
(
"play"
,
{
"url"
:
url
,
"playType"
:
playType
});
return
result
==
0
;
}
/// 播放器初始化,创建共享纹理、初始化播放器
/// @param onlyAudio 是否是纯音频模式
Future
<
void
>
initialize
({
bool
?
onlyAudio
})
async
{
if
(
_isNeedDisposed
)
return
;
@override
Future
<
void
>
initialize
({
bool
?
onlyAudio
})
async
{
if
(
_isNeedDisposed
)
return
;
await
_initPlayer
.
future
;
final
textureId
=
await
_channel
.
invokeMethod
(
"init"
,
{
"onlyAudio"
:
onlyAudio
??
false
,
...
...
@@ -155,60 +162,71 @@ class TXLivePlayerController extends ChangeNotifier implements ValueListenable<T
}
/// 设置是否自动播放
Future
<
void
>
setIsAutoPlay
({
bool
?
isAutoPlay
})
async
{
if
(
_isNeedDisposed
)
return
;
@deprecated
Future
<
void
>
setIsAutoPlay
({
bool
?
isAutoPlay
})
async
{
await
setAutoPlay
(
isAutoPlay:
isAutoPlay
);
}
/// 设置是否自动播放
@override
Future
<
void
>
setAutoPlay
({
bool
?
isAutoPlay
})
async
{
if
(
_isNeedDisposed
)
return
;
await
_initPlayer
.
future
;
await
_channel
.
invokeMethod
(
"setIsAutoPlay"
,
{
"isAutoPlay"
:
isAutoPlay
??
false
});
await
_channel
.
invokeMethod
(
"setIsAutoPlay"
,
{
"isAutoPlay"
:
isAutoPlay
??
false
});
}
/// 停止播放
/// return 是否停止成功
Future
<
bool
>
stop
({
bool
isNeedClear
=
true
})
async
{
if
(
_isNeedDisposed
)
return
false
;
@override
Future
<
bool
>
stop
({
bool
isNeedClear
=
false
})
async
{
if
(
_isNeedDisposed
)
return
false
;
await
_initPlayer
.
future
;
final
result
=
await
_channel
.
invokeMethod
(
"stop"
,
{
"isNeedClear"
:
isNeedClear
});
final
result
=
await
_channel
.
invokeMethod
(
"stop"
,
{
"isNeedClear"
:
isNeedClear
});
_changeState
(
TXPlayerState
.
stopped
);
return
result
==
0
;
}
/// 视频是否处于正在播放中
Future
<
bool
?>
isPlaying
()
async
{
@override
Future
<
bool
>
isPlaying
()
async
{
await
_initPlayer
.
future
;
return
await
_channel
.
invokeMethod
(
"isPlaying"
);
}
/// 视频暂停,必须在播放器开始播放的时候调用
@override
Future
<
void
>
pause
()
async
{
if
(
_isNeedDisposed
)
return
;
if
(
_isNeedDisposed
)
return
;
await
_initPlayer
.
future
;
await
_channel
.
invokeMethod
(
"pause"
);
if
(
_state
!=
TXPlayerState
.
paused
)
_changeState
(
TXPlayerState
.
paused
);
if
(
_state
!=
TXPlayerState
.
paused
)
_changeState
(
TXPlayerState
.
paused
);
}
/// 继续播放,在暂停的时候调用
@override
Future
<
void
>
resume
()
async
{
if
(
_isNeedDisposed
)
return
;
if
(
_isNeedDisposed
)
return
;
await
_initPlayer
.
future
;
await
_channel
.
invokeMethod
(
"resume"
);
if
(
_state
!=
TXPlayerState
.
playing
)
_changeState
(
TXPlayerState
.
playing
);
if
(
_state
!=
TXPlayerState
.
playing
)
_changeState
(
TXPlayerState
.
playing
);
}
/// 设置直播模式,see TXPlayerLiveMode
Future
<
void
>
setLiveMode
(
TXPlayerLiveMode
mode
)
async
{
if
(
_isNeedDisposed
)
return
;
if
(
_isNeedDisposed
)
return
;
await
_initPlayer
.
future
;
await
_channel
.
invokeMethod
(
"setLiveMode"
,
{
"type"
:
mode
.
index
});
}
/// 设置视频声音 0~100
Future
<
void
>
setVolume
(
int
volume
)
async
{
if
(
_isNeedDisposed
)
return
;
if
(
_isNeedDisposed
)
return
;
await
_initPlayer
.
future
;
await
_channel
.
invokeMethod
(
"setVolume"
,
{
"volume"
:
volume
});
}
/// 设置是否静音
@override
Future
<
void
>
setMute
(
bool
mute
)
async
{
if
(
_isNeedDisposed
)
return
;
await
_initPlayer
.
future
;
...
...
@@ -216,23 +234,24 @@ class TXLivePlayerController extends ChangeNotifier implements ValueListenable<T
}
/// 切换播放流
Future
<
void
>
switchStream
(
String
url
)
async
{
if
(
_isNeedDisposed
)
return
;
Future
<
int
>
switchStream
(
String
url
)
async
{
if
(
_isNeedDisposed
)
return
-
1
;
await
_initPlayer
.
future
;
await
_channel
.
invokeMethod
(
"switchStream"
,
{
"url"
:
url
});
return
await
_channel
.
invokeMethod
(
"switchStream"
,
{
"url"
:
url
});
}
/// 将视频播放进度定位到指定的进度进行播放
/// progress 要定位的视频时间,单位 秒
@override
Future
<
void
>
seek
(
double
progress
)
async
{
if
(
_isNeedDisposed
)
return
;
if
(
_isNeedDisposed
)
return
;
await
_initPlayer
.
future
;
await
_channel
.
invokeMethod
(
"seek"
,
{
"progress"
:
progress
});
}
/// 设置appId
Future
<
void
>
setAppID
(
int
appId
)
async
{
if
(
_isNeedDisposed
)
return
;
if
(
_isNeedDisposed
)
return
;
await
_initPlayer
.
future
;
await
_channel
.
invokeMethod
(
"seek"
,
{
"appId"
:
appId
});
}
...
...
@@ -240,24 +259,69 @@ class TXLivePlayerController extends ChangeNotifier implements ValueListenable<T
/// 时移 暂不支持
@deprecated
Future
<
void
>
prepareLiveSeek
(
String
domain
,
int
bizId
)
async
{
if
(
_isNeedDisposed
)
return
;
if
(
_isNeedDisposed
)
return
;
await
_initPlayer
.
future
;
await
_channel
.
invokeMethod
(
"prepareLiveSeek"
,
{
"domain"
:
domain
,
"bizId"
:
bizId
});
await
_channel
.
invokeMethod
(
"prepareLiveSeek"
,
{
"domain"
:
domain
,
"bizId"
:
bizId
});
}
/// 停止时移播放,返回直播
Future
<
int
>
resumeLive
()
async
{
if
(
_isNeedDisposed
)
return
0
;
if
(
_isNeedDisposed
)
return
0
;
await
_initPlayer
.
future
;
return
await
_channel
.
invokeMethod
(
"resumeLive"
);
}
/// 设置播放速率,暂不支持
@deprecated
@override
Future
<
void
>
setRate
(
double
rate
)
async
{
if
(
_isNeedDisposed
)
return
;
if
(
_isNeedDisposed
)
return
;
await
_initPlayer
.
future
;
await
_channel
.
invokeMethod
(
"setRate"
,
{
"rate"
:
rate
});
}
/// 设置播放器配置
/// config @see [FTXLivePlayConfig]
Future
<
void
>
setConfig
(
FTXLivePlayConfig
config
)
async
{
if
(
_isNeedDisposed
)
return
;
await
_initPlayer
.
future
;
await
_channel
.
invokeMethod
(
"setConfig"
,
{
"config"
:
config
.
toJson
()});
}
/// 开启/关闭硬件编码
@override
Future
<
bool
>
enableHardwareDecode
(
bool
enable
)
async
{
if
(
_isNeedDisposed
)
return
false
;
await
_initPlayer
.
future
;
await
_channel
.
invokeMethod
(
"setRate"
,
{
"rate"
:
rate
});
return
await
_channel
.
invokeMethod
(
"enableHardwareDecode"
,
{
"enable"
:
enable
});
}
/// 进入画中画模式,进入画中画模式,需要适配画中画模式的界面,安卓只支持7.0以上机型
/// <h1>
/// 由于android系统限制,传递的图标大小不得超过1M,否则无法显示
/// </h1>
/// @param backIcon playIcon pauseIcon forwardIcon 为播放后退、播放、暂停、前进的图标,仅适用于android,如果赋值的话,将会使用传递的图标,否则
/// 使用系统默认图标,只支持flutter本地资源图片,传递的时候,与flutter使用图片资源一致,例如: images/back_icon.png
@override
Future
<
int
>
enterPictureInPictureMode
(
{
String
?
backIconForAndroid
,
String
?
playIconForAndroid
,
String
?
pauseIconForAndroid
,
String
?
forwardIconForAndroid
})
async
{
if
(
_isNeedDisposed
)
return
-
1
;
await
_initPlayer
.
future
;
if
(
Platform
.
isAndroid
)
{
return
await
_channel
.
invokeMethod
(
"enterPictureInPictureMode"
,
{
"backIcon"
:
backIconForAndroid
,
"playIcon"
:
playIconForAndroid
,
"pauseIcon"
:
pauseIconForAndroid
,
"forwardIcon"
:
forwardIconForAndroid
});
}
else
if
(
Platform
.
isIOS
)
{
return
-
1
;
}
else
{
return
-
1
;
}
}
/// 释放播放器资源占用
...
...
@@ -267,12 +331,15 @@ class TXLivePlayerController extends ChangeNotifier implements ValueListenable<T
await
SuperPlayerPlugin
.
releasePlayer
(
_playerId
);
}
/// 释放controller
@override
void
dispose
()
async
{
void
dispose
()
async
{
_isNeedDisposed
=
true
;
if
(!
_isDisposed
)
{
if
(!
_isDisposed
)
{
await
_eventSubscription
!.
cancel
();
_eventSubscription
=
null
;
await
_netSubscription
!.
cancel
();
_netSubscription
=
null
;
await
_release
();
_changeState
(
TXPlayerState
.
disposed
);
...
...
@@ -289,7 +356,7 @@ class TXLivePlayerController extends ChangeNotifier implements ValueListenable<T
get
value
=>
_value
;
set
value
(
TXPlayerValue
?
val
){
set
value
(
TXPlayerValue
?
val
)
{
if
(
_value
==
val
)
return
;
_value
=
val
;
notifyListeners
();
...
...
@@ -297,7 +364,7 @@ class TXLivePlayerController extends ChangeNotifier implements ValueListenable<T
@override
// TODO: implement textureId
Future
<
int
>
get
textureId
async
{
Future
<
int
>
get
textureId
async
{
return
_createTexture
.
future
;
}
...
...
@@ -307,5 +374,4 @@ class TXLivePlayerController extends ChangeNotifier implements ValueListenable<T
double
?
videoTop
=
0
;
double
?
videoRight
=
0
;
double
?
videoBottom
=
0
;
}
\ No newline at end of file
}
Flutter/lib/Core/txplayer_controller.dart
浏览文件 @
efb9316d
...
...
@@ -9,4 +9,18 @@ abstract class TXPlayerController {
double
?
get
videoTop
;
double
?
get
videoRight
;
double
?
get
videoBottom
;
Future
<
bool
>
startPlay
(
String
url
);
Future
<
void
>
initialize
({
bool
?
onlyAudio
});
Future
<
void
>
setAutoPlay
({
bool
?
isAutoPlay
});
Future
<
bool
>
stop
({
bool
isNeedClear
=
false
});
Future
<
bool
>
isPlaying
();
Future
<
void
>
pause
();
Future
<
void
>
resume
();
Future
<
void
>
setMute
(
bool
mute
);
Future
<
void
>
seek
(
double
progress
);
Future
<
void
>
setRate
(
double
rate
);
Future
<
bool
>
enableHardwareDecode
(
bool
enable
);
Future
<
int
>
enterPictureInPictureMode
(
{
String
?
backIconForAndroid
,
String
?
playIconForAndroid
,
String
?
pauseIconForAndroid
,
String
?
forwardIconForAndroid
});
}
\ No newline at end of file
Flutter/lib/Core/txplayer_define.dart
浏览文件 @
efb9316d
...
...
@@ -116,7 +116,7 @@ abstract class TXVodPlayEvent {
static
const
ERROR_IOS_PIP_PLAYER_NOT_EXIST
=
-
109
;
// pip 错误,播放器对象不存在 only support iOS
static
const
ERROR_IOS_PIP_IS_RUNNING
=
-
110
;
// pip 错误,PIP功能已经运行 only support iOS
static
const
ERROR_IOS_PIP_NOT_RUNNING
=
-
111
;
// pip 错误,PIP功能没有启动 only support iOS
static
const
ERROR_PIP_CAN_NOT_ENTER
=
-
120
;
// pip 错误,当前不能进入pip模式,例如正处于全屏模式下
/// 视频下载相关事件
static
const
EVENT_PREDOWNLOAD_ON_COMPLETE
=
200
;
// 视频预下载完成
...
...
Flutter/lib/Core/txplayer_widget.dart
浏览文件 @
efb9316d
...
...
@@ -3,10 +3,9 @@ part of SuperPlayer;
class
TXPlayerVideo
extends
StatefulWidget
{
final
TXPlayerController
controller
;
final
Stream
<
TXPlayerHolder
>?
playerStream
;
TXPlayerVideo
({
required
this
.
controller
,
Key
?
key
})
:
super
(
key:
key
)
{
assert
(
controller
!=
null
);
}
TXPlayerVideo
({
required
this
.
controller
,
this
.
playerStream
});
@override
TXPlayerVideoState
createState
()
=>
TXPlayerVideoState
();
...
...
@@ -16,52 +15,82 @@ class TXPlayerVideoState extends State<TXPlayerVideo> {
static
const
TAG
=
"TXPlayerVideo"
;
int
_textureId
=
-
1
;
StreamSubscription
?
streamSubscription
;
late
TXPlayerController
controller
;
@override
void
initState
()
{
super
.
initState
();
controller
=
widget
.
controller
;
_checkStreamListen
();
_resetControllerLink
();
}
void
_checkStreamListen
()
{
if
(
null
!=
streamSubscription
)
{
streamSubscription
!.
cancel
();
}
streamSubscription
=
widget
.
playerStream
?.
listen
((
event
)
{
controller
=
event
.
controller
;
_resetControllerLink
();
});
}
widget
.
controller
.
textureId
.
then
((
newTextureId
)
{
if
(
_textureId
!=
newTextureId
)
{
void
_resetControllerLink
()
async
{
int
remainTextureId
=
await
controller
.
textureId
;
if
(
remainTextureId
>=
0
)
{
if
(
remainTextureId
!=
_textureId
)
{
setState
(()
{
LogUtils
.
d
(
TAG
,
"_textureId =
$
new
TextureId
"
);
_textureId
=
new
TextureId
;
LogUtils
.
d
(
TAG
,
"_textureId =
$
remain
TextureId
"
);
_textureId
=
remain
TextureId
;
});
}
});
}
else
{
setState
(()
{
_textureId
=
-
1
;
});
controller
.
textureId
.
then
((
newTextureId
)
{
if
(
_textureId
!=
newTextureId
)
{
setState
(()
{
LogUtils
.
d
(
TAG
,
"_textureId =
$newTextureId
"
);
_textureId
=
newTextureId
;
});
}
});
}
}
@override
Widget
build
(
BuildContext
context
)
{
if
((
defaultTargetPlatform
==
TargetPlatform
.
android
)
&&
(
widget
.
controller
.
resizeVideoHeight
!
>
0
&&
widget
.
controller
.
resizeVideoWidth
!
>
0
))
{
(
controller
.
resizeVideoHeight
!
>
0
&&
controller
.
resizeVideoWidth
!
>
0
))
{
return
_textureId
==
-
1
?
Container
()
:
LayoutBuilder
(
builder:
(
context
,
constrains
)
{
var
viewWidth
=
constrains
.
maxWidth
;
var
viewHeight
=
constrains
.
maxHeight
;
var
videoWidth
=
widget
.
controller
.
resizeVideoWidth
!;
var
videoHeight
=
widget
.
controller
.
resizeVideoHeight
!;
var
viewWidth
=
constrains
.
maxWidth
;
var
viewHeight
=
constrains
.
maxHeight
;
var
videoWidth
=
controller
.
resizeVideoWidth
!;
var
videoHeight
=
widget
.
controller
.
resizeVideoHeight
!;
double
left
=
widget
.
controller
.
videoLeft
!
*
viewWidth
/
videoWidth
;
double
top
=
widget
.
controller
.
videoTop
!
*
viewHeight
/
videoHeight
;
double
right
=
widget
.
controller
.
videoRight
!
*
viewWidth
/
videoWidth
;
double
bottom
=
widget
.
controller
.
videoBottom
!
*
viewHeight
/
videoHeight
;
return
Stack
(
children:
[
Positioned
(
top:
top
,
left:
left
,
right:
right
,
bottom:
bottom
,
child:
Texture
(
textureId:
_textureId
)
)
],
);
});
double
left
=
controller
.
videoLeft
!
*
viewWidth
/
videoWidth
;
double
top
=
controller
.
videoTop
!
*
viewHeight
/
videoHeight
;
double
right
=
controller
.
videoRight
!
*
viewWidth
/
videoWidth
;
double
bottom
=
controller
.
videoBottom
!
*
viewHeight
/
videoHeight
;
return
Stack
(
children:
[
Positioned
(
top:
top
,
left:
left
,
right:
right
,
bottom:
bottom
,
child:
Texture
(
textureId:
_textureId
))
],
);
});
}
else
{
return
_textureId
==
-
1
?
Container
()
:
Texture
(
textureId:
_textureId
);
return
_textureId
==
-
1
?
Container
()
:
Texture
(
textureId:
_textureId
);
}
}
@override
void
dispose
()
{
streamSubscription
?.
cancel
();
super
.
dispose
();
}
}
Flutter/lib/Core/txvodplayer_controller.dart
浏览文件 @
efb9316d
...
...
@@ -17,9 +17,7 @@ class TXVodPlayerController extends ChangeNotifier implements ValueListenable<TX
StreamSubscription
?
_netSubscription
;
final
StreamController
<
TXPlayerState
?>
_stateStreamController
=
StreamController
.
broadcast
();
final
StreamController
<
Map
<
dynamic
,
dynamic
>>
_eventStreamController
=
StreamController
.
broadcast
();
final
StreamController
<
Map
<
dynamic
,
dynamic
>>
_netStatusStreamController
=
StreamController
.
broadcast
();
/// 播放状态监听,@see TXPlayerState
...
...
@@ -137,6 +135,7 @@ class TXVodPlayerController extends ChangeNotifier implements ValueListenable<TX
/// 通过url开始播放视频
/// @param url : 视频播放地址
/// return 是否播放成功
@override
Future
<
bool
>
startPlay
(
String
url
)
async
{
await
_initPlayer
.
future
;
await
_createTexture
.
future
;
...
...
@@ -165,6 +164,7 @@ class TXVodPlayerController extends ChangeNotifier implements ValueListenable<TX
/// 播放器初始化,创建共享纹理、初始化播放器
/// @param onlyAudio 是否是纯音频模式
@override
Future
<
void
>
initialize
({
bool
?
onlyAudio
})
async
{
if
(
_isNeedDisposed
)
return
;
await
_initPlayer
.
future
;
...
...
@@ -176,6 +176,7 @@ class TXVodPlayerController extends ChangeNotifier implements ValueListenable<TX
}
/// 设置是否自动播放
@override
Future
<
void
>
setAutoPlay
({
bool
?
isAutoPlay
})
async
{
if
(
_isNeedDisposed
)
return
;
await
_initPlayer
.
future
;
...
...
@@ -184,7 +185,8 @@ class TXVodPlayerController extends ChangeNotifier implements ValueListenable<TX
/// 停止播放
/// return 是否停止成功
Future
<
bool
>
stop
({
bool
isNeedClear
=
true
})
async
{
@override
Future
<
bool
>
stop
({
bool
isNeedClear
=
false
})
async
{
if
(
_isNeedDisposed
)
return
false
;
await
_initPlayer
.
future
;
final
result
=
await
_channel
.
invokeMethod
(
"stop"
,
{
"isNeedClear"
:
isNeedClear
});
...
...
@@ -193,12 +195,14 @@ class TXVodPlayerController extends ChangeNotifier implements ValueListenable<TX
}
/// 视频是否处于正在播放中
Future
<
bool
?>
isPlaying
()
async
{
@override
Future
<
bool
>
isPlaying
()
async
{
await
_initPlayer
.
future
;
return
await
_channel
.
invokeMethod
(
"isPlaying"
);
}
/// 视频暂停,必须在播放器开始播放的时候调用
@override
Future
<
void
>
pause
()
async
{
if
(
_isNeedDisposed
)
return
;
await
_initPlayer
.
future
;
...
...
@@ -207,6 +211,7 @@ class TXVodPlayerController extends ChangeNotifier implements ValueListenable<TX
}
/// 继续播放,在暂停的时候调用
@override
Future
<
void
>
resume
()
async
{
if
(
_isNeedDisposed
)
return
;
await
_initPlayer
.
future
;
...
...
@@ -214,6 +219,7 @@ class TXVodPlayerController extends ChangeNotifier implements ValueListenable<TX
}
/// 设置是否静音
@override
Future
<
void
>
setMute
(
bool
mute
)
async
{
if
(
_isNeedDisposed
)
return
;
await
_initPlayer
.
future
;
...
...
@@ -229,6 +235,7 @@ class TXVodPlayerController extends ChangeNotifier implements ValueListenable<TX
/// 将视频播放进度定位到指定的进度进行播放
/// progress 要定位的视频时间,单位 秒
@override
Future
<
void
>
seek
(
double
progress
)
async
{
if
(
_isNeedDisposed
)
return
;
await
_initPlayer
.
future
;
...
...
@@ -236,6 +243,7 @@ class TXVodPlayerController extends ChangeNotifier implements ValueListenable<TX
}
/// 设置播放速率,默认速率 1
@override
Future
<
void
>
setRate
(
double
rate
)
async
{
if
(
_isNeedDisposed
)
return
;
await
_initPlayer
.
future
;
...
...
@@ -294,7 +302,7 @@ class TXVodPlayerController extends ChangeNotifier implements ValueListenable<TX
}
/// 设置播放器配置
/// config @see
FTXVodPlayConfig
/// config @see
[FTXVodPlayConfig]
Future
<
void
>
setConfig
(
FTXVodPlayConfig
config
)
async
{
if
(
_isNeedDisposed
)
return
;
await
_initPlayer
.
future
;
...
...
@@ -351,6 +359,7 @@ class TXVodPlayerController extends ChangeNotifier implements ValueListenable<TX
}
/// 开启/关闭硬件编码
@override
Future
<
bool
>
enableHardwareDecode
(
bool
enable
)
async
{
if
(
_isNeedDisposed
)
return
false
;
await
_initPlayer
.
future
;
...
...
@@ -363,12 +372,26 @@ class TXVodPlayerController extends ChangeNotifier implements ValueListenable<TX
/// </h1>
/// @param backIcon playIcon pauseIcon forwardIcon 为播放后退、播放、暂停、前进的图标,如果赋值的话,将会使用传递的图标,否则
/// 使用系统默认图标,只支持flutter本地资源图片,传递的时候,与flutter使用图片资源一致,例如: images/back_icon.png
@override
Future
<
int
>
enterPictureInPictureMode
(
{
String
?
backIcon
,
String
?
playIcon
,
String
?
pauseIcon
,
String
?
forwardIcon
})
async
{
{
String
?
backIconForAndroid
,
String
?
playIconForAndroid
,
String
?
pauseIconForAndroid
,
String
?
forwardIconForAndroid
})
async
{
if
(
_isNeedDisposed
)
return
-
1
;
await
_initPlayer
.
future
;
return
await
_channel
.
invokeMethod
(
"enterPictureInPictureMode"
,
{
"backIcon"
:
backIcon
,
"playIcon"
:
playIcon
,
"pauseIcon"
:
pauseIcon
,
"forwardIcon"
:
forwardIcon
});
if
(
Platform
.
isAndroid
)
{
return
await
_channel
.
invokeMethod
(
"enterPictureInPictureMode"
,
{
"backIcon"
:
backIconForAndroid
,
"playIcon"
:
playIconForAndroid
,
"pauseIcon"
:
pauseIconForAndroid
,
"forwardIcon"
:
forwardIconForAndroid
});
}
else
if
(
Platform
.
isIOS
)
{
return
await
_channel
.
invokeMethod
(
"enterPictureInPictureMode"
);
}
else
{
return
-
1
;
}
}
/// 获取总时长
...
...
@@ -378,13 +401,15 @@ class TXVodPlayerController extends ChangeNotifier implements ValueListenable<TX
return
await
_channel
.
invokeMethod
(
"getDuration"
);
}
///
废弃
controller
///
释放
controller
@override
void
dispose
()
async
{
_isNeedDisposed
=
true
;
if
(!
_isDisposed
)
{
await
_eventSubscription
!.
cancel
();
_eventSubscription
=
null
;
await
_netSubscription
!.
cancel
();
_netSubscription
=
null
;
await
_release
();
_changeState
(
TXPlayerState
.
disposed
);
...
...
@@ -398,8 +423,6 @@ class TXVodPlayerController extends ChangeNotifier implements ValueListenable<TX
}
@override
// TODO: implement value
get
value
=>
_value
;
set
value
(
TXPlayerValue
?
val
)
{
...
...
Flutter/lib/super_player.dart
浏览文件 @
efb9316d
...
...
@@ -3,6 +3,7 @@ library SuperPlayer;
import
'dart:async'
;
import
'dart:core'
;
import
'dart:io'
;
import
'dart:math'
;
import
'package:flutter/cupertino.dart'
;
...
...
@@ -19,4 +20,6 @@ part 'Core/txplayer_widget.dart';
part
'Core/txvodplayer_config.dart'
;
part
'Core/txvodplayer_controller.dart'
;
part
'Core/txvoddownload_controller.dart'
;
part
'Core/tools/common_utils.dart'
;
\ No newline at end of file
part
'Core/txliveplayer_config.dart'
;
part
'Core/tools/common_utils.dart'
;
part
'Core/provider/txplayer_holder.dart'
;
\ No newline at end of file
Flutter/pubspec.yaml
浏览文件 @
efb9316d
name
:
super_player
description
:
player plugin.
version
:
1.0.
3
version
:
1.0.
5
author
:
homepage
:
environment
:
sdk
:
'
>=2.12.0
<3.0.0'
flutter
:
"
>=
1.2
0.0"
flutter
:
"
>=
2.
0.0"
dependencies
:
flutter
:
...
...
编写
预览
Markdown
格式
0%
重试
或
添加新文件
添加附件
取消
您添加了
0
人
到此讨论。请谨慎行事。
请先完成此评论的编辑!
取消
请
注册
或者
登录
后发表评论