提交 efb9316d authored 作者: kongdywang's avatar kongdywang

SuperPlayer support live for Android&IOS platform

上级 2a99865e
...@@ -47,6 +47,8 @@ ...@@ -47,6 +47,8 @@
### `pubspec.yaml`配置 ### `pubspec.yaml`配置
**推荐flutter sdk 版本 3.0.0 及以上**
集成LiteAVSDK_Player版本,默认情况下也是集成此版本。在`pubspec.yaml`中增加配置 集成LiteAVSDK_Player版本,默认情况下也是集成此版本。在`pubspec.yaml`中增加配置
```yaml ```yaml
......
...@@ -205,7 +205,9 @@ public class FTXPIPManager { ...@@ -205,7 +205,9 @@ public class FTXPIPManager {
return; return;
} }
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) { if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
List<RemoteAction> actions = new ArrayList<>();
// play back // play back
if(params.mIsNeedPlayBack) {
Bundle backData = new Bundle(); Bundle backData = new Bundle();
backData.putInt(FTXEvent.EXTRA_NAME_PLAY_OP, FTXEvent.EXTRA_PIP_PLAY_BACK); backData.putInt(FTXEvent.EXTRA_NAME_PLAY_OP, FTXEvent.EXTRA_PIP_PLAY_BACK);
backData.putInt(FTXEvent.EXTRA_NAME_PLAYER_ID, params.mCurrentPlayerId); backData.putInt(FTXEvent.EXTRA_NAME_PLAYER_ID, params.mCurrentPlayerId);
...@@ -213,8 +215,11 @@ public class FTXPIPManager { ...@@ -213,8 +215,11 @@ public class FTXPIPManager {
PendingIntent preIntent = PendingIntent.getBroadcast(mActivity, FTXEvent.EXTRA_PIP_PLAY_BACK, backIntent, PendingIntent preIntent = PendingIntent.getBroadcast(mActivity, FTXEvent.EXTRA_PIP_PLAY_BACK, backIntent,
PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE); PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
RemoteAction preAction = new RemoteAction(getBackIcon(params), "skipPre", "skip pre", preIntent); RemoteAction preAction = new RemoteAction(getBackIcon(params), "skipPre", "skip pre", preIntent);
actions.add(preAction);
}
// resume or pause // resume or pause
if(params.mIsNeedPlayControl) {
Bundle playOrPauseData = new Bundle(); Bundle playOrPauseData = new Bundle();
playOrPauseData.putInt(FTXEvent.EXTRA_NAME_PLAYER_ID, params.mCurrentPlayerId); playOrPauseData.putInt(FTXEvent.EXTRA_NAME_PLAYER_ID, params.mCurrentPlayerId);
playOrPauseData.putInt(FTXEvent.EXTRA_NAME_PLAY_OP, FTXEvent.EXTRA_PIP_PLAY_RESUME_OR_PAUSE); playOrPauseData.putInt(FTXEvent.EXTRA_NAME_PLAY_OP, FTXEvent.EXTRA_PIP_PLAY_RESUME_OR_PAUSE);
...@@ -224,8 +229,11 @@ public class FTXPIPManager { ...@@ -224,8 +229,11 @@ public class FTXPIPManager {
PendingIntent playIntent = PendingIntent.getBroadcast(mActivity, FTXEvent.EXTRA_PIP_PLAY_RESUME_OR_PAUSE, PendingIntent playIntent = PendingIntent.getBroadcast(mActivity, FTXEvent.EXTRA_PIP_PLAY_RESUME_OR_PAUSE,
playOrPauseIntent, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE); playOrPauseIntent, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
RemoteAction playOrPauseAction = new RemoteAction(playIcon, "playOrPause", "play Or Pause", playIntent); RemoteAction playOrPauseAction = new RemoteAction(playIcon, "playOrPause", "play Or Pause", playIntent);
actions.add(playOrPauseAction);
}
// forward // forward
if(params.mIsNeedPlayForward) {
Bundle forwardData = new Bundle(); Bundle forwardData = new Bundle();
forwardData.putInt(FTXEvent.EXTRA_NAME_PLAY_OP, FTXEvent.EXTRA_PIP_PLAY_FORWARD); forwardData.putInt(FTXEvent.EXTRA_NAME_PLAY_OP, FTXEvent.EXTRA_PIP_PLAY_FORWARD);
forwardData.putInt(FTXEvent.EXTRA_NAME_PLAYER_ID, params.mCurrentPlayerId); forwardData.putInt(FTXEvent.EXTRA_NAME_PLAYER_ID, params.mCurrentPlayerId);
...@@ -234,11 +242,8 @@ public class FTXPIPManager { ...@@ -234,11 +242,8 @@ public class FTXPIPManager {
forwardIntent, forwardIntent,
PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE); PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
RemoteAction nextAction = new RemoteAction(getForwardIcon(params), "skipNext", "skip next", nextIntent); RemoteAction nextAction = new RemoteAction(getForwardIcon(params), "skipNext", "skip next", nextIntent);
List<RemoteAction> actions = new ArrayList<>();
actions.add(preAction);
actions.add(playOrPauseAction);
actions.add(nextAction); actions.add(nextAction);
}
params.mPipParams.setActions(actions); params.mPipParams.setActions(actions);
mActivity.setPictureInPictureParams(params.mPipParams.build()); mActivity.setPictureInPictureParams(params.mPipParams.build());
...@@ -285,6 +290,9 @@ public class FTXPIPManager { ...@@ -285,6 +290,9 @@ public class FTXPIPManager {
String mPlayForwardAssetPath; String mPlayForwardAssetPath;
int mCurrentPlayerId; int mCurrentPlayerId;
protected PictureInPictureParams.Builder mPipParams; protected PictureInPictureParams.Builder mPipParams;
private boolean mIsNeedPlayBack = true;
private boolean mIsNeedPlayForward = true;
private boolean mIsNeedPlayControl = true;
/** /**
* @param mPlayBackAssetPath 回退按钮图片资源路径,传空则使用系统默认图标 * @param mPlayBackAssetPath 回退按钮图片资源路径,传空则使用系统默认图标
...@@ -295,11 +303,21 @@ public class FTXPIPManager { ...@@ -295,11 +303,21 @@ public class FTXPIPManager {
*/ */
public PipParams(String mPlayBackAssetPath, String mPlayResumeAssetPath, String mPlayPauseAssetPath, public PipParams(String mPlayBackAssetPath, String mPlayResumeAssetPath, String mPlayPauseAssetPath,
String mPlayForwardAssetPath, int mCurrentPlayerId) { 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.mPlayBackAssetPath = mPlayBackAssetPath;
this.mPlayResumeAssetPath = mPlayResumeAssetPath; this.mPlayResumeAssetPath = mPlayResumeAssetPath;
this.mPlayPauseAssetPath = mPlayPauseAssetPath; this.mPlayPauseAssetPath = mPlayPauseAssetPath;
this.mPlayForwardAssetPath = mPlayForwardAssetPath; this.mPlayForwardAssetPath = mPlayForwardAssetPath;
this.mCurrentPlayerId = mCurrentPlayerId; this.mCurrentPlayerId = mCurrentPlayerId;
this.mIsNeedPlayBack = isNeedPlayBack;
this.mIsNeedPlayForward = isNeedPlayForward;
this.mIsNeedPlayControl = isNeedPlayControl;
} }
} }
......
...@@ -3,7 +3,7 @@ package com.tencent.vod.flutter; ...@@ -3,7 +3,7 @@ package com.tencent.vod.flutter;
import android.text.TextUtils; import android.text.TextUtils;
import com.tencent.rtmp.TXPlayerGlobalSetting; import com.tencent.rtmp.TXLivePlayConfig;
import com.tencent.rtmp.TXVodPlayConfig; import com.tencent.rtmp.TXVodPlayConfig;
import java.util.HashMap; import java.util.HashMap;
...@@ -15,75 +15,61 @@ import java.util.Map; ...@@ -15,75 +15,61 @@ import java.util.Map;
public class FTXTransformation { public class FTXTransformation {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public static TXVodPlayConfig transformToConfig(Map<Object, Object> config) { public static TXVodPlayConfig transformToVodConfig(Map<Object, Object> config) {
TXVodPlayConfig playConfig = new TXVodPlayConfig(); TXVodPlayConfig playConfig = new TXVodPlayConfig();
Integer connectRetryCount = (Integer) config.get("connectRetryCount"); Integer connectRetryCount = (Integer) config.get("connectRetryCount");
if (intIsNotEmpty(connectRetryCount)) { if (intIsNotEmpty(connectRetryCount)) {
playConfig.setConnectRetryCount(connectRetryCount); playConfig.setConnectRetryCount(connectRetryCount);
} }
Integer connectRetryInterval = (Integer) config.get("connectRetryInterval"); Integer connectRetryInterval = (Integer) config.get("connectRetryInterval");
if (intIsNotEmpty(connectRetryInterval)) { if (intIsNotEmpty(connectRetryInterval)) {
playConfig.setConnectRetryInterval(connectRetryInterval); playConfig.setConnectRetryInterval(connectRetryInterval);
} }
Integer timeout = (Integer) config.get("timeout"); Integer timeout = (Integer) config.get("timeout");
if (intIsNotEmpty(timeout)) { if (intIsNotEmpty(timeout)) {
playConfig.setTimeout(timeout); playConfig.setTimeout(timeout);
} }
Integer playerType = (Integer) config.get("playerType"); Integer playerType = (Integer) config.get("playerType");
if(null != playerType) { if(null != playerType) {
playConfig.setPlayerType(playerType); playConfig.setPlayerType(playerType);
} }
Map<String, String> headers = (Map<String, String>) config.get("headers"); Map<String, String> headers = (Map<String, String>) config.get("headers");
if (null == headers) { if (null == headers) {
headers = new HashMap<>(); headers = new HashMap<>();
} }
playConfig.setHeaders(headers); playConfig.setHeaders(headers);
Boolean enableAccurateSeek = (Boolean) config.get("enableAccurateSeek"); Boolean enableAccurateSeek = (Boolean) config.get("enableAccurateSeek");
if(null != enableAccurateSeek) { if(null != enableAccurateSeek) {
playConfig.setEnableAccurateSeek(enableAccurateSeek); playConfig.setEnableAccurateSeek(enableAccurateSeek);
} }
Boolean autoRotate = (Boolean) config.get("autoRotate"); Boolean autoRotate = (Boolean) config.get("autoRotate");
if(null != autoRotate) { if(null != autoRotate) {
playConfig.setAutoRotate(autoRotate); playConfig.setAutoRotate(autoRotate);
} }
Boolean smoothSwitchBitrate = (Boolean) config.get("smoothSwitchBitrate"); Boolean smoothSwitchBitrate = (Boolean) config.get("smoothSwitchBitrate");
if(null != smoothSwitchBitrate) { if(null != smoothSwitchBitrate) {
playConfig.setSmoothSwitchBitrate(smoothSwitchBitrate); playConfig.setSmoothSwitchBitrate(smoothSwitchBitrate);
} }
String cacheMp4ExtName = (String) config.get("cacheMp4ExtName"); String cacheMp4ExtName = (String) config.get("cacheMp4ExtName");
if (!TextUtils.isEmpty(cacheMp4ExtName)) { if (!TextUtils.isEmpty(cacheMp4ExtName)) {
playConfig.setCacheMp4ExtName(cacheMp4ExtName); playConfig.setCacheMp4ExtName(cacheMp4ExtName);
} }
Integer progressInterval = (Integer) config.get("progressInterval"); Integer progressInterval = (Integer) config.get("progressInterval");
if (intIsNotEmpty(progressInterval)) { if (intIsNotEmpty(progressInterval)) {
playConfig.setProgressInterval(progressInterval); playConfig.setProgressInterval(progressInterval);
} }
Integer maxBufferSize = (Integer) config.get("maxBufferSize"); Integer maxBufferSize = (Integer) config.get("maxBufferSize");
if (intIsNotEmpty(maxBufferSize)) { if (intIsNotEmpty(maxBufferSize)) {
playConfig.setMaxBufferSize(maxBufferSize); playConfig.setMaxBufferSize(maxBufferSize);
} }
Integer maxPreloadSize = (Integer) config.get("maxPreloadSize"); Integer maxPreloadSize = (Integer) config.get("maxPreloadSize");
if (intIsNotEmpty(maxPreloadSize)) { if (intIsNotEmpty(maxPreloadSize)) {
playConfig.setMaxPreloadSize(maxPreloadSize); playConfig.setMaxPreloadSize(maxPreloadSize);
} }
Integer firstStartPlayBufferTime = (Integer) config.get("firstStartPlayBufferTime"); Integer firstStartPlayBufferTime = (Integer) config.get("firstStartPlayBufferTime");
if(null != firstStartPlayBufferTime) { if(null != firstStartPlayBufferTime) {
playConfig.setFirstStartPlayBufferTime(firstStartPlayBufferTime); playConfig.setFirstStartPlayBufferTime(firstStartPlayBufferTime);
} }
Integer nextStartPlayBufferTime = (Integer) config.get("nextStartPlayBufferTime"); Integer nextStartPlayBufferTime = (Integer) config.get("nextStartPlayBufferTime");
if(null != nextStartPlayBufferTime) { if(null != nextStartPlayBufferTime) {
playConfig.setNextStartPlayBufferTime(nextStartPlayBufferTime); playConfig.setNextStartPlayBufferTime(nextStartPlayBufferTime);
...@@ -93,23 +79,19 @@ public class FTXTransformation { ...@@ -93,23 +79,19 @@ public class FTXTransformation {
if (!TextUtils.isEmpty(overlayKey)) { if (!TextUtils.isEmpty(overlayKey)) {
playConfig.setOverlayKey(overlayKey); playConfig.setOverlayKey(overlayKey);
} }
String overlayIv = (String) config.get("overlayIv"); String overlayIv = (String) config.get("overlayIv");
if (!TextUtils.isEmpty(overlayIv)) { if (!TextUtils.isEmpty(overlayIv)) {
playConfig.setOverlayIv(overlayIv); playConfig.setOverlayIv(overlayIv);
} }
Map<String, Object> extInfoMap = (Map<String, Object>) config.get("extInfoMap"); Map<String, Object> extInfoMap = (Map<String, Object>) config.get("extInfoMap");
if (null == extInfoMap) { if (null == extInfoMap) {
extInfoMap = new HashMap<>(); extInfoMap = new HashMap<>();
} }
playConfig.setExtInfo(extInfoMap); playConfig.setExtInfo(extInfoMap);
Boolean enableRenderProcess = (Boolean) config.get("enableRenderProcess"); Boolean enableRenderProcess = (Boolean) config.get("enableRenderProcess");
if(null != enableRenderProcess) { if(null != enableRenderProcess) {
playConfig.setEnableRenderProcess(enableRenderProcess); playConfig.setEnableRenderProcess(enableRenderProcess);
} }
String preferredResolutionStr = (String) config.get("preferredResolution"); String preferredResolutionStr = (String) config.get("preferredResolution");
if (null != preferredResolutionStr) { if (null != preferredResolutionStr) {
long preferredResolution = Long.parseLong(preferredResolutionStr); long preferredResolution = Long.parseLong(preferredResolutionStr);
...@@ -119,8 +101,60 @@ public class FTXTransformation { ...@@ -119,8 +101,60 @@ public class FTXTransformation {
return playConfig; 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) { private static boolean intIsNotEmpty(Integer value) {
return null != value && value > 0; return null != value && value > 0;
} }
private static boolean doubleIsNotEmpty(Double value) {
return null != value && value > 0;
}
} }
...@@ -22,7 +22,6 @@ import java.util.ArrayList; ...@@ -22,7 +22,6 @@ import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set;
import io.flutter.embedding.engine.plugins.FlutterPlugin; import io.flutter.embedding.engine.plugins.FlutterPlugin;
import io.flutter.plugin.common.EventChannel; import io.flutter.plugin.common.EventChannel;
...@@ -510,7 +509,7 @@ public class FTXVodPlayer extends FTXBasePlayer implements MethodChannel.MethodC ...@@ -510,7 +509,7 @@ public class FTXVodPlayer extends FTXBasePlayer implements MethodChannel.MethodC
void setPlayConfig(Map<Object, Object> config) { void setPlayConfig(Map<Object, Object> config) {
if (mVodPlayer != null) { if (mVodPlayer != null) {
TXVodPlayConfig playConfig = FTXTransformation.transformToConfig(config); TXVodPlayConfig playConfig = FTXTransformation.transformToVodConfig(config);
mVodPlayer.setConfig(playConfig); mVodPlayer.setConfig(playConfig);
} }
} }
......
...@@ -133,7 +133,7 @@ public class SuperPlayerPlugin implements FlutterPlugin, MethodCallHandler, Acti ...@@ -133,7 +133,7 @@ public class SuperPlayerPlugin implements FlutterPlugin, MethodCallHandler, Acti
mPlayers.append(playerId, player); mPlayers.append(playerId, player);
result.success(playerId); result.success(playerId);
} else if (call.method.equals("createLivePlayer")) { } 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(); int playerId = player.getPlayerId();
mPlayers.append(playerId, player); mPlayers.append(playerId, player);
result.success(playerId); result.success(playerId);
...@@ -210,6 +210,10 @@ public class SuperPlayerPlugin implements FlutterPlugin, MethodCallHandler, Acti ...@@ -210,6 +210,10 @@ public class SuperPlayerPlugin implements FlutterPlugin, MethodCallHandler, Acti
result.success(mTxPipManager.isSupportDevice()); result.success(mTxPipManager.isSupportDevice());
} else if (call.method.equals("getLiteAVSDKVersion")) { } else if (call.method.equals("getLiteAVSDKVersion")) {
result.success(TXLiveBase.getSDKVersionStr()); result.success(TXLiveBase.getSDKVersionStr());
} else if(call.method.equals("setGlobalEnv")) {
String envConfig = call.argument("envConfig");
int setResult = TXLiveBase.setGlobalEnv(envConfig);
result.success(setResult);
} else { } else {
result.notImplemented(); result.notImplemented();
} }
......
...@@ -13,26 +13,12 @@ ...@@ -13,26 +13,12 @@
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-feature android:name="android.hardware.camera" /> <uses-feature android:name="android.hardware.camera" />
<uses-feature android:name="android.hardware.camera.autofocus" /> <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.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.CALL_PHONE" /> <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.RECORD_AUDIO" />
<uses-permission android:name="android.permission.CAMERA" /> <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.MODIFY_AUDIO_SETTINGS" />
<uses-permission android:name="android.permission.BLUETOOTH" /> <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" /> <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
</manifest> </manifest>
...@@ -14,10 +14,20 @@ class DemoSuperplayer extends StatefulWidget { ...@@ -14,10 +14,20 @@ class DemoSuperplayer extends StatefulWidget {
} }
class _DemoSuperplayerState extends State<DemoSuperplayer> { class _DemoSuperplayerState extends State<DemoSuperplayer> {
static const DEFAULT_PLACE_HOLDER = "http://xiaozhibo-10055601.file.myqcloud.com/coverImg.jpg";
List<SuperPlayerModel> videoModels = []; List<SuperPlayerModel> videoModels = [];
bool _isFullScreen = false; bool _isFullScreen = false;
late SuperPlayerController _controller; late SuperPlayerController _controller;
StreamSubscription? simpleEventSubscription; StreamSubscription? simpleEventSubscription;
int tabSelectPos = 0;
TextStyle _textStyleSelected = new TextStyle(
fontSize: 16, color: Colors.white
);
TextStyle _textStyleUnSelected = new TextStyle(
fontSize: 16, color: Colors.grey
);
@override @override
void initState() { void initState() {
...@@ -30,12 +40,14 @@ class _DemoSuperplayerState extends State<DemoSuperplayer> { ...@@ -30,12 +40,14 @@ class _DemoSuperplayerState extends State<DemoSuperplayer> {
simpleEventSubscription = _controller.onSimplePlayerEventBroadcast.listen((event) { simpleEventSubscription = _controller.onSimplePlayerEventBroadcast.listen((event) {
String evtName = event["event"]; String evtName = event["event"];
if (evtName == SuperPlayerViewEvent.onStartFullScreenPlay) { if (evtName == SuperPlayerViewEvent.onStartFullScreenPlay) {
// enter fullscreen
} else if (evtName == SuperPlayerViewEvent.onStopFullScreenPlay) { } else if (evtName == SuperPlayerViewEvent.onStopFullScreenPlay) {
// exit fullscreen
} else { } else {
print(evtName); print(evtName);
} }
}); });
initData(); _getLiveListData();
} }
@override @override
...@@ -69,6 +81,34 @@ class _DemoSuperplayerState extends State<DemoSuperplayer> { ...@@ -69,6 +81,34 @@ class _DemoSuperplayerState extends State<DemoSuperplayer> {
return !_controller.onBackPress(); 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() { Widget getBody() {
return Column( return Column(
children: [_getPlayArea(), Expanded(flex: 1, child: _getListArea()), _getAddArea()], children: [_getPlayArea(), Expanded(flex: 1, child: _getListArea()), _getAddArea()],
...@@ -86,12 +126,18 @@ class _DemoSuperplayerState extends State<DemoSuperplayer> { ...@@ -86,12 +126,18 @@ class _DemoSuperplayerState extends State<DemoSuperplayer> {
Widget _getListArea() { Widget _getListArea() {
return Container( return Container(
margin: EdgeInsets.only(top: 10), margin: EdgeInsets.only(top: 10),
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
children: [
getTabRow(),
Expanded(
child: ListView.builder( child: ListView.builder(
shrinkWrap: true, shrinkWrap: true,
itemCount: videoModels.length, itemCount: videoModels.length,
itemBuilder: (context, i) => _buildVideoItem(videoModels[i]), itemBuilder: (context, i) => _buildVideoItem(videoModels[i]),
), ))
); ],
));
} }
Widget _buildVideoItem(SuperPlayerModel playModel) { Widget _buildVideoItem(SuperPlayerModel playModel) {
...@@ -99,7 +145,7 @@ class _DemoSuperplayerState extends State<DemoSuperplayer> { ...@@ -99,7 +145,7 @@ class _DemoSuperplayerState extends State<DemoSuperplayer> {
children: [ children: [
ListTile( ListTile(
leading: Image.network( leading: Image.network(
playModel.coverUrl, playModel.coverUrl.isEmpty ? DEFAULT_PLACE_HOLDER : playModel.coverUrl,
width: 100, width: 100,
height: 60, height: 60,
fit: BoxFit.cover, fit: BoxFit.cover,
...@@ -110,7 +156,8 @@ class _DemoSuperplayerState extends State<DemoSuperplayer> { ...@@ -110,7 +156,8 @@ class _DemoSuperplayerState extends State<DemoSuperplayer> {
style: TextStyle(color: Colors.white), style: TextStyle(color: Colors.white),
), ),
onTap: () => playCurrentModel(playModel), onTap: () => playCurrentModel(playModel),
horizontalTitleGap: 10,), horizontalTitleGap: 10,
),
Divider() Divider()
], ],
); );
...@@ -124,6 +171,7 @@ class _DemoSuperplayerState extends State<DemoSuperplayer> { ...@@ -124,6 +171,7 @@ class _DemoSuperplayerState extends State<DemoSuperplayer> {
} }
void onAddVideoTap(BuildContext context) { void onAddVideoTap(BuildContext context) {
bool isLive = tabSelectPos == 0;
showDialog( showDialog(
context: context, context: context,
builder: (context) { builder: (context) {
...@@ -144,7 +192,7 @@ class _DemoSuperplayerState extends State<DemoSuperplayer> { ...@@ -144,7 +192,7 @@ class _DemoSuperplayerState extends State<DemoSuperplayer> {
} }
playCurrentModel(model); playCurrentModel(model);
}, needPisgn: true); }, needPisgn: !isLive, showFileEdited: !isLive,);
}); });
} }
...@@ -152,7 +200,35 @@ class _DemoSuperplayerState extends State<DemoSuperplayer> { ...@@ -152,7 +200,35 @@ class _DemoSuperplayerState extends State<DemoSuperplayer> {
_controller.playWithModel(model); _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 = []; List<SuperPlayerModel> models = [];
int playAction = SuperPlayerModel.PLAY_ACTION_AUTO_PLAY; int playAction = SuperPlayerModel.PLAY_ACTION_AUTO_PLAY;
...@@ -196,13 +272,12 @@ class _DemoSuperplayerState extends State<DemoSuperplayer> { ...@@ -196,13 +272,12 @@ class _DemoSuperplayerState extends State<DemoSuperplayer> {
List<Future<void>> requestList = []; List<Future<void>> requestList = [];
SuperVodDataLoader loader = SuperVodDataLoader(); SuperVodDataLoader loader = SuperVodDataLoader();
for (SuperPlayerModel tempModel in models) { for (SuperPlayerModel tempModel in models) {
requestList.add(loader.getVideoData(tempModel, (resultModel) { requestList.add(loader.getVideoData(tempModel, (_) {}));
videoModels.add(resultModel);
}));
} }
await Future.wait(requestList); await Future.wait(requestList);
videoModels.clear();
videoModels.addAll(models);
setState(() { setState(() {
if (videoModels.isNotEmpty) { if (videoModels.isNotEmpty) {
playCurrentModel(videoModels[0]); playCurrentModel(videoModels[0]);
......
...@@ -81,15 +81,16 @@ class _DemoTXLivelayerState extends State<DemoTXLivePlayer> with WidgetsBindingO ...@@ -81,15 +81,16 @@ class _DemoTXLivelayerState extends State<DemoTXLivePlayer> with WidgetsBindingO
await SuperPlayerPlugin.setConsoleEnabled(true); await SuperPlayerPlugin.setConsoleEnabled(true);
await _controller.initialize(); await _controller.initialize();
await _controller.setConfig(FTXLivePlayConfig());
// 安卓需要设置hls格式才可正常播放 // 安卓需要设置hls格式才可正常播放
await _controller.play(_url, playType: TXPlayType.LIVE_FLV); await _controller.startPlay(_url, playType: TXPlayType.LIVE_FLV);
} }
@override @override
void initState() { void initState() {
super.initState(); super.initState();
init(); init();
WidgetsBinding.instance?.addObserver(this); WidgetsBinding.instance.addObserver(this);
EasyLoading.show(status: 'loading...'); EasyLoading.show(status: 'loading...');
} }
...@@ -207,7 +208,7 @@ class _DemoTXLivelayerState extends State<DemoTXLivePlayer> with WidgetsBindingO ...@@ -207,7 +208,7 @@ class _DemoTXLivelayerState extends State<DemoTXLivePlayer> with WidgetsBindingO
), ),
new GestureDetector( new GestureDetector(
onTap: () async { onTap: () async {
_controller.play(_url, playType: TXPlayType.LIVE_FLV); _controller.startPlay(_url, playType: TXPlayType.LIVE_FLV);
}, },
child: Container( child: Container(
color: Colors.transparent, color: Colors.transparent,
...@@ -290,7 +291,7 @@ class _DemoTXLivelayerState extends State<DemoTXLivePlayer> with WidgetsBindingO ...@@ -290,7 +291,7 @@ class _DemoTXLivelayerState extends State<DemoTXLivePlayer> with WidgetsBindingO
playNetEventSubscription?.cancel(); playNetEventSubscription?.cancel();
_controller.dispose(); _controller.dispose();
super.dispose(); super.dispose();
WidgetsBinding.instance?.removeObserver(this); WidgetsBinding.instance.removeObserver(this);
EasyLoading.dismiss(); EasyLoading.dismiss();
} }
...@@ -302,7 +303,7 @@ class _DemoTXLivelayerState extends State<DemoTXLivePlayer> with WidgetsBindingO ...@@ -302,7 +303,7 @@ class _DemoTXLivelayerState extends State<DemoTXLivePlayer> with WidgetsBindingO
_url = url; _url = url;
_controller.stop(); _controller.stop();
if (url.isNotEmpty) { if (url.isNotEmpty) {
_controller.play(url); _controller.startPlay(url);
} }
}, showFileEdited: false); }, showFileEdited: false);
}); });
......
...@@ -18,7 +18,7 @@ class DemoTXVodPlayer extends StatefulWidget { ...@@ -18,7 +18,7 @@ class DemoTXVodPlayer extends StatefulWidget {
class _DemoTXVodlayerState extends State<DemoTXVodPlayer> class _DemoTXVodlayerState extends State<DemoTXVodPlayer>
with WidgetsBindingObserver { with WidgetsBindingObserver {
late TXVodPlayerController _controller; late TXVodPlayerController _controller;
double _aspectRatio = 0; double _aspectRatio = 16 / 9;
double _currentProgress = 0.0; double _currentProgress = 0.0;
bool _isMute = false; bool _isMute = false;
int _volume = 100; int _volume = 100;
...@@ -92,7 +92,7 @@ class _DemoTXVodlayerState extends State<DemoTXVodPlayer> ...@@ -92,7 +92,7 @@ class _DemoTXVodlayerState extends State<DemoTXVodPlayer>
void initState() { void initState() {
super.initState(); super.initState();
init(); init();
WidgetsBinding.instance?.addObserver(this); WidgetsBinding.instance.addObserver(this);
EasyLoading.show(status: 'loading...'); EasyLoading.show(status: 'loading...');
} }
...@@ -386,7 +386,7 @@ class _DemoTXVodlayerState extends State<DemoTXVodPlayer> ...@@ -386,7 +386,7 @@ class _DemoTXVodlayerState extends State<DemoTXVodPlayer>
playEventSubscription?.cancel(); playEventSubscription?.cancel();
_controller.dispose(); _controller.dispose();
super.dispose(); super.dispose();
WidgetsBinding.instance?.removeObserver(this); WidgetsBinding.instance.removeObserver(this);
EasyLoading.dismiss(); EasyLoading.dismiss();
} }
......
...@@ -6,4 +6,5 @@ class ColorResource { ...@@ -6,4 +6,5 @@ class ColorResource {
static const COLOR_GRAY = 0xFFBBBBBB; static const COLOR_GRAY = 0xFFBBBBBB;
static const COLOR_TRANS_BLACK = 0xBB000000; static const COLOR_TRANS_BLACK = 0xBB000000;
static const COLOR_WHITE = 0xFFFFFFFF; static const COLOR_WHITE = 0xFFFFFFFF;
static const COLOR_TRANS_GRAY = 0x11BBBBBB;
} }
\ No newline at end of file
...@@ -34,7 +34,7 @@ class SuperPlayerCode { ...@@ -34,7 +34,7 @@ class SuperPlayerCode {
static const VOD_REQUEST_FILE_ID_FAIL = 40002; static const VOD_REQUEST_FILE_ID_FAIL = 40002;
} }
abstract class SuperPlayerViewEvent { class SuperPlayerViewEvent {
static const onStartFullScreenPlay = "onStartFullScreenPlay"; //进入全屏播放 static const onStartFullScreenPlay = "onStartFullScreenPlay"; //进入全屏播放
static const onStopFullScreenPlay = "onStopFullScreenPlay"; //退出全屏播放 static const onStopFullScreenPlay = "onStopFullScreenPlay"; //退出全屏播放
static const onSuperPlayerDidStart = "onSuperPlayerDidStart"; //播放开始通知 static const onSuperPlayerDidStart = "onSuperPlayerDidStart"; //播放开始通知
...@@ -43,4 +43,11 @@ abstract class SuperPlayerViewEvent { ...@@ -43,4 +43,11 @@ abstract class SuperPlayerViewEvent {
static const onSuperPlayerBackAction = "onSuperPlayerBackAction"; //返回事件 static const onSuperPlayerBackAction = "onSuperPlayerBackAction"; //返回事件
} }
/// 播放器插件当前所处的布局状态
class SuperPlayerUIStatus {
static const WINDOW_MODE = 0;
static const FULLSCREEN_MODE = 1;
static const PIP_MODE = 2;
}
...@@ -8,10 +8,10 @@ class _SuperPlayerObserver { ...@@ -8,10 +8,10 @@ class _SuperPlayerObserver {
Function onPlayPause; Function onPlayPause;
Function onPlayStop; Function onPlayStop;
Function onPlayLoading; Function onPlayLoading;
Function(int current, int duration,double playableDuration) onPlayProgress; Function(double current, double duration,double playableDuration) onPlayProgress;
Function(double position) onSeek; Function(double position) onSeek;
Function(bool success, SuperPlayerType playerType, VideoQuality quality) onSwitchStreamStart; Function(bool success, SuperPlayerType playerType, VideoQuality? quality) onSwitchStreamStart;
Function(bool success, SuperPlayerType playerType, VideoQuality quality) onSwitchStreamEnd; Function(bool success, SuperPlayerType playerType, VideoQuality? quality) onSwitchStreamEnd;
Function(int code, String msg) onError; Function(int code, String msg) onError;
Function(SuperPlayerType playerType) onPlayerTypeChange; Function(SuperPlayerType playerType) onPlayerTypeChange;
Function(TXLivePlayerController controller, String url) onPlayTimeShiftLive; Function(TXLivePlayerController controller, String url) onPlayTimeShiftLive;
......
...@@ -17,15 +17,15 @@ class VideoBottomView extends StatefulWidget { ...@@ -17,15 +17,15 @@ class VideoBottomView extends StatefulWidget {
class _VideoBottomViewState extends State<VideoBottomView> { class _VideoBottomViewState extends State<VideoBottomView> {
static const TAG = "VideoBottomView"; static const TAG = "VideoBottomView";
double _currentProgress = 0;
double _playableProgress = 0; double _playableProgress = 0;
int _currentDuration = 0; double _currentDuration = 0;
int _videoDuration = 0; double _videoDuration = 0;
double _bufferedDuration = 0; double _bufferedDuration = 0;
bool _showFullScreenBtn = true; bool _showFullScreenBtn = true;
bool _isPlayMode = false; bool _isPlayMode = false;
bool _isShowQuality = false; // only showed on fullscreen mode bool _isShowQuality = false; // only showed on fullscreen mode
bool _isOnDraging = false; bool _isOnDraging = false;
SuperPlayerType _playerType = SuperPlayerType.VOD;
VideoQuality? _currentQuality; VideoQuality? _currentQuality;
@override @override
...@@ -34,13 +34,15 @@ class _VideoBottomViewState extends State<VideoBottomView> { ...@@ -34,13 +34,15 @@ class _VideoBottomViewState extends State<VideoBottomView> {
_videoDuration = widget._playerController.videoDuration; _videoDuration = widget._playerController.videoDuration;
_currentDuration = widget._playerController.currentDuration; _currentDuration = widget._playerController.currentDuration;
} else if (null != widget._playerController.videoModel) { } else if (null != widget._playerController.videoModel) {
_videoDuration = widget._playerController.videoModel!.duration; _videoDuration = widget._playerController.videoModel!.duration.toDouble();
} }
_isPlayMode = (widget._playerController.playerState == SuperPlayerState.PLAYING); _isPlayMode = (widget._playerController.playerState == SuperPlayerState.PLAYING);
_showFullScreenBtn = !widget._playerController._isFullScreen; bool isFullScreen = widget._playerController._playerUIStatus == SuperPlayerUIStatus.FULLSCREEN_MODE;
_isShowQuality = widget._playerController._isFullScreen; _showFullScreenBtn = !isFullScreen;
_isShowQuality = isFullScreen;
_currentQuality = widget._playerController.currentQuality; _currentQuality = widget._playerController.currentQuality;
_playerType = widget._playerController.playerType;
_fixProgress(); _fixProgress();
super.initState(); super.initState();
...@@ -128,8 +130,8 @@ class _VideoBottomViewState extends State<VideoBottomView> { ...@@ -128,8 +130,8 @@ class _VideoBottomViewState extends State<VideoBottomView> {
return Expanded( return Expanded(
child: VideoSlider( child: VideoSlider(
min: 0, min: 0,
max: 1, max: _videoDuration,
value: _currentProgress, value: _currentDuration,
bufferedValue: _playableProgress, bufferedValue: _playableProgress,
activeColor: Color(ColorResource.COLOR_MAIN_THEME), activeColor: Color(ColorResource.COLOR_MAIN_THEME),
inactiveColor: Color(ColorResource.COLOR_GRAY), inactiveColor: Color(ColorResource.COLOR_GRAY),
...@@ -138,16 +140,18 @@ class _VideoBottomViewState extends State<VideoBottomView> { ...@@ -138,16 +140,18 @@ class _VideoBottomViewState extends State<VideoBottomView> {
progressHeight: 2, progressHeight: 2,
sliderRadius: 4, sliderRadius: 4,
sliderOutterRadius: 10, sliderOutterRadius: 10,
// 直播禁止时移
canDrag: _playerType == SuperPlayerType.VOD,
onDragUpdate: (value) { onDragUpdate: (value) {
_isOnDraging = true; _isOnDraging = true;
}, },
onDragEnd: (value) { onDragEnd: (value) {
setState(() { setState(() {
_isOnDraging = false; _isOnDraging = false;
_currentProgress = value; _currentDuration = value * _videoDuration;
widget._playerController.seek(_currentProgress * _videoDuration); widget._playerController.seek(_currentDuration);
LogUtils.d(TAG, LogUtils.d(TAG,
"_currentProgress:$_currentProgress,_videoDuration:$_videoDuration,currentDuration:${_currentProgress * _videoDuration}"); "_currentDuration:$_currentDuration,_videoDuration:$_videoDuration");
}); });
}, },
), ),
...@@ -166,7 +170,7 @@ class _VideoBottomViewState extends State<VideoBottomView> { ...@@ -166,7 +170,7 @@ class _VideoBottomViewState extends State<VideoBottomView> {
widget._controller.onTapQuality(); widget._controller.onTapQuality();
} }
void updateDuration(int duration, int videoDuration, double bufferedDration) { void updateDuration(double duration, double videoDuration, double bufferedDration) {
if (_isOnDraging) { if (_isOnDraging) {
return; return;
} }
...@@ -183,19 +187,6 @@ class _VideoBottomViewState extends State<VideoBottomView> { ...@@ -183,19 +187,6 @@ class _VideoBottomViewState extends State<VideoBottomView> {
} }
void _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;
}
if (_bufferedDuration == 0) { if (_bufferedDuration == 0) {
_playableProgress = 0; _playableProgress = 0;
} else { } else {
...@@ -218,15 +209,24 @@ class _VideoBottomViewState extends State<VideoBottomView> { ...@@ -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(() { setState(() {
_showFullScreenBtn = !showFullScreen; bool isFullScreen = widget._playerController._playerUIStatus == SuperPlayerUIStatus.FULLSCREEN_MODE;
_isShowQuality = showFullScreen; _showFullScreenBtn = !isFullScreen;
_isShowQuality = isFullScreen;
}); });
} }
String _buildTextString(int time) { String _buildTextString(double time) {
Duration duration = Duration(seconds: time); Duration duration = Duration(seconds: time.toInt());
// 返回此持续时间跨越的整秒数。 // 返回此持续时间跨越的整秒数。
String inSeconds = (duration.inSeconds % 60).toString().padLeft(2, "0"); String inSeconds = (duration.inSeconds % 60).toString().padLeft(2, "0");
// 返回此持续时间跨越的整分钟数。 // 返回此持续时间跨越的整分钟数。
......
...@@ -43,7 +43,7 @@ class _SuperPlayerCoverViewState extends State<SuperPlayerCoverView> { ...@@ -43,7 +43,7 @@ class _SuperPlayerCoverViewState extends State<SuperPlayerCoverView> {
} }
return Visibility( return Visibility(
visible: _isShowCover, visible: _isShowCover && hasCover,
child: Positioned.fill( child: Positioned.fill(
top: topBottomOffset, top: topBottomOffset,
bottom: topBottomOffset, bottom: topBottomOffset,
...@@ -53,7 +53,11 @@ class _SuperPlayerCoverViewState extends State<SuperPlayerCoverView> { ...@@ -53,7 +53,11 @@ class _SuperPlayerCoverViewState extends State<SuperPlayerCoverView> {
onDoubleTap: _onDoubleTapVideo, onDoubleTap: _onDoubleTapVideo,
onTap: _onSingleTapVideo, onTap: _onSingleTapVideo,
child: Container( 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),
) )
)), )),
); );
......
...@@ -18,6 +18,7 @@ class _SuperPlayerMoreViewState extends State<SuperPlayerMoreView> { ...@@ -18,6 +18,7 @@ class _SuperPlayerMoreViewState extends State<SuperPlayerMoreView> {
double _currentVolumn = 0; double _currentVolumn = 0;
bool _isShowMoreView = false; bool _isShowMoreView = false;
bool _isOpenAccelerate = true; bool _isOpenAccelerate = true;
bool _isVodPlay = false;
String _currentRate = ""; String _currentRate = "";
Map<String, double> playRateStr = {"1.0x": 1.0, "1.25x": 1.25, "1.5x": 1.5, "2.0x": 2.0}; Map<String, double> playRateStr = {"1.0x": 1.0, "1.25x": 1.25, "1.5x": 1.5, "2.0x": 2.0};
StreamSubscription? volumeSubscription; StreamSubscription? volumeSubscription;
...@@ -25,6 +26,7 @@ class _SuperPlayerMoreViewState extends State<SuperPlayerMoreView> { ...@@ -25,6 +26,7 @@ class _SuperPlayerMoreViewState extends State<SuperPlayerMoreView> {
@override @override
void initState() { void initState() {
super.initState(); super.initState();
_isVodPlay = widget.controller.getIsVodPlay();
double playerPlayRate = widget.controller.getPlayRate(); double playerPlayRate = widget.controller.getPlayRate();
for(String rateStr in playRateStr.keys) { for(String rateStr in playRateStr.keys) {
if(playerPlayRate == playRateStr[rateStr]) { if(playerPlayRate == playRateStr[rateStr]) {
...@@ -132,11 +134,14 @@ class _SuperPlayerMoreViewState extends State<SuperPlayerMoreView> { ...@@ -132,11 +134,14 @@ class _SuperPlayerMoreViewState extends State<SuperPlayerMoreView> {
), ),
)); ));
} }
return Container( return Visibility(
visible: _isVodPlay,
child: Container(
margin: EdgeInsets.only(top: 10, bottom: 10), margin: EdgeInsets.only(top: 10, bottom: 10),
child: Row( child: Row(
children: playRateChild, children: playRateChild,
), ),
),
); );
} }
...@@ -243,6 +248,15 @@ class _SuperPlayerMoreViewState extends State<SuperPlayerMoreView> { ...@@ -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 @override
void dispose() { void dispose() {
super.dispose(); super.dispose();
...@@ -255,6 +269,7 @@ class _MoreViewController { ...@@ -255,6 +269,7 @@ class _MoreViewController {
DoubleFunction getPlayRate; DoubleFunction getPlayRate;
Function(bool value) siwtchAccelerate; Function(bool value) siwtchAccelerate;
Function(double playRate) onChangedPlayRate; 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);
} }
...@@ -76,9 +76,9 @@ class _VideoTitleViewState extends State<_VideoTitleView> { ...@@ -76,9 +76,9 @@ class _VideoTitleViewState extends State<_VideoTitleView> {
} }
} }
void updateFullScreen(bool showFullScreen) { void updateUIStatus(int status) {
setState(() { setState(() {
_isFullScreen = showFullScreen; _isFullScreen = status == SuperPlayerUIStatus.FULLSCREEN_MODE;
}); });
} }
} }
......
...@@ -17,6 +17,7 @@ class VideoSlider extends StatefulWidget { ...@@ -17,6 +17,7 @@ class VideoSlider extends StatefulWidget {
final Color? bufferedColor; final Color? bufferedColor;
final Color? sliderColor; final Color? sliderColor;
final Color? sliderOutterColor; final Color? sliderOutterColor;
bool? canDrag = true;
// calback // calback
final Function? onDragStart; final Function? onDragStart;
...@@ -38,13 +39,17 @@ class VideoSlider extends StatefulWidget { ...@@ -38,13 +39,17 @@ class VideoSlider extends StatefulWidget {
this.sliderOutterColor, this.sliderOutterColor,
this.onDragStart, this.onDragStart,
this.onDragUpdate, this.onDragUpdate,
this.onDragEnd}) { this.onDragEnd,
this.canDrag}) {
double range = (max - min); double range = (max - min);
_checkRange(range, valueName: "max - min"); if(range <= 0) {
controller = _VideoSliderController(1, bufferedProgress: 1);
} else {
double currentProgress = value / range; double currentProgress = value / range;
double? bufferedProgress = bufferedValue != null ? bufferedValue! / range : null; double? bufferedProgress = bufferedValue != null ? bufferedValue! / range : null;
controller = _VideoSliderController(currentProgress, bufferedProgress: bufferedProgress); controller = _VideoSliderController(currentProgress, bufferedProgress: bufferedProgress);
} }
}
@override @override
State<StatefulWidget> createState() => VideoSliderState(); State<StatefulWidget> createState() => VideoSliderState();
...@@ -77,24 +82,31 @@ class VideoSliderState extends State<VideoSlider> { ...@@ -77,24 +82,31 @@ class VideoSliderState extends State<VideoSlider> {
double rightPadding = overlayRadius; double rightPadding = overlayRadius;
return GestureDetector( return GestureDetector(
onHorizontalDragStart: (DragStartDetails details) { onHorizontalDragStart: (DragStartDetails details) {
if(widget.canDrag!) {
isDraging = true; isDraging = true;
widget.onDragStart?.call(); widget.onDragStart?.call();
}
}, },
onHorizontalDragUpdate: (DragUpdateDetails details) { onHorizontalDragUpdate: (DragUpdateDetails details) {
if(widget.canDrag!) {
isDraging = true; isDraging = true;
_seekToPosition(details.globalPosition); _seekToPosition(details.globalPosition);
widget.onDragUpdate?.call(widget.controller.progress); widget.onDragUpdate?.call(widget.controller.progress);}
}, },
onHorizontalDragEnd: (DragEndDetails details) { onHorizontalDragEnd: (DragEndDetails details) {
if(widget.canDrag!) {
isDraging = false; isDraging = false;
widget.onDragEnd?.call(widget.controller.progress); widget.onDragEnd?.call(widget.controller.progress);}
}, },
onHorizontalDragCancel: () { onHorizontalDragCancel: () {
if(widget.canDrag!) {
isDraging = false; isDraging = false;
widget.onDragEnd?.call(widget.controller.progress); widget.onDragEnd?.call(widget.controller.progress);}
}, },
onTapDown: (TapDownDetails details) { onTapDown: (TapDownDetails details) {
if(widget.canDrag!) {
_seekToPosition(details.globalPosition); _seekToPosition(details.globalPosition);
}
}, },
child: Center( child: Center(
child: Container( child: Container(
......
...@@ -7,7 +7,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev ...@@ -7,7 +7,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev
environment: environment:
sdk: '>=2.12.0 <3.0.0' sdk: '>=2.12.0 <3.0.0'
flutter: ">=1.20.0" flutter: ">=2.0.0"
dependencies: dependencies:
flutter: flutter:
......
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
#import "FTXLivePlayer.h" #import "FTXLivePlayer.h"
#import "FTXPlayerEventSinkQueue.h" #import "FTXPlayerEventSinkQueue.h"
#import "FTXTransformation.h"
#import <TXLiteAVSDK_Professional/TXLiteAVSDK.h> #import <TXLiteAVSDK_Professional/TXLiteAVSDK.h>
#import <Flutter/Flutter.h> #import <Flutter/Flutter.h>
#import <stdatomic.h> #import <stdatomic.h>
...@@ -137,11 +138,12 @@ static const int uninitialized = -1; ...@@ -137,11 +138,12 @@ static const int uninitialized = -1;
} }
} }
- (void)switchStream:(NSString *)url - (int)switchStream:(NSString *)url
{ {
if (_txLivePlayer != nil) { if (_txLivePlayer != nil) {
[_txLivePlayer switchStream:url]; return [_txLivePlayer switchStream:url];
} }
return -1;
} }
- (int)seek:(float)progress - (int)seek:(float)progress
...@@ -262,6 +264,20 @@ static const int uninitialized = -1; ...@@ -262,6 +264,20 @@ 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 - #pragma mark -
- (void)handleMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result - (void)handleMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result
...@@ -309,12 +325,15 @@ static const int uninitialized = -1; ...@@ -309,12 +325,15 @@ static const int uninitialized = -1;
result(nil); result(nil);
}else if([@"destory" isEqualToString:call.method]) { }else if([@"destory" isEqualToString:call.method]) {
[self destory]; [self destory];
result(nil);
}else if([@"setRenderRotation" isEqualToString:call.method]) { }else if([@"setRenderRotation" isEqualToString:call.method]) {
int rotation = [args[@"rotation"] intValue]; int rotation = [args[@"rotation"] intValue];
[self setRenderRotation:rotation]; [self setRenderRotation:rotation];
result(nil);
}else if([@"switchStream" isEqualToString:call.method]) { }else if([@"switchStream" isEqualToString:call.method]) {
NSString *url = args[@"url"]; NSString *url = args[@"url"];
[self switchStream:url]; int switchResult = [self switchStream:url];
result(@(switchResult));
}else if ([@"seek" isEqualToString:call.method]) { }else if ([@"seek" isEqualToString:call.method]) {
result(FlutterMethodNotImplemented); result(FlutterMethodNotImplemented);
}else if ([@"setAppID" isEqualToString:call.method]) { }else if ([@"setAppID" isEqualToString:call.method]) {
...@@ -327,6 +346,13 @@ static const int uninitialized = -1; ...@@ -327,6 +346,13 @@ static const int uninitialized = -1;
}else if([@"resumeLive" isEqualToString:call.method]) { }else if([@"resumeLive" isEqualToString:call.method]) {
int r = [self resumeLive]; int r = [self resumeLive];
result(@(r)); 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 { }else {
result(FlutterMethodNotImplemented); result(FlutterMethodNotImplemented);
} }
......
...@@ -6,6 +6,8 @@ static NSString* cacheFolder = nil; ...@@ -6,6 +6,8 @@ static NSString* cacheFolder = nil;
static int maxCacheItems = -1; static int maxCacheItems = -1;
@interface FTXTransformation : NSObject @interface FTXTransformation : NSObject
+ (TXVodPlayConfig *)transformToConfig:(NSDictionary*)map; + (TXVodPlayConfig *)transformToVodConfig:(NSDictionary*)map;
+ (TXLivePlayConfig *)transformToLiveConfig:(NSDictionary*)map;
@end @end
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
@implementation FTXTransformation @implementation FTXTransformation
+ (TXVodPlayConfig *)transformToConfig:(NSDictionary *)args + (TXVodPlayConfig *)transformToVodConfig:(NSDictionary *)args
{ {
TXVodPlayConfig *playConfig = [[TXVodPlayConfig alloc] init]; TXVodPlayConfig *playConfig = [[TXVodPlayConfig alloc] init];
playConfig.connectRetryCount = [args[@"config"][@"connectRetryCount"] intValue]; playConfig.connectRetryCount = [args[@"config"][@"connectRetryCount"] intValue];
...@@ -48,6 +48,25 @@ ...@@ -48,6 +48,25 @@
return playConfig; 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 @end
...@@ -583,7 +583,7 @@ BOOL volatile isStop = false; ...@@ -583,7 +583,7 @@ BOOL volatile isStop = false;
- (void)setPlayConfig:(NSDictionary *)args - (void)setPlayConfig:(NSDictionary *)args
{ {
if (_txVodPlayer != nil && [args[@"config"] isKindOfClass:[NSDictionary class]]) { if (_txVodPlayer != nil && [args[@"config"] isKindOfClass:[NSDictionary class]]) {
_txVodPlayer.config = [FTXTransformation transformToConfig:args]; _txVodPlayer.config = [FTXTransformation transformToVodConfig:args];
} }
} }
......
...@@ -187,6 +187,10 @@ SuperPlayerPlugin* instance; ...@@ -187,6 +187,10 @@ SuperPlayerPlugin* instance;
} else if ([@"isDeviceSupportPip" isEqualToString:call.method]) { } else if ([@"isDeviceSupportPip" isEqualToString:call.method]) {
BOOL isSupport = [TXVodPlayer isSupportPictureInPicture]; BOOL isSupport = [TXVodPlayer isSupportPictureInPicture];
result([NSNumber numberWithBool:isSupport]); result([NSNumber numberWithBool:isSupport]);
} else if([@"setGlobalEnv" isEqualToString:call.method]) {
NSString *envConfig = call.arguments[@"envConfig"];
int setResult = [TXLiveBase setGlobalEnv:[envConfig UTF8String]];
result(@(setResult));
} else { } else {
result(FlutterMethodNotImplemented); result(FlutterMethodNotImplemented);
} }
......
// 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;
}
}
...@@ -19,6 +19,7 @@ class SuperPlayerPlugin { ...@@ -19,6 +19,7 @@ class SuperPlayerPlugin {
/// 原生交互,通用事件监听,来自插件的事件,例如 声音变化等事件 /// 原生交互,通用事件监听,来自插件的事件,例如 声音变化等事件
Stream<Map<dynamic, dynamic>> get onEventBroadcast => _eventStreamController.stream; Stream<Map<dynamic, dynamic>> get onEventBroadcast => _eventStreamController.stream;
/// 原生交互,通用事件监听,来自原生容器的事件,例如 PIP事件、activity/controller 生命周期变化 /// 原生交互,通用事件监听,来自原生容器的事件,例如 PIP事件、activity/controller 生命周期变化
Stream<Map<dynamic, dynamic>> get onExtraEventBroadcast => _eventPipStreamController.stream; Stream<Map<dynamic, dynamic>> get onExtraEventBroadcast => _eventPipStreamController.stream;
...@@ -153,4 +154,16 @@ class SuperPlayerPlugin { ...@@ -153,4 +154,16 @@ class SuperPlayerPlugin {
static Future<String?> getLiteAVSDKVersion() async { static Future<String?> getLiteAVSDKVersion() async {
return await _channel.invokeMethod('getLiteAVSDKVersion'); 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});
}
} }
// 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
...@@ -9,4 +9,18 @@ abstract class TXPlayerController { ...@@ -9,4 +9,18 @@ abstract class TXPlayerController {
double? get videoTop; double? get videoTop;
double? get videoRight; double? get videoRight;
double? get videoBottom; 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
...@@ -116,7 +116,7 @@ abstract class TXVodPlayEvent { ...@@ -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_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_IS_RUNNING = -110; // pip 错误,PIP功能已经运行 only support iOS
static const ERROR_IOS_PIP_NOT_RUNNING = -111; // 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; // 视频预下载完成 static const EVENT_PREDOWNLOAD_ON_COMPLETE = 200; // 视频预下载完成
......
...@@ -3,10 +3,9 @@ part of SuperPlayer; ...@@ -3,10 +3,9 @@ part of SuperPlayer;
class TXPlayerVideo extends StatefulWidget { class TXPlayerVideo extends StatefulWidget {
final TXPlayerController controller; final TXPlayerController controller;
final Stream<TXPlayerHolder>? playerStream;
TXPlayerVideo({required this.controller, Key? key}) : super(key: key) { TXPlayerVideo({required this.controller, this.playerStream});
assert(controller != null);
}
@override @override
TXPlayerVideoState createState() => TXPlayerVideoState(); TXPlayerVideoState createState() => TXPlayerVideoState();
...@@ -16,11 +15,41 @@ class TXPlayerVideoState extends State<TXPlayerVideo> { ...@@ -16,11 +15,41 @@ class TXPlayerVideoState extends State<TXPlayerVideo> {
static const TAG = "TXPlayerVideo"; static const TAG = "TXPlayerVideo";
int _textureId = -1; int _textureId = -1;
StreamSubscription? streamSubscription;
late TXPlayerController controller;
@override @override
void initState() { void initState() {
super.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) { void _resetControllerLink() async {
int remainTextureId = await controller.textureId;
if (remainTextureId >= 0) {
if (remainTextureId != _textureId) {
setState(() {
LogUtils.d(TAG, "_textureId = $remainTextureId");
_textureId = remainTextureId;
});
}
} else {
setState(() {
_textureId = -1;
});
controller.textureId.then((newTextureId) {
if (_textureId != newTextureId) { if (_textureId != newTextureId) {
setState(() { setState(() {
LogUtils.d(TAG, "_textureId = $newTextureId"); LogUtils.d(TAG, "_textureId = $newTextureId");
...@@ -29,39 +58,39 @@ class TXPlayerVideoState extends State<TXPlayerVideo> { ...@@ -29,39 +58,39 @@ class TXPlayerVideoState extends State<TXPlayerVideo> {
} }
}); });
} }
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
if ((defaultTargetPlatform == TargetPlatform.android) && if ((defaultTargetPlatform == TargetPlatform.android) &&
(widget.controller.resizeVideoHeight! > 0 && widget.controller.resizeVideoWidth! > 0)) { (controller.resizeVideoHeight! > 0 && controller.resizeVideoWidth! > 0)) {
return _textureId == -1 return _textureId == -1
? Container() ? Container()
: LayoutBuilder(builder: (context, constrains) { : LayoutBuilder(builder: (context, constrains) {
var viewWidth = constrains.maxWidth; var viewWidth = constrains.maxWidth;
var viewHeight = constrains.maxHeight; var viewHeight = constrains.maxHeight;
var videoWidth = widget.controller.resizeVideoWidth!; var videoWidth = controller.resizeVideoWidth!;
var videoHeight = widget.controller.resizeVideoHeight!; var videoHeight = widget.controller.resizeVideoHeight!;
double left = widget.controller.videoLeft! * viewWidth / videoWidth; double left = controller.videoLeft! * viewWidth / videoWidth;
double top = widget.controller.videoTop! * viewHeight / videoHeight; double top = controller.videoTop! * viewHeight / videoHeight;
double right = widget.controller.videoRight! * viewWidth / videoWidth; double right = controller.videoRight! * viewWidth / videoWidth;
double bottom = widget.controller.videoBottom! * viewHeight / videoHeight; double bottom = controller.videoBottom! * viewHeight / videoHeight;
return Stack( return Stack(
children: [ children: [
Positioned( Positioned(
top: top, top: top, left: left, right: right, bottom: bottom, child: Texture(textureId: _textureId))
left: left,
right: right,
bottom: bottom,
child: Texture(textureId: _textureId)
)
], ],
); );
}); });
} else { } else {
return _textureId == -1 return _textureId == -1 ? Container() : Texture(textureId: _textureId);
? Container() }
: Texture(textureId: _textureId);
} }
@override
void dispose() {
streamSubscription?.cancel();
super.dispose();
} }
} }
...@@ -17,9 +17,7 @@ class TXVodPlayerController extends ChangeNotifier implements ValueListenable<TX ...@@ -17,9 +17,7 @@ class TXVodPlayerController extends ChangeNotifier implements ValueListenable<TX
StreamSubscription? _netSubscription; 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();
/// 播放状态监听,@see TXPlayerState /// 播放状态监听,@see TXPlayerState
...@@ -137,6 +135,7 @@ class TXVodPlayerController extends ChangeNotifier implements ValueListenable<TX ...@@ -137,6 +135,7 @@ class TXVodPlayerController extends ChangeNotifier implements ValueListenable<TX
/// 通过url开始播放视频 /// 通过url开始播放视频
/// @param url : 视频播放地址 /// @param url : 视频播放地址
/// return 是否播放成功 /// return 是否播放成功
@override
Future<bool> startPlay(String url) async { Future<bool> startPlay(String url) async {
await _initPlayer.future; await _initPlayer.future;
await _createTexture.future; await _createTexture.future;
...@@ -165,6 +164,7 @@ class TXVodPlayerController extends ChangeNotifier implements ValueListenable<TX ...@@ -165,6 +164,7 @@ class TXVodPlayerController extends ChangeNotifier implements ValueListenable<TX
/// 播放器初始化,创建共享纹理、初始化播放器 /// 播放器初始化,创建共享纹理、初始化播放器
/// @param onlyAudio 是否是纯音频模式 /// @param onlyAudio 是否是纯音频模式
@override
Future<void> initialize({bool? onlyAudio}) async { Future<void> initialize({bool? onlyAudio}) async {
if (_isNeedDisposed) return; if (_isNeedDisposed) return;
await _initPlayer.future; await _initPlayer.future;
...@@ -176,6 +176,7 @@ class TXVodPlayerController extends ChangeNotifier implements ValueListenable<TX ...@@ -176,6 +176,7 @@ class TXVodPlayerController extends ChangeNotifier implements ValueListenable<TX
} }
/// 设置是否自动播放 /// 设置是否自动播放
@override
Future<void> setAutoPlay({bool? isAutoPlay}) async { Future<void> setAutoPlay({bool? isAutoPlay}) async {
if (_isNeedDisposed) return; if (_isNeedDisposed) return;
await _initPlayer.future; await _initPlayer.future;
...@@ -184,7 +185,8 @@ class TXVodPlayerController extends ChangeNotifier implements ValueListenable<TX ...@@ -184,7 +185,8 @@ class TXVodPlayerController extends ChangeNotifier implements ValueListenable<TX
/// 停止播放 /// 停止播放
/// return 是否停止成功 /// return 是否停止成功
Future<bool> stop({bool isNeedClear = true}) async { @override
Future<bool> stop({bool isNeedClear = false}) async {
if (_isNeedDisposed) return false; if (_isNeedDisposed) return false;
await _initPlayer.future; await _initPlayer.future;
final result = await _channel.invokeMethod("stop", {"isNeedClear": isNeedClear}); final result = await _channel.invokeMethod("stop", {"isNeedClear": isNeedClear});
...@@ -193,12 +195,14 @@ class TXVodPlayerController extends ChangeNotifier implements ValueListenable<TX ...@@ -193,12 +195,14 @@ class TXVodPlayerController extends ChangeNotifier implements ValueListenable<TX
} }
/// 视频是否处于正在播放中 /// 视频是否处于正在播放中
Future<bool?> isPlaying() async { @override
Future<bool> isPlaying() async {
await _initPlayer.future; await _initPlayer.future;
return await _channel.invokeMethod("isPlaying"); return await _channel.invokeMethod("isPlaying");
} }
/// 视频暂停,必须在播放器开始播放的时候调用 /// 视频暂停,必须在播放器开始播放的时候调用
@override
Future<void> pause() async { Future<void> pause() async {
if (_isNeedDisposed) return; if (_isNeedDisposed) return;
await _initPlayer.future; await _initPlayer.future;
...@@ -207,6 +211,7 @@ class TXVodPlayerController extends ChangeNotifier implements ValueListenable<TX ...@@ -207,6 +211,7 @@ class TXVodPlayerController extends ChangeNotifier implements ValueListenable<TX
} }
/// 继续播放,在暂停的时候调用 /// 继续播放,在暂停的时候调用
@override
Future<void> resume() async { Future<void> resume() async {
if (_isNeedDisposed) return; if (_isNeedDisposed) return;
await _initPlayer.future; await _initPlayer.future;
...@@ -214,6 +219,7 @@ class TXVodPlayerController extends ChangeNotifier implements ValueListenable<TX ...@@ -214,6 +219,7 @@ class TXVodPlayerController extends ChangeNotifier implements ValueListenable<TX
} }
/// 设置是否静音 /// 设置是否静音
@override
Future<void> setMute(bool mute) async { Future<void> setMute(bool mute) async {
if (_isNeedDisposed) return; if (_isNeedDisposed) return;
await _initPlayer.future; await _initPlayer.future;
...@@ -229,6 +235,7 @@ class TXVodPlayerController extends ChangeNotifier implements ValueListenable<TX ...@@ -229,6 +235,7 @@ class TXVodPlayerController extends ChangeNotifier implements ValueListenable<TX
/// 将视频播放进度定位到指定的进度进行播放 /// 将视频播放进度定位到指定的进度进行播放
/// progress 要定位的视频时间,单位 秒 /// progress 要定位的视频时间,单位 秒
@override
Future<void> seek(double progress) async { Future<void> seek(double progress) async {
if (_isNeedDisposed) return; if (_isNeedDisposed) return;
await _initPlayer.future; await _initPlayer.future;
...@@ -236,6 +243,7 @@ class TXVodPlayerController extends ChangeNotifier implements ValueListenable<TX ...@@ -236,6 +243,7 @@ class TXVodPlayerController extends ChangeNotifier implements ValueListenable<TX
} }
/// 设置播放速率,默认速率 1 /// 设置播放速率,默认速率 1
@override
Future<void> setRate(double rate) async { Future<void> setRate(double rate) async {
if (_isNeedDisposed) return; if (_isNeedDisposed) return;
await _initPlayer.future; await _initPlayer.future;
...@@ -294,7 +302,7 @@ class TXVodPlayerController extends ChangeNotifier implements ValueListenable<TX ...@@ -294,7 +302,7 @@ class TXVodPlayerController extends ChangeNotifier implements ValueListenable<TX
} }
/// 设置播放器配置 /// 设置播放器配置
/// config @see FTXVodPlayConfig /// config @see [FTXVodPlayConfig]
Future<void> setConfig(FTXVodPlayConfig config) async { Future<void> setConfig(FTXVodPlayConfig config) async {
if (_isNeedDisposed) return; if (_isNeedDisposed) return;
await _initPlayer.future; await _initPlayer.future;
...@@ -351,6 +359,7 @@ class TXVodPlayerController extends ChangeNotifier implements ValueListenable<TX ...@@ -351,6 +359,7 @@ class TXVodPlayerController extends ChangeNotifier implements ValueListenable<TX
} }
/// 开启/关闭硬件编码 /// 开启/关闭硬件编码
@override
Future<bool> enableHardwareDecode(bool enable) async { Future<bool> enableHardwareDecode(bool enable) async {
if (_isNeedDisposed) return false; if (_isNeedDisposed) return false;
await _initPlayer.future; await _initPlayer.future;
...@@ -363,12 +372,26 @@ class TXVodPlayerController extends ChangeNotifier implements ValueListenable<TX ...@@ -363,12 +372,26 @@ class TXVodPlayerController extends ChangeNotifier implements ValueListenable<TX
/// </h1> /// </h1>
/// @param backIcon playIcon pauseIcon forwardIcon 为播放后退、播放、暂停、前进的图标,如果赋值的话,将会使用传递的图标,否则 /// @param backIcon playIcon pauseIcon forwardIcon 为播放后退、播放、暂停、前进的图标,如果赋值的话,将会使用传递的图标,否则
/// 使用系统默认图标,只支持flutter本地资源图片,传递的时候,与flutter使用图片资源一致,例如: images/back_icon.png /// 使用系统默认图标,只支持flutter本地资源图片,传递的时候,与flutter使用图片资源一致,例如: images/back_icon.png
@override
Future<int> enterPictureInPictureMode( 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; if (_isNeedDisposed) return -1;
await _initPlayer.future; await _initPlayer.future;
return await _channel.invokeMethod("enterPictureInPictureMode", if (Platform.isAndroid) {
{"backIcon": backIcon, "playIcon": playIcon, "pauseIcon": pauseIcon, "forwardIcon": forwardIcon}); 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 ...@@ -378,13 +401,15 @@ class TXVodPlayerController extends ChangeNotifier implements ValueListenable<TX
return await _channel.invokeMethod("getDuration"); return await _channel.invokeMethod("getDuration");
} }
/// 废弃controller /// 释放controller
@override @override
void dispose() async { void dispose() async {
_isNeedDisposed = true; _isNeedDisposed = true;
if (!_isDisposed) { if (!_isDisposed) {
await _eventSubscription!.cancel(); await _eventSubscription!.cancel();
_eventSubscription = null; _eventSubscription = null;
await _netSubscription!.cancel();
_netSubscription = null;
await _release(); await _release();
_changeState(TXPlayerState.disposed); _changeState(TXPlayerState.disposed);
...@@ -398,8 +423,6 @@ class TXVodPlayerController extends ChangeNotifier implements ValueListenable<TX ...@@ -398,8 +423,6 @@ class TXVodPlayerController extends ChangeNotifier implements ValueListenable<TX
} }
@override @override
// TODO: implement value
get value => _value; get value => _value;
set value(TXPlayerValue? val) { set value(TXPlayerValue? val) {
......
...@@ -3,6 +3,7 @@ library SuperPlayer; ...@@ -3,6 +3,7 @@ library SuperPlayer;
import 'dart:async'; import 'dart:async';
import 'dart:core'; import 'dart:core';
import 'dart:io';
import 'dart:math'; import 'dart:math';
import 'package:flutter/cupertino.dart'; import 'package:flutter/cupertino.dart';
...@@ -19,4 +20,6 @@ part 'Core/txplayer_widget.dart'; ...@@ -19,4 +20,6 @@ part 'Core/txplayer_widget.dart';
part 'Core/txvodplayer_config.dart'; part 'Core/txvodplayer_config.dart';
part 'Core/txvodplayer_controller.dart'; part 'Core/txvodplayer_controller.dart';
part 'Core/txvoddownload_controller.dart'; part 'Core/txvoddownload_controller.dart';
part 'Core/txliveplayer_config.dart';
part 'Core/tools/common_utils.dart'; part 'Core/tools/common_utils.dart';
part 'Core/provider/txplayer_holder.dart';
\ No newline at end of file
name: super_player name: super_player
description: player plugin. description: player plugin.
version: 1.0.3 version: 1.0.5
author: author:
homepage: homepage:
environment: environment:
sdk: '>=2.12.0 <3.0.0' sdk: '>=2.12.0 <3.0.0'
flutter: ">=1.20.0" flutter: ">=2.0.0"
dependencies: dependencies:
flutter: flutter:
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论