提交 a679b40a authored 作者: dokieyang's avatar dokieyang

Add Player_Premium features

1. supports external subtitle 2. supports multi audio tracks 3. supports SEI, PdtSeek and mediaType for Vodconfig
上级 546f55e5
...@@ -5,10 +5,13 @@ rootProject.ext { ...@@ -5,10 +5,13 @@ rootProject.ext {
minSdkVersion = 19 minSdkVersion = 19
targetSdkVersion = 28 targetSdkVersion = 28
playerVersion = '11.7.0' playerVersion = '11.7.0'
/*
在此处可以更换需要的SDK版本,替换为专业版为 com.tencent.liteav:LiteAVSDK_Professional:latest.release /**
其中 latest.release 可指定为自己需要的版本号,例如 "com.tencent.liteav:LiteAVSDK_Player:9.5.29035" ,写成 latest.release 则默认使用最新版本 Set the dependent LiteAV SDK type:
Player SDK: liteavSdk="com.tencent.liteav:LiteAVSDK_Player:latest.release"
Player_Premium SDK: liteavSdk="com.tencent.liteav:LiteAVSDK_Player_Premium:latest.release"
Professional SDK: liteavSdk="com.tencent.liteav:LiteAVSDK_Professional:latest.release"
If you want to specify the SDK version(eg 11.7.0.13946), use: liteavSdk="com.tencent.liteav:LiteAVSDK_Player:11.7.0.13946"
*/ */
liteavSdk="com.tencent.liteav:LiteAVSDK_Player:latest.release" liteavSdk="com.tencent.liteav:LiteAVSDK_Player:11.7.0.13946"
// liteavSdk="com.tencent.liteav:LiteAVSDK_Professional:latest.release"
} }
\ No newline at end of file
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
package="com.tencent.vod.flutter"> package="com.tencent.vod.flutter">
<uses-permission android:name="android.permission.REORDER_TASKS" />
<application> <application>
<activity <activity
android:name="com.tencent.vod.flutter.ui.FlutterPipImplActivity" android:name="com.tencent.vod.flutter.ui.FlutterPipImplActivity"
......
...@@ -181,4 +181,13 @@ public class FTXEvent { ...@@ -181,4 +181,13 @@ public class FTXEvent {
public static final String EVENT_HOST_NAME = "hostName"; public static final String EVENT_HOST_NAME = "hostName";
public static final String EVENT_IPS = "ips"; public static final String EVENT_IPS = "ips";
// subtitle data event
// 回调 SubtitleData 事件id
public static final int EVENT_SUBTITLE_DATA = 601;
// subtitle data extra key
// 回调 SubtitleData 事件对应的key
public static final String EXTRA_SUBTITLE_DATA = "subtitleData";
public static final String EXTRA_SUBTITLE_START_POSITION_MS = "startPositionMs";
public static final String EXTRA_SUBTITLE_DURATION_MS = "durationMs";
public static final String EXTRA_SUBTITLE_TRACK_INDEX = "trackIndex";
} }
...@@ -4,9 +4,11 @@ package com.tencent.vod.flutter; ...@@ -4,9 +4,11 @@ package com.tencent.vod.flutter;
import android.text.TextUtils; import android.text.TextUtils;
import com.tencent.liteav.txcplayer.model.TXSubtitleRenderModel;
import com.tencent.rtmp.TXLivePlayConfig; import com.tencent.rtmp.TXLivePlayConfig;
import com.tencent.rtmp.TXVodPlayConfig; import com.tencent.rtmp.TXVodPlayConfig;
import com.tencent.vod.flutter.messages.FtxMessages;
import com.tencent.vod.flutter.messages.FtxMessages.FTXLivePlayConfigPlayerMsg; import com.tencent.vod.flutter.messages.FtxMessages.FTXLivePlayConfigPlayerMsg;
import com.tencent.vod.flutter.messages.FtxMessages.FTXVodPlayConfigPlayerMsg; import com.tencent.vod.flutter.messages.FtxMessages.FTXVodPlayConfigPlayerMsg;
import java.util.HashMap; import java.util.HashMap;
...@@ -73,7 +75,7 @@ public class FTXTransformation { ...@@ -73,7 +75,7 @@ public class FTXTransformation {
if (null != configPlayerMsg.getPreferredResolution()) { if (null != configPlayerMsg.getPreferredResolution()) {
playConfig.setPreferredResolution(configPlayerMsg.getPreferredResolution()); playConfig.setPreferredResolution(configPlayerMsg.getPreferredResolution());
} }
playConfig.setMediaType(configPlayerMsg.getMediaType().intValue());
return playConfig; return playConfig;
} }
...@@ -264,6 +266,48 @@ public class FTXTransformation { ...@@ -264,6 +266,48 @@ public class FTXTransformation {
return livePlayConfig; return livePlayConfig;
} }
public static TXSubtitleRenderModel transToTitleRenderModel(FtxMessages.SubTitleRenderModelPlayerMsg msg) {
TXSubtitleRenderModel renderModel = new TXSubtitleRenderModel();
if (null != msg.getCanvasWidth()) {
renderModel.canvasWidth = msg.getCanvasWidth().intValue();
}
if (null != msg.getCanvasHeight()) {
renderModel.canvasHeight = msg.getCanvasHeight().intValue();
}
renderModel.familyName = msg.getFamilyName();
if (null != msg.getFontSize()) {
renderModel.fontSize = msg.getFontSize().floatValue();
}
if (null != msg.getFontScale()) {
renderModel.fontScale = msg.getFontScale().floatValue();
}
if (null != msg.getFontColor()) {
renderModel.fontColor = msg.getFontColor().intValue();
}
if (null != msg.getIsBondFontStyle()) {
renderModel.isBondFontStyle = msg.getIsBondFontStyle();
}
if (null != msg.getOutlineWidth()) {
renderModel.outlineWidth = msg.getOutlineWidth().floatValue();
}
if (null != msg.getOutlineColor()) {
renderModel.outlineColor = msg.getOutlineColor().intValue();
}
if (null != msg.getLineSpace()) {
renderModel.lineSpace = msg.getLineSpace().floatValue();
}
if (null != msg.getStartMargin()) {
renderModel.startMargin = msg.getStartMargin().floatValue();
}
if (null != msg.getEndMargin()) {
renderModel.endMargin = msg.getEndMargin().floatValue();
}
if (null != msg.getVerticalMargin()) {
renderModel.verticalMargin = msg.getVerticalMargin().floatValue();
}
return renderModel;
}
private static boolean intIsNotEmpty(Integer value) { private static boolean intIsNotEmpty(Integer value) {
return null != value && value > 0; return null != value && value > 0;
} }
......
...@@ -14,7 +14,10 @@ import com.tencent.rtmp.TXBitrateItem; ...@@ -14,7 +14,10 @@ import com.tencent.rtmp.TXBitrateItem;
import com.tencent.rtmp.TXImageSprite; import com.tencent.rtmp.TXImageSprite;
import com.tencent.rtmp.TXLiveConstants; import com.tencent.rtmp.TXLiveConstants;
import com.tencent.rtmp.TXPlayInfoParams; import com.tencent.rtmp.TXPlayInfoParams;
import com.tencent.rtmp.TXPlayerDrmBuilder;
import com.tencent.rtmp.TXTrackInfo;
import com.tencent.rtmp.TXVodConstants; import com.tencent.rtmp.TXVodConstants;
import com.tencent.rtmp.TXVodDef;
import com.tencent.rtmp.TXVodPlayConfig; import com.tencent.rtmp.TXVodPlayConfig;
import com.tencent.rtmp.TXVodPlayer; import com.tencent.rtmp.TXVodPlayer;
import com.tencent.vod.flutter.messages.FtxMessages; import com.tencent.vod.flutter.messages.FtxMessages;
...@@ -36,17 +39,20 @@ import com.tencent.vod.flutter.model.TXPipResult; ...@@ -36,17 +39,20 @@ import com.tencent.vod.flutter.model.TXPipResult;
import com.tencent.vod.flutter.model.TXVideoModel; import com.tencent.vod.flutter.model.TXVideoModel;
import com.tencent.vod.flutter.tools.TXCommonUtil; import com.tencent.vod.flutter.tools.TXCommonUtil;
import io.flutter.embedding.engine.plugins.FlutterPlugin;
import io.flutter.plugin.common.EventChannel;
import io.flutter.view.TextureRegistry;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections;
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.Objects; import java.util.Objects;
import io.flutter.embedding.engine.plugins.FlutterPlugin;
import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding;
import io.flutter.plugin.common.EventChannel;
import io.flutter.view.TextureRegistry;
/** /**
* vodPlayer plugin processor * vodPlayer plugin processor
*/ */
...@@ -55,6 +61,7 @@ public class FTXVodPlayer extends FTXBasePlayer implements ITXVodPlayListener, F ...@@ -55,6 +61,7 @@ public class FTXVodPlayer extends FTXBasePlayer implements ITXVodPlayListener, F
private static final String TAG = "FTXVodPlayer"; private static final String TAG = "FTXVodPlayer";
private FlutterPlugin.FlutterPluginBinding mFlutterPluginBinding; private FlutterPlugin.FlutterPluginBinding mFlutterPluginBinding;
private ActivityPluginBinding mActivityPluginBinding;
private final EventChannel mEventChannel; private final EventChannel mEventChannel;
private final EventChannel mNetChannel; private final EventChannel mNetChannel;
...@@ -99,7 +106,8 @@ public class FTXVodPlayer extends FTXBasePlayer implements ITXVodPlayListener, F ...@@ -99,7 +106,8 @@ public class FTXVodPlayer extends FTXBasePlayer implements ITXVodPlayListener, F
* *
* 点播播放器 * 点播播放器
*/ */
public FTXVodPlayer(FlutterPlugin.FlutterPluginBinding flutterPluginBinding, FTXPIPManager pipManager) { public FTXVodPlayer(FlutterPlugin.FlutterPluginBinding flutterPluginBinding,
ActivityPluginBinding activityPluginBinding, FTXPIPManager pipManager) {
super(); super();
mPipManager = pipManager; mPipManager = pipManager;
mFlutterPluginBinding = flutterPluginBinding; mFlutterPluginBinding = flutterPluginBinding;
...@@ -250,6 +258,17 @@ public class FTXVodPlayer extends FTXBasePlayer implements ITXVodPlayListener, F ...@@ -250,6 +258,17 @@ public class FTXVodPlayer extends FTXBasePlayer implements ITXVodPlayListener, F
mVodPlayer.setVodListener(this); mVodPlayer.setVodListener(this);
// prevent config null exception // prevent config null exception
mVodPlayer.setConfig(new TXVodPlayConfig()); mVodPlayer.setConfig(new TXVodPlayConfig());
mVodPlayer.setVodSubtitleDataListener(new ITXVodPlayListener.ITXVodSubtitleDataListener() {
@Override
public void onSubtitleData(TXVodDef.TXVodSubtitleData sub) {
Bundle bundle = new Bundle();
bundle.putString(FTXEvent.EXTRA_SUBTITLE_DATA, sub.subtitleData);
bundle.putLong(FTXEvent.EXTRA_SUBTITLE_START_POSITION_MS, sub.startPositionMs);
bundle.putLong(FTXEvent.EXTRA_SUBTITLE_DURATION_MS, sub.durationMs);
bundle.putLong(FTXEvent.EXTRA_SUBTITLE_TRACK_INDEX, sub.trackIndex);
mEventSink.success(TXCommonUtil.getParams(FTXEvent.EVENT_SUBTITLE_DATA, bundle));
}
});
setPlayer(onlyAudio); setPlayer(onlyAudio);
} }
return mSurfaceTextureEntry == null ? -1 : mSurfaceTextureEntry.id(); return mSurfaceTextureEntry == null ? -1 : mSurfaceTextureEntry.id();
...@@ -377,6 +396,12 @@ public class FTXVodPlayer extends FTXBasePlayer implements ITXVodPlayListener, F ...@@ -377,6 +396,12 @@ public class FTXVodPlayer extends FTXBasePlayer implements ITXVodPlayListener, F
} }
} }
public void seekToPdtTime(long pdtTimeMs) {
if (mVodPlayer != null) {
mVodPlayer.seekToPdtTime(pdtTimeMs);
}
}
void setPlayerRate(float rate) { void setPlayerRate(float rate) {
if (mVodPlayer != null) { if (mVodPlayer != null) {
mVodPlayer.setRate(rate); mVodPlayer.setRate(rate);
...@@ -386,6 +411,13 @@ public class FTXVodPlayer extends FTXBasePlayer implements ITXVodPlayListener, F ...@@ -386,6 +411,13 @@ public class FTXVodPlayer extends FTXBasePlayer implements ITXVodPlayListener, F
void setPlayConfig(FTXVodPlayConfigPlayerMsg config) { void setPlayConfig(FTXVodPlayConfigPlayerMsg config) {
if (mVodPlayer != null) { if (mVodPlayer != null) {
TXVodPlayConfig playConfig = FTXTransformation.transformToVodConfig(config); TXVodPlayConfig playConfig = FTXTransformation.transformToVodConfig(config);
Map<String, Object> map = new HashMap<>();
Map<String, Object> extInfoMap = playConfig.getExtInfoMap();
if (extInfoMap != null && !extInfoMap.isEmpty()) {
map.putAll(extInfoMap);
}
map.put("450", 0);
playConfig.setExtInfo(map);
mVodPlayer.setConfig(playConfig); mVodPlayer.setConfig(playConfig);
} }
} }
...@@ -488,6 +520,20 @@ public class FTXVodPlayer extends FTXBasePlayer implements ITXVodPlayListener, F ...@@ -488,6 +520,20 @@ public class FTXVodPlayer extends FTXBasePlayer implements ITXVodPlayListener, F
startPlayerVodPlayWithParams(appId, fileId, psign); startPlayerVodPlayWithParams(appId, fileId, psign);
} }
@NonNull
@Override
public IntMsg startPlayDrm(@NonNull FtxMessages.TXPlayerDrmMsg params) {
if (null != mVodPlayer) {
TXPlayerDrmBuilder builder = new TXPlayerDrmBuilder(params.getLicenseUrl(), params.getPlayUrl());
if (!TextUtils.isEmpty(params.getDeviceCertificateUrl())) {
builder.setDeviceCertificateUrl(params.getDeviceCertificateUrl());
}
int result = mVodPlayer.startPlayDrm(builder);
return TXCommonUtil.intMsgWith((long) result);
}
return TXCommonUtil.intMsgWith((long) Uninitialized);
}
@Override @Override
public void setAutoPlay(@NonNull BoolPlayerMsg isAutoPlay) { public void setAutoPlay(@NonNull BoolPlayerMsg isAutoPlay) {
if (null != isAutoPlay.getValue()) { if (null != isAutoPlay.getValue()) {
...@@ -539,6 +585,13 @@ public class FTXVodPlayer extends FTXBasePlayer implements ITXVodPlayListener, F ...@@ -539,6 +585,13 @@ public class FTXVodPlayer extends FTXBasePlayer implements ITXVodPlayListener, F
} }
} }
@Override
public void seekToPdtTime(@NonNull IntPlayerMsg pdtTimeMs) {
if (null != pdtTimeMs.getValue()) {
seekToPdtTime(pdtTimeMs.getValue().longValue());
}
}
@Override @Override
public void setRate(@NonNull DoublePlayerMsg rate) { public void setRate(@NonNull DoublePlayerMsg rate) {
if (null != rate.getValue()) { if (null != rate.getValue()) {
...@@ -708,4 +761,74 @@ public class FTXVodPlayer extends FTXBasePlayer implements ITXVodPlayListener, F ...@@ -708,4 +761,74 @@ public class FTXVodPlayer extends FTXBasePlayer implements ITXVodPlayListener, F
} }
return TXCommonUtil.doubleMsgWith(0D); return TXCommonUtil.doubleMsgWith(0D);
} }
@Override
public void addSubtitleSource(@NonNull FtxMessages.SubTitlePlayerMsg playerMsg) {
if (null != mVodPlayer) {
mVodPlayer.addSubtitleSource(playerMsg.getUrl(), playerMsg.getName(), playerMsg.getMimeType());
}
}
@NonNull
@Override
public ListMsg getSubtitleTrackInfo(@NonNull PlayerMsg playerMsg) {
if (null != mVodPlayer) {
List<TXTrackInfo> trackInfoList = mVodPlayer.getSubtitleTrackInfo();
List<Object> json = new ArrayList<>();
for (TXTrackInfo trackInfo : trackInfoList) {
Map<Object, Object> map = new HashMap<>();
map.put("trackType", trackInfo.trackType);
map.put("trackIndex", trackInfo.trackIndex);
map.put("name", trackInfo.name);
map.put("isSelected", trackInfo.isSelected);
map.put("isExclusive", trackInfo.isExclusive);
map.put("isInternal", trackInfo.isInternal);
json.add(map);
}
return TXCommonUtil.listMsgWith(json);
}
return TXCommonUtil.listMsgWith(Collections.emptyList());
}
@NonNull
@Override
public ListMsg getAudioTrackInfo(@NonNull PlayerMsg playerMsg) {
if (null != mVodPlayer) {
List<TXTrackInfo> trackInfoList = mVodPlayer.getAudioTrackInfo();
List<Object> json = new ArrayList<>();
for (TXTrackInfo trackInfo : trackInfoList) {
Map<Object, Object> map = new HashMap<>();
map.put("trackType", trackInfo.trackType);
map.put("trackIndex", trackInfo.trackIndex);
map.put("name", trackInfo.name);
map.put("isSelected", trackInfo.isSelected);
map.put("isExclusive", trackInfo.isExclusive);
map.put("isInternal", trackInfo.isInternal);
json.add(map);
}
return TXCommonUtil.listMsgWith(json);
}
return TXCommonUtil.listMsgWith(Collections.emptyList());
}
@Override
public void selectTrack(@NonNull IntPlayerMsg playerMsg) {
if (null != mVodPlayer && null != playerMsg.getValue()) {
mVodPlayer.selectTrack(playerMsg.getValue().intValue());
}
}
@Override
public void deselectTrack(@NonNull IntPlayerMsg playerMsg) {
if (null != mVodPlayer && null != playerMsg.getValue()) {
mVodPlayer.deselectTrack(playerMsg.getValue().intValue());
}
}
@Override
public void setSubtitleStyle(@NonNull FtxMessages.SubTitleRenderModelPlayerMsg playerMsg) {
if (null != mVodPlayer) {
mVodPlayer.setSubtitleStyle(FTXTransformation.transToTitleRenderModel(playerMsg));
}
}
} }
...@@ -219,7 +219,7 @@ public class SuperPlayerPlugin implements FlutterPlugin, ActivityAware, ...@@ -219,7 +219,7 @@ public class SuperPlayerPlugin implements FlutterPlugin, ActivityAware,
@NonNull @NonNull
@Override @Override
public PlayerMsg createVodPlayer() { public PlayerMsg createVodPlayer() {
FTXVodPlayer player = new FTXVodPlayer(mFlutterPluginBinding, mTxPipManager); FTXVodPlayer player = new FTXVodPlayer(mFlutterPluginBinding, mActivityPluginBinding, mTxPipManager);
int playerId = player.getPlayerId(); int playerId = player.getPlayerId();
mPlayers.append(playerId, player); mPlayers.append(playerId, player);
PlayerMsg playerMsg = new PlayerMsg(); PlayerMsg playerMsg = new PlayerMsg();
......
...@@ -72,6 +72,16 @@ public class FTXVodPlayerDispatcher implements FtxMessages.TXFlutterVodPlayerApi ...@@ -72,6 +72,16 @@ public class FTXVodPlayerDispatcher implements FtxMessages.TXFlutterVodPlayerApi
} }
} }
@NonNull
@Override
public IntMsg startPlayDrm(@NonNull FtxMessages.TXPlayerDrmMsg params) {
TXFlutterVodPlayerApi api = getPlayer(params.getPlayerId());
if (null != api) {
return api.startPlayDrm(params);
}
return null;
}
@Override @Override
public void setAutoPlay(@NonNull BoolPlayerMsg isAutoPlay) { public void setAutoPlay(@NonNull BoolPlayerMsg isAutoPlay) {
TXFlutterVodPlayerApi api = getPlayer(isAutoPlay.getPlayerId()); TXFlutterVodPlayerApi api = getPlayer(isAutoPlay.getPlayerId());
...@@ -140,6 +150,14 @@ public class FTXVodPlayerDispatcher implements FtxMessages.TXFlutterVodPlayerApi ...@@ -140,6 +150,14 @@ public class FTXVodPlayerDispatcher implements FtxMessages.TXFlutterVodPlayerApi
} }
} }
@Override
public void seekToPdtTime(@NonNull IntPlayerMsg pdtTimeMs) {
TXFlutterVodPlayerApi api = getPlayer(pdtTimeMs.getPlayerId());
if (null != api) {
api.seekToPdtTime(pdtTimeMs);
}
}
@Override @Override
public void setRate(@NonNull DoublePlayerMsg rate) { public void setRate(@NonNull DoublePlayerMsg rate) {
TXFlutterVodPlayerApi api = getPlayer(rate.getPlayerId()); TXFlutterVodPlayerApi api = getPlayer(rate.getPlayerId());
...@@ -333,4 +351,56 @@ public class FTXVodPlayerDispatcher implements FtxMessages.TXFlutterVodPlayerApi ...@@ -333,4 +351,56 @@ public class FTXVodPlayerDispatcher implements FtxMessages.TXFlutterVodPlayerApi
} }
return null; return null;
} }
@Override
public void addSubtitleSource(@NonNull FtxMessages.SubTitlePlayerMsg playerMsg) {
TXFlutterVodPlayerApi api = getPlayer(playerMsg.getPlayerId());
if (null != api) {
api.addSubtitleSource(playerMsg);
}
}
@NonNull
@Override
public ListMsg getSubtitleTrackInfo(@NonNull PlayerMsg playerMsg) {
TXFlutterVodPlayerApi api = getPlayer(playerMsg.getPlayerId());
if (null != api) {
return api.getSubtitleTrackInfo(playerMsg);
}
return null;
}
@NonNull
@Override
public ListMsg getAudioTrackInfo(@NonNull PlayerMsg playerMsg) {
TXFlutterVodPlayerApi api = getPlayer(playerMsg.getPlayerId());
if (null != api) {
return api.getAudioTrackInfo(playerMsg);
}
return null;
}
@Override
public void selectTrack(@NonNull IntPlayerMsg playerMsg) {
TXFlutterVodPlayerApi api = getPlayer(playerMsg.getPlayerId());
if (null != api) {
api.selectTrack(playerMsg);
}
}
@Override
public void deselectTrack(@NonNull IntPlayerMsg playerMsg) {
TXFlutterVodPlayerApi api = getPlayer(playerMsg.getPlayerId());
if (null != api) {
api.deselectTrack(playerMsg);
}
}
@Override
public void setSubtitleStyle(@NonNull FtxMessages.SubTitleRenderModelPlayerMsg playerMsg) {
TXFlutterVodPlayerApi api = getPlayer(playerMsg.getPlayerId());
if (null != api) {
api.setSubtitleStyle(playerMsg);
}
}
} }
...@@ -1262,6 +1262,113 @@ Future<void> exitPictureInPictureMode() async; ...@@ -1262,6 +1262,113 @@ Future<void> exitPictureInPictureMode() async;
### addSubtitleSource
**说明**
添加外挂字幕。
注意:此功能需要播放器高级版 11.7 版本开始支持。
**接口**
```dart
Future<void> addSubtitleSource(String url, String name, {String? mimeType}) async;
```
**参数说明**
| 参数名 | 类型 | 描述 |
| -------- | ------ | ------------------------------------------------------------ |
| url | String | 字幕url |
| name | String | 字幕名称 |
| mimeType | String | 字幕类型,支持SRT(TXVodPlayEvent.VOD_PLAY_MIMETYPE_TEXT_SRT)和VVT(TXVodPlayEvent.VOD_PLAY_MIMETYPE_TEXT_VTT)格式 |
### getSubtitleTrackInfo
**说明**
返回字幕轨道信息列表。
注意:此功能需要播放器高级版 11.7 版本开始支持。
**接口**
```dart
Future<List<TXTrackInfo>> getSubtitleTrackInfo() async;
```
**参数说明**
TXTrackInfo类:
| 参数名 | 类型 | 描述 |
| ----------- | ------ | ------------------------------------------------------------ |
| trackType | int | 轨道类型。取值有:<br />视频轨:TX_VOD_MEDIA_TRACK_TYPE_VIDEO = 1<br />音频轨: TX_VOD_MEDIA_TRACK_TYPE_AUDIO = 2<br />字幕轨:TX_VOD_MEDIA_TRACK_TYPE_SUBTITLE = 3 |
| trackIndex | int | 轨道index |
| name | String | 轨道名字 |
| isSelected | bool | 当前轨道是否被选中 |
| isExclusive | bool | 如果是true,该类型轨道每个时刻只有一条能被选中,如果是false,该类型轨道可以同时选中多条 |
| isInternal | bool | 当前的轨道是否是内部原始轨道 |
### getAudioTrackInfo
**说明**
返回字幕轨道信息列表。
注意:此功能需要播放器高级版 11.7 版本开始支持。
**接口**
```dart
Future<List<TXTrackInfo>> getAudioTrackInfo() async;
```
**参数说明**
参考TXTrackInfo类
### selectTrack
**说明**
选择轨道。
注意:此功能需要播放器高级版 11.7 版本开始支持。
**接口**
```dart
Future<void> selectTrack(int trackIndex) async;
```
**参数说明**
| 参数名 | 类型 | 描述 |
| ---------- | ---- | ------------------------------------------------------------ |
| trackIndex | int | 轨道index,trackIndex 轨道index,通过[TXTrackInfo]的trackIndex获取。 |
### deselectTrack
**说明**
取消选择轨道。
注意:此功能需要播放器高级版 11.7 版本开始支持。
**接口**
```dart
Future<void> deselectTrack(int trackIndex) async;
```
**参数说明**
| 参数名 | 类型 | 描述 |
| ---------- | ---- | ------------------------------------------------------------ |
| trackIndex | int | 轨道index,trackIndex 轨道index,通过[TXTrackInfo]的trackIndex获取。 |
## FTXVodPlayConfig类 ## FTXVodPlayConfig类
#### 属性配置说明 #### 属性配置说明
...@@ -1288,6 +1395,7 @@ Future<void> exitPictureInPictureMode() async; ...@@ -1288,6 +1395,7 @@ Future<void> exitPictureInPictureMode() async;
| extInfoMap | Map | 一些不必周知的特殊配置| | extInfoMap | Map | 一些不必周知的特殊配置|
| enableRenderProcess | bool | 是否允许加载后渲染后处理服务,默认开启,开启后超分插件如果存在,默认加载| | enableRenderProcess | bool | 是否允许加载后渲染后处理服务,默认开启,开启后超分插件如果存在,默认加载|
| preferredResolution | int | 优先播放的分辨率,preferredResolution = width * height| | preferredResolution | int | 优先播放的分辨率,preferredResolution = width * height|
| mediaType | int | 设置媒资类型, 默认为 auto 类型。可选值有:<br/>TXVodConstants#MEDIA_TYPE_AUTO,AUTO 类型(默认值,自适应码率播放暂不支持)。<br/>TXVodConstants#MEDIA_TYPE_HLS_VOD,HLS 点播媒资。<br/>TXVodConstants#MEDIA_TYPE_HLS_LIVE,HLS 直播媒资。<br/>TXVodConstants#MEDIA_TYPE_FILE_VOD,MP4 等通用文件点播媒资(从 11.7 版本开始支持)。<br/>TXVodConstants#MEDIA_TYPE_DASH_VOD,DASH 点播媒资(从 11.7 版本开始支持)。 |
## TXLivePlayerController类 ## TXLivePlayerController类
......
...@@ -782,4 +782,15 @@ SuperPlayerPlugin.instance.onExtraEventBroadcast.listen((event) { ...@@ -782,4 +782,15 @@ SuperPlayerPlugin.instance.onExtraEventBroadcast.listen((event) {
}); });
``` ```
### 5、外挂字幕
通过`SuperPlayerModel``subtitleSources`可以添加外挂字幕。
示例如下:
```dart
model.subtitleSources.add(FSubtitleSourceModel()
..name = "ex-cn-srt"
..url = "https://mediacloud-76607.gzc.vod.tencent-cloud.com/DemoResource/TED-CN.srt"
..mimeType = FSubtitleSourceModel.VOD_PLAY_MIMETYPE_TEXT_SRT);
```
目前支持VTT和SRT格式
\ No newline at end of file
...@@ -170,6 +170,17 @@ double time = 600; // double,单位为 秒 ...@@ -170,6 +170,17 @@ double time = 600; // double,单位为 秒
_controller.seek(time); _controller.seek(time);
``` ```
#### Seek 到视频流指定 PDT 时间点
跳转到视频流指定 PDT(Program Date Time )时间点,可实现视频快进、快退、进度条跳转等功能,目前只支持 HLS 视频格式。
注意:播放器高级版 11.7 版本开始支持。
```dart
int pdtTimeMs = 600; // 单位为 毫秒
_controller.seekToPdtTime(time);
```
#### 从指定时间开始播放 #### 从指定时间开始播放
首次调用 startVodPlay 之前,支持从指定时间开始播放。 首次调用 startVodPlay 之前,支持从指定时间开始播放。
...@@ -247,7 +258,7 @@ _controller.setConfig(playConfig); ...@@ -247,7 +258,7 @@ _controller.setConfig(playConfig);
### 10、清晰度设置 ### 10、清晰度设置
SDK 支持 HLS 的多码率格式,方便用户切换不同码率的播放流,从而达到播放不同清晰的目标。在收到 PLAY_EVT_PLAY_BEGIN 事件后,可以通过下面方法进行清晰度设置。 SDK 支持 HLS 的多码率格式,方便用户切换不同码率的播放流,从而达到播放不同清晰的目标。可以通过下面方法进行清晰度设置。
```dart ```dart
List _supportedBitrates = (await _controller.getSupportedBitrates())!;; //获取多码率数组 List _supportedBitrates = (await _controller.getSupportedBitrates())!;; //获取多码率数组
...@@ -259,7 +270,7 @@ _controller.setBitrateIndex(index); // 切换码率到想要的清晰度 ...@@ -259,7 +270,7 @@ _controller.setBitrateIndex(index); // 切换码率到想要的清晰度
### 11、码流自适应 ### 11、码流自适应
SDK 支持 HLS 的多码流自适应,开启相关能力后播放器能够根据当前带宽,动态选择最合适的码率播放。在收到 PLAY_EVT_PLAY_BEGIN 事件后,可以通过下面方法开启码流自适应。 SDK 支持 HLS 的多码流自适应,开启相关能力后播放器能够根据当前带宽,动态选择最合适的码率播放。可以通过下面方法开启码流自适应。
```dart ```dart
_controller.setBitrateIndex(-1); //index 参数传入-1 _controller.setBitrateIndex(-1); //index 参数传入-1
...@@ -353,7 +364,92 @@ SuperPlayerPlugin.setGlobalMaxCacheSize(200); ...@@ -353,7 +364,92 @@ SuperPlayerPlugin.setGlobalMaxCacheSize(200);
SuperPlayerPlugin.setGlobalCacheFolderPath("postfixPath"); SuperPlayerPlugin.setGlobalCacheFolderPath("postfixPath");
``` ```
## 高级功能使用 ### 18、外挂字幕
注意:此功能需要播放器高级版 11.7 版本开始支持。
播放器高级版 SDK 支持添加和切换外挂字幕,现已支持 SRT 和 VTT 这两种格式的字幕。
最佳实践:建议在 `startVodPlay `之前添加字幕和配置字幕样式,在收到` VOD_PLAY_EVT_VOD_PLAY_PREPARED `事件后,调用 selectTrack 选择字幕。 添加字幕后,并不会主动加载字幕, 调用 `selectTrack` 后,才会加载字幕,字幕选择成功会有 `VOD_PLAY_EVT_SELECT_TRACK_COMPLETE` 事件回调。选择字幕后,字幕文本内容会通过 `TXVodPlayEvent.EVENT_SUBTITLE_DATA`
事件回调,字幕的显示在需要业务方自行处理。
#### 步骤 1:添加外挂字幕
```dart
// 添加外挂字幕,传入 字幕url, 字幕名称, 字幕类型, 建议在启动播放器前添加
controller.addSubtitleSource("https://mediacloud-76607.gzc.vod.tencent-cloud.com/DemoResource/subtitleVTT.vtt", "subtitleName", TXVodPlayEvent.VOD_PLAY_MIMETYPE_TEXT_SRT)
// 开始播放视频后,监听字幕文本内容回调
_controller.onPlayerEventBroadcast.listen((event) async {
if(event["event"] == TXVodPlayEvent.EVENT_SUBTITLE_DATA) {
// 字幕文本内容,可用于显示
String subtitleDataStr = event[TXVodPlayEvent.EXTRA_SUBTITLE_DATA] ?? "";
}
});
```
#### 步骤2:播放后切换字幕
```dart
// 开始播放视频后,选中添加的外挂字幕, 在收到 VOD_PLAY_EVT_VOD_PLAY_PREPARED 事件后调用
_controller.onPlayerEventBroadcast.listen((event) async {
if(event["event"] == TXVodPlayEvent.PLAY_EVT_VOD_PLAY_PREPARED) {
List<TXTrackInfo> trackInfoList = await _vodPlayerController.getSubtitleTrackInfo();
for (TXTrackInfo tempInfo in trackInfoList) {
if(tempInfo.name == "subtitleName") {
// 选中字幕
_vodPlayerController.selectTrack(tempInfo.trackIndex);
} else {
// 其它字幕不需要的话, 进行deselectTrack
_vodPlayerController.deselectTrack(tempInfo.trackIndex);
}
}
}
});
// 如果需要,可以监听轨道切换消息
_controller.onPlayerEventBroadcast.listen((event) async {
if(event["event"] == TXVodPlayEvent.VOD_PLAY_EVT_SELECT_TRACK_COMPLETE) {
int trackIndex = event[TXVodPlayEvent.EVT_KEY_SELECT_TRACK_INDEX];
int errorCode = event[TXVodPlayEvent.EVT_KEY_SELECT_TRACK_ERROR_CODE];
}
});
```
#### 步骤3:监听字幕文本内容
```dart
// 开始播放视频后,监听字幕文本内容回调
_controller.onPlayerEventBroadcast.listen((event) async {
if(event["event"] == TXVodPlayEvent.EVENT_SUBTITLE_DATA) {
// 字幕文本内容,可用于显示
String subtitleDataStr = event[TXVodPlayEvent.EXTRA_SUBTITLE_DATA] ?? "";
}
});
```
### 19、多音轨切换
注意:此功能需要播放器高级版 11.7 版本开始支持。
播放器高级版 SDK 支持切换视频内置的多音轨。用法参见如下代码:
```dart
// 返回音频轨道信息列表
List<TXTrackInfo> trackInfoList = await _vodPlayerController.getAudioTrackInfo();
for (TXTrackInfo tempInfo in trackInfoList) {
if(tempInfo.trackIndex == 0) {
// 通过判断 trackIndex 或者 name 切换到需要的音轨
_vodPlayerController.selectTrack(tempInfo.trackIndex);
} else {
// 不需要的音轨进行 deselectTrack
_vodPlayerController.deselectTrack(tempInfo.trackIndex);
}
}
```
## 进阶功能使用
### 1、视频预播放 ### 1、视频预播放
...@@ -656,7 +752,7 @@ config.maxBufferSize = 50; // 最大预加载大小,单位 MB ...@@ -656,7 +752,7 @@ config.maxBufferSize = 50; // 最大预加载大小,单位 MB
_controller.setPlayConfig(config); _controller.setPlayConfig(config);
``` ```
##### 启播指定分辨率 ##### 启播指定分辨率
播放 HLS 的多码率视频源,如果你提前知道视频流的分辨率信息,可以在启播前优先指定播放的视频分辨率。 播放器会查找小于或等于偏好分辨率的流进行启播,启播后没有必要再通过 setBitrateIndex 切换到需要的码流。 播放 HLS 的多码率视频源,如果你提前知道视频流的分辨率信息,可以在启播前优先指定播放的视频分辨率。 播放器会查找小于或等于偏好分辨率的流进行启播,启播后没有必要再通过 setBitrateIndex 切换到需要的码流。
...@@ -667,6 +763,17 @@ config.preferredResolution = 720 * 1280; ...@@ -667,6 +763,17 @@ config.preferredResolution = 720 * 1280;
_controller.setPlayConfig(config); _controller.setPlayConfig(config);
``` ```
**启播前指定媒资类型**
当提前知道播放的媒资类型时,可以通过配置`FTXVodPlayConfig#mediaType`减少播放器SDK内部播放类型探测,提升启播速度。
```dart
FTXVodPlayConfig config = FTXVodPlayConfig();
config.mediaType = TXVodPlayEvent.MEDIA_TYPE_FILE_VOD; // 用于提升MP4启播速度
config.mediaType = TXVodPlayEvent.MEDIA_TYPE_HLS_VOD; // // 用于提升HLS启播速度
_controller.setPlayConfig(config);
```
##### 设置播放进度回调时间间隔 ##### 设置播放进度回调时间间隔
```dart ```dart
......
...@@ -15,11 +15,13 @@ ...@@ -15,11 +15,13 @@
腾讯云视立方 Flutter 播放器项目的地址是 [Player Flutter](https://github.com/LiteAVSDK/Player_Flutter) 腾讯云视立方 Flutter 播放器项目的地址是 [Player Flutter](https://github.com/LiteAVSDK/Player_Flutter)
注意:运行该 demo 的时候,需要在 demo_config 中设置自己的播放器 license,并在 Android 和 iOS 配置中,将包名和 bundleId 修改为自己签名的包名和 bundleId。
## 快速集成 ## 快速集成
### 在项目的 pubspec.yaml 中添加依赖 ### 在项目的 pubspec.yaml 中添加依赖
支持基于LiteAVSDK Player Professional 版本集成,你可以根据项目需要进行集成。 支持基于LiteAVSDK Player 、Player_Premium 或 Professional 版本集成,你可以根据项目需要进行集成。
1. 集成 LiteAVSDK_Player 版本最新版本,默认情况下也是集成此版本。在`pubspec.yaml`中增加配置: 1. 集成 LiteAVSDK_Player 版本最新版本,默认情况下也是集成此版本。在`pubspec.yaml`中增加配置:
```yaml ```yaml
...@@ -37,6 +39,16 @@ super_player: ...@@ -37,6 +39,16 @@ super_player:
path: Flutter path: Flutter
ref: Professional ref: Professional
``` ```
集成 LiteAVSDK_Player_Premium 最新版本,则`pubspec.yaml`中配置改为:
```yaml
super_player:
git:
url: https://github.com/LiteAVSDK/Player_Flutter
path: Flutter
ref: Player_Premium
```
如果需要集成指定播放器版本的SDK,可以指定通过ref 依赖的tag来指定到对应版本,如下所示: 如果需要集成指定播放器版本的SDK,可以指定通过ref 依赖的tag来指定到对应版本,如下所示:
```yaml ```yaml
...@@ -238,6 +250,7 @@ pod update ...@@ -238,6 +250,7 @@ pod update
3. 需要同时使用多个播放器实例的时候,频繁切换播放视频,画面呈现模糊。 3. 需要同时使用多个播放器实例的时候,频繁切换播放视频,画面呈现模糊。
**解决方法**: **解决方法**:
​ 在每个播放器组件容器销毁的时候,调用播放器的`dispose`方法,将播放器释放 ​ 在每个播放器组件容器销毁的时候,调用播放器的`dispose`方法,将播放器释放
......
...@@ -394,6 +394,36 @@ class _DemoSuperPlayerState extends State<DemoSuperPlayer> with TXPipPlayerResto ...@@ -394,6 +394,36 @@ class _DemoSuperPlayerState extends State<DemoSuperPlayer> with TXPipPlayerResto
model.title = AppLocals.current.playerVideoTitleAchievement; model.title = AppLocals.current.playerVideoTitleAchievement;
models.add(model); models.add(model);
model = SuperPlayerModel();
model.coverUrl = "http://1500005830.vod2.myqcloud.com/43843ec0vodtranscq1500005830/dc455d1d387702306937256938/coverBySnapshot_10_0.jpg";
model.videoURL = "http://1500005830.vod2.myqcloud.com/43843ec0vodtranscq1500005830/dc455d1d387702306937256938/adp.10.m3u8";
model.playAction = playAction;
model.isEnableDownload = false;
model.title = "Multi-subtitle video";
// add external subtitle
model.subtitleSources.add(FSubtitleSourceModel()
..name = "ex-cn-srt"
..url = "https://mediacloud-76607.gzc.vod.tencent-cloud.com/DemoResource/TED-CN.srt"
..mimeType = TXVodPlayEvent.VOD_PLAY_MIMETYPE_TEXT_SRT);
model.subtitleSources.add(FSubtitleSourceModel()
..name = "ex-in-srt"
..url = "https://mediacloud-76607.gzc.vod.tencent-cloud.com/DemoResource/TED-IN.srt"
..mimeType = TXVodPlayEvent.VOD_PLAY_MIMETYPE_TEXT_SRT);
model.subtitleSources.add(FSubtitleSourceModel()
..name = "ex-en-vtt"
..url = "https://mediacloud-76607.gzc.vod.tencent-cloud.com/DemoResource/TED-EN.vtt"
..mimeType = TXVodPlayEvent.VOD_PLAY_MIMETYPE_TEXT_VTT);
models.add(model);
model = SuperPlayerModel();
model.coverUrl = "http://1500005830.vod2.myqcloud.com/6c9a5118vodcq1500005830/3a76d6ac387702303793151471/387702307093360124.png";
model.videoURL = "http://1500005830.vod2.myqcloud.com/6c9a5118vodcq1500005830/3a76d6ac387702303793151471/iP3rnDdxMH4A.mov";
model.playAction = playAction;
model.isEnableDownload = false;
model.title = "Multi-audio track video";
models.add(model);
List<Future<void>> requestList = []; List<Future<void>> requestList = [];
for (SuperPlayerModel tempModel in models) { for (SuperPlayerModel tempModel in models) {
requestList.add(loader.getVideoData(tempModel, (_) {})); requestList.add(loader.getVideoData(tempModel, (_) {}));
...@@ -403,7 +433,7 @@ class _DemoSuperPlayerState extends State<DemoSuperPlayer> with TXPipPlayerResto ...@@ -403,7 +433,7 @@ class _DemoSuperPlayerState extends State<DemoSuperPlayer> with TXPipPlayerResto
videoModels.clear(); videoModels.clear();
videoModels.addAll(models); videoModels.addAll(models);
if(mounted) { if (mounted) {
setState(() { setState(() {
if (videoModels.isNotEmpty) { if (videoModels.isNotEmpty) {
playVideo(videoModels[0]); playVideo(videoModels[0]);
......
...@@ -4,13 +4,26 @@ ...@@ -4,13 +4,26 @@
#import "FTXPlayerEventSinkQueue.h" #import "FTXPlayerEventSinkQueue.h"
#import "FTXEvent.h" #import "FTXEvent.h"
#import "FTXDownloadManager.h" #import "FTXDownloadManager.h"
#import <TXLiteAVSDK_Player/TXVodPreloadManager.h>
#import <TXLiteAVSDK_Player/TXVodDownloadManager.h>
#import "FTXEvent.h" #import "FTXEvent.h"
#import "TXCommonUtil.h" #import "TXCommonUtil.h"
#import "FtxMessages.h" #import "FtxMessages.h"
#import "TXPredownloadFileHelperDelegate.h" #import "TXPredownloadFileHelperDelegate.h"
#if __has_include(<TXLiteAVSDK_Player/TXVodPreloadManager.h>)
#import <TXLiteAVSDK_Player/TXVodPreloadManager.h>
#import <TXLiteAVSDK_Player/TXVodDownloadManager.h>
#elif __has_include(<TXLiteAVSDK_Player_Premium/TXVodPreloadManager.h>)
#import <TXLiteAVSDK_Player_Premium/TXVodPreloadManager.h>
#import <TXLiteAVSDK_Player_Premium/TXVodDownloadManager.h>
#elif __has_include(<TXLiteAVSDK_Professional/TXVodPreloadManager.h>)
#import <TXLiteAVSDK_Professional/TXVodPreloadManager.h>
#import <TXLiteAVSDK_Professional/TXVodDownloadManager.h>
#else
#import <TXVodPreloadManager.h>
#import <TXVodDownloadManager.h>
#endif
@interface FTXDownloadManager ()<FlutterStreamHandler, TXVodPreloadManagerDelegate, TXVodDownloadDelegate, TXFlutterDownloadApi> @interface FTXDownloadManager ()<FlutterStreamHandler, TXVodPreloadManagerDelegate, TXVodDownloadDelegate, TXFlutterDownloadApi>
@property (nonatomic, strong) dispatch_queue_t mPreloadQueue; @property (nonatomic, strong) dispatch_queue_t mPreloadQueue;
......
...@@ -112,5 +112,14 @@ ...@@ -112,5 +112,14 @@
#define EVENT_REASON "reason" #define EVENT_REASON "reason"
#define EVENT_HOST_NAME "hostName" #define EVENT_HOST_NAME "hostName"
#define EVENT_IPS "ips" #define EVENT_IPS "ips"
// subtitle data event
// 回调 SubtitleData 事件id
#define EVENT_SUBTITLE_DATA 601
// subtitle data extra key
// 回调 SubtitleData 事件对应的key
#define EXTRA_SUBTITLE_DATA @"subtitleData"
#define EXTRA_SUBTITLE_START_POSITION_MS @"startPositionMs"
#define EXTRA_SUBTITLE_DURATION_MS @"durationMs"
#define EXTRA_SUBTITLE_TRACK_INDEX @"trackIndex"
#endif // SUPERPLAYER_FLUTTER_IOS_CLASSES_FTXEVENT_H_ #endif // SUPERPLAYER_FLUTTER_IOS_CLASSES_FTXEVENT_H_
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
#import "FTXLivePlayer.h" #import "FTXLivePlayer.h"
#import "FTXPlayerEventSinkQueue.h" #import "FTXPlayerEventSinkQueue.h"
#import "FTXTransformation.h" #import "FTXTransformation.h"
#import <TXLiteAVSDK_Player/TXLiteAVSDK.h> #import "FTXLiteAVSDKHeader.h"
#import <Flutter/Flutter.h> #import <Flutter/Flutter.h>
#import <stdatomic.h> #import <stdatomic.h>
#import <libkern/OSAtomic.h> #import <libkern/OSAtomic.h>
......
...@@ -3,10 +3,11 @@ ...@@ -3,10 +3,11 @@
#define SUPERPLAYER_FLUTTER_IOS_CLASSES_FTXTRANSFORMATION_H_ #define SUPERPLAYER_FLUTTER_IOS_CLASSES_FTXTRANSFORMATION_H_
#import <Foundation/Foundation.h> #import <Foundation/Foundation.h>
#import <TXLiteAVSDK_Player/TXLiteAVSDK.h> #import "FTXLiteAVSDKHeader.h"
@class FTXVodPlayConfigPlayerMsg; @class FTXVodPlayConfigPlayerMsg;
@class FTXLivePlayConfigPlayerMsg; @class FTXLivePlayConfigPlayerMsg;
@class SubTitleRenderModelPlayerMsg;
static NSString* cacheFolder = nil; static NSString* cacheFolder = nil;
static int maxCacheItems = -1; static int maxCacheItems = -1;
...@@ -16,6 +17,8 @@ static int maxCacheItems = -1; ...@@ -16,6 +17,8 @@ static int maxCacheItems = -1;
+ (TXLivePlayConfig *)transformMsgToLiveConfig:(FTXLivePlayConfigPlayerMsg *)msg; + (TXLivePlayConfig *)transformMsgToLiveConfig:(FTXLivePlayConfigPlayerMsg *)msg;
+ (TXPlayerSubtitleRenderModel *)transformToTitleRenderModel:(SubTitleRenderModelPlayerMsg *)msg;
@end @end
#endif // SUPERPLAYER_FLUTTER_IOS_CLASSES_FTXTRANSFORMATION_H_ #endif // SUPERPLAYER_FLUTTER_IOS_CLASSES_FTXTRANSFORMATION_H_
...@@ -20,6 +20,7 @@ ...@@ -20,6 +20,7 @@
config.nextStartPlayBufferTime = msg.nextStartPlayBufferTime.intValue; config.nextStartPlayBufferTime = msg.nextStartPlayBufferTime.intValue;
config.enableRenderProcess = msg.enableRenderProcess.boolValue; config.enableRenderProcess = msg.enableRenderProcess.boolValue;
config.preferredResolution = msg.preferredResolution.longValue; config.preferredResolution = msg.preferredResolution.longValue;
config.mediaType = msg.mediaType.intValue;
NSTimeInterval progressInerval = msg.progressInterval.intValue / 1000.0; NSTimeInterval progressInerval = msg.progressInterval.intValue / 1000.0;
if(progressInerval > 0) { if(progressInerval > 0) {
config.progressInterval = progressInerval; config.progressInterval = progressInerval;
...@@ -47,6 +48,48 @@ ...@@ -47,6 +48,48 @@
return config; return config;
} }
+ (TXPlayerSubtitleRenderModel *)transformToTitleRenderModel:(SubTitleRenderModelPlayerMsg *)msg {
TXPlayerSubtitleRenderModel *model = [[TXPlayerSubtitleRenderModel alloc] init];
if (msg.canvasWidth) {
model.canvasWidth = msg.canvasWidth.intValue;
}
if (msg.canvasHeight) {
model.canvasHeight = msg.canvasHeight.intValue;
}
model.familyName = msg.familyName;
if (msg.fontSize) {
model.fontSize = msg.fontSize.floatValue;
}
if (msg.fontScale) {
model.fontScale = msg.fontScale.floatValue;
}
if (msg.fontColor) {
model.fontColor = msg.fontColor.intValue;
}
if (msg.isBondFontStyle) {
model.isBondFontStyle = msg.isBondFontStyle.boolValue;
}
if (msg.outlineWidth) {
model.outlineWidth = msg.outlineWidth.floatValue;
}
if (msg.outlineColor) {
model.outlineColor = msg.outlineColor.intValue;
}
if (msg.lineSpace) {
model.lineSpace = msg.lineSpace.floatValue;
}
if (msg.startMargin) {
model.startMargin = msg.startMargin.floatValue;
}
if (msg.endMargin) {
model.endMargin = msg.endMargin.floatValue;
}
if (msg.verticalMargin) {
model.verticalMargin = msg.verticalMargin.floatValue;
}
return model;
}
@end @end
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
#import "FTXVodPlayer.h" #import "FTXVodPlayer.h"
#import "FTXPlayerEventSinkQueue.h" #import "FTXPlayerEventSinkQueue.h"
#import "FTXTransformation.h" #import "FTXTransformation.h"
#import <TXLiteAVSDK_Player/TXLiteAVSDK.h> #import "FTXLiteAVSDKHeader.h"
#import <stdatomic.h> #import <stdatomic.h>
#import <libkern/OSAtomic.h> #import <libkern/OSAtomic.h>
#import <Flutter/Flutter.h> #import <Flutter/Flutter.h>
...@@ -160,9 +160,26 @@ static const int CODE_ON_RECEIVE_FIRST_FRAME = 2003; ...@@ -160,9 +160,26 @@ static const int CODE_ON_RECEIVE_FIRST_FRAME = 2003;
if (_txVodPlayer != nil) { if (_txVodPlayer != nil) {
[_txVodPlayer setVideoProcessDelegate:self]; [_txVodPlayer setVideoProcessDelegate:self];
} }
NSMutableDictionary *dic = [[NSMutableDictionary alloc] init];
[dic setObject:@(0xFFFFFFFF) forKey:@"fontColor"];
[dic setObject:@(0) forKey:@"bondFont"];
[dic setObject:@(1) forKey:@"outlineWidth"];
[dic setObject:@(0xFF000000) forKey:@"outlineColor"];
[self setSubtitleStyle:dic];
} }
} }
-(void)setSubtitleStyle:(NSDictionary *)dic{
TXPlayerSubtitleRenderModel *model = [[TXPlayerSubtitleRenderModel alloc] init];
model.canvasWidth = 1920; // 字幕渲染画布的宽
model.canvasHeight = 1080; // 字幕渲染画布的高
model.isBondFontStyle = [dic[@"bondFont"] boolValue]; // 设置字幕字体是否为粗体
model.fontColor = [(NSNumber *)dic[@"fontColor"] unsignedIntValue]; // 设置字幕字体颜色,默认白色
model.outlineWidth = [(NSNumber *)dic[@"outlineWidth"] floatValue]; //描边宽度
model.outlineColor = [(NSNumber *)dic[@"outlineColor"] unsignedIntValue]; //描边颜色
[_txVodPlayer setSubtitleStyle:model];
}
#pragma mark - #pragma mark -
- (NSNumber*)createPlayer:(BOOL)onlyAudio - (NSNumber*)createPlayer:(BOOL)onlyAudio
...@@ -259,6 +276,13 @@ static const int CODE_ON_RECEIVE_FIRST_FRAME = 2003; ...@@ -259,6 +276,13 @@ static const int CODE_ON_RECEIVE_FIRST_FRAME = 2003;
} }
} }
- (void)seekToPdtTimePdtTimeMs:(long long)pdtTimeMs
{
if (_txVodPlayer != nil) {
[_txVodPlayer seekToPdtTime:pdtTimeMs];
}
}
- (void)setRate:(float)rate - (void)setRate:(float)rate
{ {
if (_txVodPlayer != nil) { if (_txVodPlayer != nil) {
...@@ -454,6 +478,23 @@ static const int CODE_ON_RECEIVE_FIRST_FRAME = 2003; ...@@ -454,6 +478,23 @@ static const int CODE_ON_RECEIVE_FIRST_FRAME = 2003;
[_netStatusSink success:param]; [_netStatusSink success:param];
} }
/**
* 字幕数据回调
*
* @param player 当前播放器对象
* @param subtitleData 字幕数据,详细见 TXVodDef.h 文件
*/
- (void)onPlayer:(TXVodPlayer *)player subtitleData:(TXVodSubtitleData *)subtitleData
{
NSMutableDictionary *mutableDic = [[NSMutableDictionary alloc] init];
mutableDic[EXTRA_SUBTITLE_DATA] = subtitleData.subtitleData;
mutableDic[EXTRA_SUBTITLE_START_POSITION_MS] = @(subtitleData.startPositionMs);
mutableDic[EXTRA_SUBTITLE_DURATION_MS] = @(subtitleData.durationMs);
mutableDic[EXTRA_SUBTITLE_TRACK_INDEX] = @(subtitleData.trackIndex);
[_eventSink success:[FTXVodPlayer getParamsWithEvent:EVENT_SUBTITLE_DATA withParams:mutableDic]];
}
#pragma mark - TXVideoCustomProcessDelegate #pragma mark - TXVideoCustomProcessDelegate
/** /**
...@@ -532,7 +573,17 @@ static const int CODE_ON_RECEIVE_FIRST_FRAME = 2003; ...@@ -532,7 +573,17 @@ static const int CODE_ON_RECEIVE_FIRST_FRAME = 2003;
- (void)setPlayerConfig:(FTXVodPlayConfigPlayerMsg *)args - (void)setPlayerConfig:(FTXVodPlayConfigPlayerMsg *)args
{ {
if (_txVodPlayer != nil && args != nil) { if (_txVodPlayer != nil && args != nil) {
_txVodPlayer.config = [FTXTransformation transformMsgToVodConfig:args]; TXVodPlayConfig *vodConfig = [FTXTransformation transformMsgToVodConfig:args];
NSMutableDictionary<NSString *, id> *newExtInfoMap = [NSMutableDictionary dictionary];
NSDictionary *extInfoMap = vodConfig.extInfoMap;
if(extInfoMap != nil && [extInfoMap count] >0){
[newExtInfoMap addEntriesFromDictionary:extInfoMap];
}
[newExtInfoMap setObject:@(0) forKey:@"450"];
[vodConfig setExtInfoMap:newExtInfoMap];
_txVodPlayer.config = vodConfig;
} }
} }
...@@ -906,6 +957,10 @@ BOOL CGImageRefContainsAlpha(CGImageRef imageRef) { ...@@ -906,6 +957,10 @@ BOOL CGImageRefContainsAlpha(CGImageRef imageRef) {
[self seek:progress.value.floatValue]; [self seek:progress.value.floatValue];
} }
- (void)seekToPdtTimePdtTimeMs:(IntPlayerMsg *)pdtTimeMs error:(FlutterError * _Nullable __autoreleasing *)error {
[self seekToPdtTimePdtTimeMs:pdtTimeMs.value.longLongValue];
}
- (void)setAudioPlayOutVolumeVolume:(nonnull IntPlayerMsg *)volume error:(FlutterError * _Nullable __autoreleasing * _Nonnull)error { - (void)setAudioPlayOutVolumeVolume:(nonnull IntPlayerMsg *)volume error:(FlutterError * _Nullable __autoreleasing * _Nonnull)error {
[self setAudioPlayoutVolume:volume.value.intValue]; [self setAudioPlayoutVolume:volume.value.intValue];
} }
...@@ -961,4 +1016,88 @@ BOOL CGImageRefContainsAlpha(CGImageRef imageRef) { ...@@ -961,4 +1016,88 @@ BOOL CGImageRefContainsAlpha(CGImageRef imageRef) {
return [TXCommonUtil boolMsgWith:r]; return [TXCommonUtil boolMsgWith:r];
} }
- (nullable IntMsg *)startPlayDrmParams:(nonnull TXPlayerDrmMsg *)params error:(FlutterError * _Nullable __autoreleasing * _Nonnull)error {
if (nil != _txVodPlayer) {
TXPlayerDrmBuilder *builder = [[TXPlayerDrmBuilder alloc] init];
builder.playUrl = params.playUrl;
builder.keyLicenseUrl = params.licenseUrl;
if(params.deviceCertificateUrl) {
builder.deviceCertificateUrl = builder.deviceCertificateUrl;
}
int result = [_txVodPlayer startPlayDrm:builder];
return [TXCommonUtil intMsgWith:@(result)];
}
return [TXCommonUtil intMsgWith:@(uninitialized)];
}
- (void)addSubtitleSourcePlayerMsg:(SubTitlePlayerMsg *)playerMsg error:(FlutterError * _Nullable __autoreleasing *)error {
if (nil != _txVodPlayer) {
TX_VOD_PLAYER_SUBTITLE_MIME_TYPE mimeType = TX_VOD_PLAYER_MIMETYPE_TEXT_SRT;
if([@"text/vtt" isEqualToString:playerMsg.mimeType]) {
mimeType = TX_VOD_PLAYER_MIMETYPE_TEXT_VTT;
}
[_txVodPlayer addSubtitleSource:playerMsg.url name:playerMsg.name mimeType:mimeType];
}
}
- (void)deselectTrackPlayerMsg:(nonnull IntPlayerMsg *)playerMsg error:(FlutterError * _Nullable __autoreleasing * _Nonnull)error {
if (nil != _txVodPlayer && nil != playerMsg.value) {
[_txVodPlayer deselectTrack:[playerMsg.value intValue]];
}
}
- (nullable ListMsg *)getAudioTrackInfoPlayerMsg:(nonnull PlayerMsg *)playerMsg error:(FlutterError * _Nullable __autoreleasing * _Nonnull)error {
if (nil != _txVodPlayer) {
NSArray *audioTrackInfoList = [_txVodPlayer getAudioTrackInfo];
NSMutableArray *subTitleTrackMapList = [[NSMutableArray alloc] init];
for (int i = 0; i < audioTrackInfoList.count; i++) {
TXTrackInfo *trackInfo = audioTrackInfoList[i];
NSMutableDictionary *map = [[NSMutableDictionary alloc] init];
[map setObject:@(trackInfo.trackType)forKey:@"trackType"];
[map setObject:@(trackInfo.trackIndex) forKey:@"trackIndex"];
[map setObject:trackInfo.name forKey:@"name"];
[map setObject:@(trackInfo.isSelected) forKey:@"isSelected"];
[map setObject:@(trackInfo.isExclusive) forKey:@"isExclusive"];
[map setObject:@(trackInfo.isInternal) forKey:@"isInternal"];
[subTitleTrackMapList addObject:map];
}
return [TXCommonUtil listMsgWith:subTitleTrackMapList];
}
return [TXCommonUtil listMsgWith:@[]];
}
- (nullable ListMsg *)getSubtitleTrackInfoPlayerMsg:(nonnull PlayerMsg *)playerMsg error:(FlutterError * _Nullable __autoreleasing * _Nonnull)error {
if(nil != _txVodPlayer) {
NSArray *subTitleTrackInfoList = [_txVodPlayer getSubtitleTrackInfo];
NSMutableArray *subTitleTrackMapList = [[NSMutableArray alloc] init];
for (int i = 0; i < subTitleTrackInfoList.count; i++) {
TXTrackInfo *trackInfo = subTitleTrackInfoList[i];
NSMutableDictionary *map = [[NSMutableDictionary alloc] init];
[map setObject:@(trackInfo.trackType )forKey:@"trackType"];
[map setObject:@(trackInfo.trackIndex) forKey:@"trackIndex"];
[map setObject:trackInfo.name forKey:@"name"];
[map setObject:@(trackInfo.isSelected) forKey:@"isSelected"];
[map setObject:@(trackInfo.isExclusive) forKey:@"isExclusive"];
[map setObject:@(trackInfo.isInternal) forKey:@"isInternal"];
[subTitleTrackMapList addObject:map];
}
return [TXCommonUtil listMsgWith:subTitleTrackMapList];
}
return [TXCommonUtil listMsgWith:@[]];
}
- (void)selectTrackPlayerMsg:(nonnull IntPlayerMsg *)playerMsg error:(FlutterError * _Nullable __autoreleasing * _Nonnull)error {
if (nil != _txVodPlayer && nil != playerMsg.value) {
[_txVodPlayer selectTrack:[playerMsg.value intValue]];
}
}
- (void)setSubtitleStylePlayerMsg:(nonnull SubTitleRenderModelPlayerMsg *)playerMsg error:(FlutterError * _Nullable __autoreleasing * _Nonnull)error {
if (nil != _txVodPlayer) {
[_txVodPlayer setSubtitleStyle:[FTXTransformation transformToTitleRenderModel:playerMsg]];
}
}
@end @end
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
#import "FTXPlayerEventSinkQueue.h" #import "FTXPlayerEventSinkQueue.h"
#import "FTXEvent.h" #import "FTXEvent.h"
#import <MediaPlayer/MediaPlayer.h> #import <MediaPlayer/MediaPlayer.h>
#import <TXLiteAVSDK_Player/TXLiteAVSDK.h> #import "FTXLiteAVSDKHeader.h"
#import "FTXAudioManager.h" #import "FTXAudioManager.h"
#import "FTXDownloadManager.h" #import "FTXDownloadManager.h"
#import "FtxMessages.h" #import "FtxMessages.h"
......
// Copyright (c) 2022 Tencent. All rights reserved. // Copyright (c) 2022 Tencent. All rights reserved.
#import <TXLiteAVSDK_Player/TXVodDownloadManager.h>
#import <Foundation/Foundation.h> #import <Foundation/Foundation.h>
#import "FTXEvent.h" #import "FTXEvent.h"
#import "FtxMessages.h" #import "FtxMessages.h"
......
...@@ -2,6 +2,18 @@ ...@@ -2,6 +2,18 @@
#import "TXCommonUtil.h" #import "TXCommonUtil.h"
#import <Flutter/Flutter.h> #import <Flutter/Flutter.h>
#include "FTXLiteAVSDKHeader.h"
#if __has_include(<TXLiteAVSDK_Player/TXVodDownloadManager.h>)
#import <TXLiteAVSDK_Player/TXVodDownloadManager.h>
#elif __has_include(<TXLiteAVSDK_Player_Premium/TXVodDownloadManager.h>)
#import <TXLiteAVSDK_Player_Premium/TXVodDownloadManager.h>
#elif __has_include(<TXLiteAVSDK_Professional/TXVodDownloadManager.h>)
#import <TXLiteAVSDK_Professional/TXVodDownloadManager.h>
#else
#import <TXVodDownloadManager.h>
#endif
@implementation TXCommonUtil @implementation TXCommonUtil
......
//
// FTXLiteAVSDKHeader.h
// super_player
//
// Created by Kongdywang on 2024/2/27.
//
#ifndef FTXLiteAVSDKHeader_h
#define FTXLiteAVSDKHeader_h
#if __has_include(<TXLiteAVSDK_Player/TXLiteAVSDK.h>)
#import <TXLiteAVSDK_Player/TXLiteAVSDK.h>
#elif __has_include(<TXLiteAVSDK_Player_Premium/TXLiteAVSDK.h>)
#import <TXLiteAVSDK_Player_Premium/TXLiteAVSDK.h>
#elif __has_include(<TXLiteAVSDK_Professional/TXLiteAVSDK.h>)
#import <TXLiteAVSDK_Professional/TXLiteAVSDK.h>
#else
#import "TXLiteAVSDK.h"
#endif
#endif /* FTXLiteAVSDKHeader_h */
...@@ -3,7 +3,19 @@ ...@@ -3,7 +3,19 @@
#define SUPERPLAYER_FLUTTER_IOS_CLASSES_HELPER_TXPREDOWNLOADFILEHELPERDELEGATE_H_ #define SUPERPLAYER_FLUTTER_IOS_CLASSES_HELPER_TXPREDOWNLOADFILEHELPERDELEGATE_H_
#import <Foundation/Foundation.h> #import <Foundation/Foundation.h>
#if __has_include(<TXLiteAVSDK_Player/TXVodPreloadManager.h>)
#import <TXLiteAVSDK_Player/TXVodPreloadManager.h> #import <TXLiteAVSDK_Player/TXVodPreloadManager.h>
#import <TXLiteAVSDK_Player/TXVodDownloadManager.h>
#elif __has_include(<TXLiteAVSDK_Player_Premium/TXVodPreloadManager.h>)
#import <TXLiteAVSDK_Player_Premium/TXVodPreloadManager.h>
#import <TXLiteAVSDK_Player_Premium/TXVodDownloadManager.h>
#elif __has_include(<TXLiteAVSDK_Professional/TXVodPreloadManager.h>)
#import <TXLiteAVSDK_Professional/TXVodPreloadManager.h>
#import <TXLiteAVSDK_Professional/TXVodDownloadManager.h>
#else
#import <TXVodPreloadManager.h>
#import <TXVodDownloadManager.h>
#endif
NS_ASSUME_NONNULL_BEGIN NS_ASSUME_NONNULL_BEGIN
......
...@@ -160,6 +160,13 @@ ...@@ -160,6 +160,13 @@
} }
} }
- (void)seekToPdtTimePdtTimeMs:(IntPlayerMsg *)pdtTimeMs error:(FlutterError * _Nullable __autoreleasing *)error {
id<TXFlutterVodPlayerApi> api = self.bridge.getPlayers[pdtTimeMs.playerId];
if(api) {
[api seekToPdtTimePdtTimeMs:pdtTimeMs error:error];
}
}
- (void)setAudioPlayOutVolumeVolume:(nonnull IntPlayerMsg *)volume error:(FlutterError * _Nullable __autoreleasing * _Nonnull)error { - (void)setAudioPlayOutVolumeVolume:(nonnull IntPlayerMsg *)volume error:(FlutterError * _Nullable __autoreleasing * _Nonnull)error {
id<TXFlutterVodPlayerApi> api = self.bridge.getPlayers[volume.playerId]; id<TXFlutterVodPlayerApi> api = self.bridge.getPlayers[volume.playerId];
if(api) { if(api) {
...@@ -254,6 +261,65 @@ ...@@ -254,6 +261,65 @@
return nil; return nil;
} }
- (IntMsg *)startPlayDrmParams:(TXPlayerDrmMsg *)params error:(FlutterError * _Nullable __autoreleasing *)error {
id<TXFlutterVodPlayerApi> api = self.bridge.getPlayers[params.playerId];
if(api) {
return [api startPlayDrmParams:params error:error];
}
return nil;
}
- (void)addSubtitleSourcePlayerMsg:(nonnull SubTitlePlayerMsg *)playerMsg error:(FlutterError * _Nullable __autoreleasing * _Nonnull)error {
id<TXFlutterVodPlayerApi> api = self.bridge.getPlayers[playerMsg.playerId];
if(api) {
[api addSubtitleSourcePlayerMsg:playerMsg error:error];
}
}
- (void)deselectTrackPlayerMsg:(nonnull IntPlayerMsg *)playerMsg error:(FlutterError * _Nullable __autoreleasing * _Nonnull)error {
id<TXFlutterVodPlayerApi> api = self.bridge.getPlayers[playerMsg.playerId];
if(api) {
[api deselectTrackPlayerMsg:playerMsg error:error];
}
}
- (nullable ListMsg *)getAudioTrackInfoPlayerMsg:(nonnull PlayerMsg *)playerMsg error:(FlutterError * _Nullable __autoreleasing * _Nonnull)error {
id<TXFlutterVodPlayerApi> api = self.bridge.getPlayers[playerMsg.playerId];
if(api) {
return [api getAudioTrackInfoPlayerMsg:playerMsg error:error];
}
return nil;
}
- (nullable ListMsg *)getSubtitleTrackInfoPlayerMsg:(nonnull PlayerMsg *)playerMsg error:(FlutterError * _Nullable __autoreleasing * _Nonnull)error {
id<TXFlutterVodPlayerApi> api = self.bridge.getPlayers[playerMsg.playerId];
if(api) {
return [api getSubtitleTrackInfoPlayerMsg:playerMsg error:error];
}
return nil;
}
- (void)selectTrackPlayerMsg:(nonnull IntPlayerMsg *)playerMsg error:(FlutterError * _Nullable __autoreleasing * _Nonnull)error {
id<TXFlutterVodPlayerApi> api = self.bridge.getPlayers[playerMsg.playerId];
if(api) {
[api selectTrackPlayerMsg:playerMsg error:error];
}
}
- (void)setSubtitleStylePlayerMsg:(nonnull SubTitleRenderModelPlayerMsg *)playerMsg error:(FlutterError * _Nullable __autoreleasing * _Nonnull)error {
id<TXFlutterVodPlayerApi> api = self.bridge.getPlayers[playerMsg.playerId];
if(api) {
[api setSubtitleStylePlayerMsg:playerMsg error:error];
}
}
@end @end
...@@ -14,6 +14,7 @@ NS_ASSUME_NONNULL_BEGIN ...@@ -14,6 +14,7 @@ NS_ASSUME_NONNULL_BEGIN
@class PlayerMsg; @class PlayerMsg;
@class LicenseMsg; @class LicenseMsg;
@class TXPlayInfoParamsPlayerMsg; @class TXPlayInfoParamsPlayerMsg;
@class TXPlayerDrmMsg;
@class PipParamsPlayerMsg; @class PipParamsPlayerMsg;
@class StringListPlayerMsg; @class StringListPlayerMsg;
@class BoolPlayerMsg; @class BoolPlayerMsg;
...@@ -34,6 +35,8 @@ NS_ASSUME_NONNULL_BEGIN ...@@ -34,6 +35,8 @@ NS_ASSUME_NONNULL_BEGIN
@class PreLoadMsg; @class PreLoadMsg;
@class PreLoadInfoMsg; @class PreLoadInfoMsg;
@class MapMsg; @class MapMsg;
@class SubTitlePlayerMsg;
@class SubTitleRenderModelPlayerMsg;
/// Pigeon original component, used to generate native communication code for `messages`. /// Pigeon original component, used to generate native communication code for `messages`.
/// The generation command is as follows. When using the generation command, /// The generation command is as follows. When using the generation command,
...@@ -66,6 +69,19 @@ NS_ASSUME_NONNULL_BEGIN ...@@ -66,6 +69,19 @@ NS_ASSUME_NONNULL_BEGIN
@property(nonatomic, copy, nullable) NSString * url; @property(nonatomic, copy, nullable) NSString * url;
@end @end
@interface TXPlayerDrmMsg : NSObject
/// `init` unavailable to enforce nonnull fields, see the `make` class method.
- (instancetype)init NS_UNAVAILABLE;
+ (instancetype)makeWithLicenseUrl:(NSString *)licenseUrl
playUrl:(NSString *)playUrl
playerId:(nullable NSNumber *)playerId
deviceCertificateUrl:(nullable NSString *)deviceCertificateUrl;
@property(nonatomic, copy) NSString * licenseUrl;
@property(nonatomic, copy) NSString * playUrl;
@property(nonatomic, strong, nullable) NSNumber * playerId;
@property(nonatomic, copy, nullable) NSString * deviceCertificateUrl;
@end
@interface PipParamsPlayerMsg : NSObject @interface PipParamsPlayerMsg : NSObject
+ (instancetype)makeWithPlayerId:(nullable NSNumber *)playerId + (instancetype)makeWithPlayerId:(nullable NSNumber *)playerId
backIconForAndroid:(nullable NSString *)backIconForAndroid backIconForAndroid:(nullable NSString *)backIconForAndroid
...@@ -145,7 +161,8 @@ NS_ASSUME_NONNULL_BEGIN ...@@ -145,7 +161,8 @@ NS_ASSUME_NONNULL_BEGIN
overlayIv:(nullable NSString *)overlayIv overlayIv:(nullable NSString *)overlayIv
extInfoMap:(nullable NSDictionary<NSString *, id> *)extInfoMap extInfoMap:(nullable NSDictionary<NSString *, id> *)extInfoMap
enableRenderProcess:(nullable NSNumber *)enableRenderProcess enableRenderProcess:(nullable NSNumber *)enableRenderProcess
preferredResolution:(nullable NSNumber *)preferredResolution; preferredResolution:(nullable NSNumber *)preferredResolution
mediaType:(nullable NSNumber *)mediaType;
@property(nonatomic, strong, nullable) NSNumber * playerId; @property(nonatomic, strong, nullable) NSNumber * playerId;
@property(nonatomic, strong, nullable) NSNumber * connectRetryCount; @property(nonatomic, strong, nullable) NSNumber * connectRetryCount;
@property(nonatomic, strong, nullable) NSNumber * connectRetryInterval; @property(nonatomic, strong, nullable) NSNumber * connectRetryInterval;
...@@ -166,6 +183,9 @@ NS_ASSUME_NONNULL_BEGIN ...@@ -166,6 +183,9 @@ NS_ASSUME_NONNULL_BEGIN
@property(nonatomic, strong, nullable) NSDictionary<NSString *, id> * extInfoMap; @property(nonatomic, strong, nullable) NSDictionary<NSString *, id> * extInfoMap;
@property(nonatomic, strong, nullable) NSNumber * enableRenderProcess; @property(nonatomic, strong, nullable) NSNumber * enableRenderProcess;
@property(nonatomic, strong, nullable) NSNumber * preferredResolution; @property(nonatomic, strong, nullable) NSNumber * preferredResolution;
/// Media asset type, default auto type, refer to value see[TXVodPlayEvent]
/// 媒资类型,默认auto类型, 取值参考 see[TXVodPlayEvent]
@property(nonatomic, strong, nullable) NSNumber * mediaType;
@end @end
@interface FTXLivePlayConfigPlayerMsg : NSObject @interface FTXLivePlayConfigPlayerMsg : NSObject
...@@ -315,6 +335,50 @@ NS_ASSUME_NONNULL_BEGIN ...@@ -315,6 +335,50 @@ NS_ASSUME_NONNULL_BEGIN
@property(nonatomic, strong, nullable) NSDictionary<NSString *, NSString *> * map; @property(nonatomic, strong, nullable) NSDictionary<NSString *, NSString *> * map;
@end @end
@interface SubTitlePlayerMsg : NSObject
/// `init` unavailable to enforce nonnull fields, see the `make` class method.
- (instancetype)init NS_UNAVAILABLE;
+ (instancetype)makeWithUrl:(NSString *)url
name:(NSString *)name
mimeType:(nullable NSString *)mimeType
playerId:(nullable NSNumber *)playerId;
@property(nonatomic, copy) NSString * url;
@property(nonatomic, copy) NSString * name;
@property(nonatomic, copy, nullable) NSString * mimeType;
@property(nonatomic, strong, nullable) NSNumber * playerId;
@end
@interface SubTitleRenderModelPlayerMsg : NSObject
+ (instancetype)makeWithCanvasWidth:(nullable NSNumber *)canvasWidth
canvasHeight:(nullable NSNumber *)canvasHeight
familyName:(nullable NSString *)familyName
fontSize:(nullable NSNumber *)fontSize
fontScale:(nullable NSNumber *)fontScale
fontColor:(nullable NSNumber *)fontColor
isBondFontStyle:(nullable NSNumber *)isBondFontStyle
outlineWidth:(nullable NSNumber *)outlineWidth
outlineColor:(nullable NSNumber *)outlineColor
lineSpace:(nullable NSNumber *)lineSpace
startMargin:(nullable NSNumber *)startMargin
endMargin:(nullable NSNumber *)endMargin
verticalMargin:(nullable NSNumber *)verticalMargin
playerId:(nullable NSNumber *)playerId;
@property(nonatomic, strong, nullable) NSNumber * canvasWidth;
@property(nonatomic, strong, nullable) NSNumber * canvasHeight;
@property(nonatomic, copy, nullable) NSString * familyName;
@property(nonatomic, strong, nullable) NSNumber * fontSize;
@property(nonatomic, strong, nullable) NSNumber * fontScale;
@property(nonatomic, strong, nullable) NSNumber * fontColor;
@property(nonatomic, strong, nullable) NSNumber * isBondFontStyle;
@property(nonatomic, strong, nullable) NSNumber * outlineWidth;
@property(nonatomic, strong, nullable) NSNumber * outlineColor;
@property(nonatomic, strong, nullable) NSNumber * lineSpace;
@property(nonatomic, strong, nullable) NSNumber * startMargin;
@property(nonatomic, strong, nullable) NSNumber * endMargin;
@property(nonatomic, strong, nullable) NSNumber * verticalMargin;
@property(nonatomic, strong, nullable) NSNumber * playerId;
@end
/// The codec used by TXFlutterSuperPlayerPluginAPI. /// The codec used by TXFlutterSuperPlayerPluginAPI.
NSObject<FlutterMessageCodec> *TXFlutterSuperPlayerPluginAPIGetCodec(void); NSObject<FlutterMessageCodec> *TXFlutterSuperPlayerPluginAPIGetCodec(void);
...@@ -470,6 +534,12 @@ NSObject<FlutterMessageCodec> *TXFlutterVodPlayerApiGetCodec(void); ...@@ -470,6 +534,12 @@ NSObject<FlutterMessageCodec> *TXFlutterVodPlayerApiGetCodec(void);
/// @params : see[TXPlayInfoParams] /// @params : see[TXPlayInfoParams]
/// return 是否播放成功 if play successful /// return 是否播放成功 if play successful
- (void)startVodPlayWithParamsParams:(TXPlayInfoParamsPlayerMsg *)params error:(FlutterError *_Nullable *_Nonnull)error; - (void)startVodPlayWithParamsParams:(TXPlayInfoParamsPlayerMsg *)params error:(FlutterError *_Nullable *_Nonnull)error;
/// 播放 DRM 加密视频
///
/// Playing DRM-encrypted video.
///
/// @return `nil` only when `error != nil`.
- (nullable IntMsg *)startPlayDrmParams:(TXPlayerDrmMsg *)params error:(FlutterError *_Nullable *_Nonnull)error;
/// 设置是否自动播放 /// 设置是否自动播放
/// ///
/// set autoplay /// set autoplay
...@@ -508,6 +578,14 @@ NSObject<FlutterMessageCodec> *TXFlutterVodPlayerApiGetCodec(void); ...@@ -508,6 +578,14 @@ NSObject<FlutterMessageCodec> *TXFlutterVodPlayerApiGetCodec(void);
/// Set the video playback progress to a specific time and start playing. /// Set the video playback progress to a specific time and start playing.
/// progress 要定位的视频时间,单位 秒 The video playback time to be located, in seconds /// progress 要定位的视频时间,单位 秒 The video playback time to be located, in seconds
- (void)seekProgress:(DoublePlayerMsg *)progress error:(FlutterError *_Nullable *_Nonnull)error; - (void)seekProgress:(DoublePlayerMsg *)progress error:(FlutterError *_Nullable *_Nonnull)error;
/// 跳转到视频流指定PDT时间点, 可实现视频快进,快退,进度条跳转等功能
/// 单位毫秒(ms)
/// 播放器高级版 11.6 版本开始支持
///
/// Jump to the specified PDT time point of the video stream, which can realize video fast forward, fast rewind, progress bar jump and other functions.
/// Unit millisecond (ms)
/// Player Premium version 11.6 starts to support
- (void)seekToPdtTimePdtTimeMs:(IntPlayerMsg *)pdtTimeMs error:(FlutterError *_Nullable *_Nonnull)error;
/// 设置播放速率,默认速率 1 /// 设置播放速率,默认速率 1
/// ///
/// Set the playback speed, with a default speed of 1. /// Set the playback speed, with a default speed of 1.
...@@ -625,6 +703,14 @@ NSObject<FlutterMessageCodec> *TXFlutterVodPlayerApiGetCodec(void); ...@@ -625,6 +703,14 @@ NSObject<FlutterMessageCodec> *TXFlutterVodPlayerApiGetCodec(void);
/// ///
/// @return `nil` only when `error != nil`. /// @return `nil` only when `error != nil`.
- (nullable DoubleMsg *)getDurationPlayerMsg:(PlayerMsg *)playerMsg error:(FlutterError *_Nullable *_Nonnull)error; - (nullable DoubleMsg *)getDurationPlayerMsg:(PlayerMsg *)playerMsg error:(FlutterError *_Nullable *_Nonnull)error;
- (void)addSubtitleSourcePlayerMsg:(SubTitlePlayerMsg *)playerMsg error:(FlutterError *_Nullable *_Nonnull)error;
/// @return `nil` only when `error != nil`.
- (nullable ListMsg *)getSubtitleTrackInfoPlayerMsg:(PlayerMsg *)playerMsg error:(FlutterError *_Nullable *_Nonnull)error;
/// @return `nil` only when `error != nil`.
- (nullable ListMsg *)getAudioTrackInfoPlayerMsg:(PlayerMsg *)playerMsg error:(FlutterError *_Nullable *_Nonnull)error;
- (void)selectTrackPlayerMsg:(IntPlayerMsg *)playerMsg error:(FlutterError *_Nullable *_Nonnull)error;
- (void)deselectTrackPlayerMsg:(IntPlayerMsg *)playerMsg error:(FlutterError *_Nullable *_Nonnull)error;
- (void)setSubtitleStylePlayerMsg:(SubTitleRenderModelPlayerMsg *)playerMsg error:(FlutterError *_Nullable *_Nonnull)error;
@end @end
extern void TXFlutterVodPlayerApiSetup(id<FlutterBinaryMessenger> binaryMessenger, NSObject<TXFlutterVodPlayerApi> *_Nullable api); extern void TXFlutterVodPlayerApiSetup(id<FlutterBinaryMessenger> binaryMessenger, NSObject<TXFlutterVodPlayerApi> *_Nullable api);
......
...@@ -16,12 +16,18 @@ player plugin. ...@@ -16,12 +16,18 @@ player plugin.
s.source_files = 'Classes/**/*' s.source_files = 'Classes/**/*'
s.public_header_files = 'Classes/**/*.h' s.public_header_files = 'Classes/**/*.h'
s.dependency 'Flutter' s.dependency 'Flutter'
s.dependency 'TXLiteAVSDK_Player'
s.ios.framework = ['MobileCoreServices'] s.ios.framework = ['MobileCoreServices']
s.platform = :ios, '9.0' s.platform = :ios, '9.0'
s.static_framework = true s.static_framework = true
s.resources = ['Classes/TXResource/**/*'] s.resources = ['Classes/TXResource/**/*']
# Set the dependent LiteAV SDK type:
# Player SDK: s.dependency 'TXLiteAVSDK_Player'
# Player_Premium SDK: s.dependency 'TXLiteAVSDK_Player_Premium'
# Professional SDK: s.dependency 'TXLiteAVSDK_Professional'
# If you want to specify the SDK version(eg 11.6.15041), use: s.dependency 'TXLiteAVSDK_Player','11.6.15041'
s.dependency 'TXLiteAVSDK_Player', '11.7.15343'
# Flutter.framework does not contain a i386 slice. # Flutter.framework does not contain a i386 slice.
s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES', 'EXCLUDED_ARCHS[sdk=iphonesimulator*]' => 'i386' } s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES', 'EXCLUDED_ARCHS[sdk=iphonesimulator*]' => 'i386' }
end end
...@@ -34,6 +34,14 @@ class TXPlayInfoParamsPlayerMsg { ...@@ -34,6 +34,14 @@ class TXPlayInfoParamsPlayerMsg {
String? url; String? url;
} }
class TXPlayerDrmMsg {
String licenseUrl;
String playUrl;
int? playerId;
String? deviceCertificateUrl;
TXPlayerDrmMsg(this.playUrl, this.licenseUrl);
}
class PipParamsPlayerMsg { class PipParamsPlayerMsg {
int? playerId; int? playerId;
String? backIconForAndroid; String? backIconForAndroid;
...@@ -136,6 +144,10 @@ class FTXVodPlayConfigPlayerMsg { ...@@ -136,6 +144,10 @@ class FTXVodPlayConfigPlayerMsg {
// 优先播放的分辨率,preferredResolution = width * height // 优先播放的分辨率,preferredResolution = width * height
int? preferredResolution; int? preferredResolution;
/// Media asset type, default auto type, refer to value see[TXVodPlayEvent]
/// 媒资类型,默认auto类型, 取值参考 see[TXVodPlayEvent]
int? mediaType;
} }
class FTXLivePlayConfigPlayerMsg { class FTXLivePlayConfigPlayerMsg {
...@@ -281,6 +293,31 @@ class MapMsg { ...@@ -281,6 +293,31 @@ class MapMsg {
Map<String?, String?>? map; Map<String?, String?>? map;
} }
class SubTitlePlayerMsg {
String url;
String name;
String? mimeType;
int? playerId;
SubTitlePlayerMsg(this.url, this.name, {this.playerId, this.mimeType});
}
class SubTitleRenderModelPlayerMsg {
int? canvasWidth;
int? canvasHeight;
String? familyName;
double? fontSize;
double? fontScale;
int? fontColor;
bool? isBondFontStyle;
double? outlineWidth;
int? outlineColor;
double? lineSpace;
double? startMargin;
double? endMargin;
double? verticalMargin;
int? playerId;
}
@HostApi() @HostApi()
abstract class TXFlutterSuperPlayerPluginAPI { abstract class TXFlutterSuperPlayerPluginAPI {
StringMsg getPlatformVersion(); StringMsg getPlatformVersion();
...@@ -424,6 +461,11 @@ abstract class TXFlutterVodPlayerApi { ...@@ -424,6 +461,11 @@ abstract class TXFlutterVodPlayerApi {
/// return 是否播放成功 if play successful /// return 是否播放成功 if play successful
void startVodPlayWithParams(TXPlayInfoParamsPlayerMsg params); void startVodPlayWithParams(TXPlayInfoParamsPlayerMsg params);
/// 播放 DRM 加密视频
///
/// Playing DRM-encrypted video.
IntMsg startPlayDrm(TXPlayerDrmMsg params);
/// 设置是否自动播放 /// 设置是否自动播放
/// ///
/// set autoplay /// set autoplay
...@@ -466,6 +508,16 @@ abstract class TXFlutterVodPlayerApi { ...@@ -466,6 +508,16 @@ abstract class TXFlutterVodPlayerApi {
/// progress 要定位的视频时间,单位 秒 The video playback time to be located, in seconds /// progress 要定位的视频时间,单位 秒 The video playback time to be located, in seconds
void seek(DoublePlayerMsg progress); void seek(DoublePlayerMsg progress);
/// 跳转到视频流指定PDT时间点, 可实现视频快进,快退,进度条跳转等功能
/// 单位毫秒(ms)
/// 播放器高级版 11.6 版本开始支持
///
/// Jump to the specified PDT time point of the video stream, which can realize video fast forward, fast rewind, progress bar jump and other functions.
/// Unit millisecond (ms)
/// Player Premium version 11.6 starts to support
void seekToPdtTime(IntPlayerMsg pdtTimeMs);
/// 设置播放速率,默认速率 1 /// 设置播放速率,默认速率 1
/// ///
/// Set the playback speed, with a default speed of 1. /// Set the playback speed, with a default speed of 1.
...@@ -578,6 +630,18 @@ abstract class TXFlutterVodPlayerApi { ...@@ -578,6 +630,18 @@ abstract class TXFlutterVodPlayerApi {
/// ///
/// To get the total duration /// To get the total duration
DoubleMsg getDuration(PlayerMsg playerMsg); DoubleMsg getDuration(PlayerMsg playerMsg);
void addSubtitleSource(SubTitlePlayerMsg playerMsg);
ListMsg getSubtitleTrackInfo(PlayerMsg playerMsg);
ListMsg getAudioTrackInfo(PlayerMsg playerMsg);
void selectTrack(IntPlayerMsg playerMsg);
void deselectTrack(IntPlayerMsg playerMsg);
void setSubtitleStyle(SubTitleRenderModelPlayerMsg playerMsg);
} }
@HostApi() @HostApi()
......
...@@ -22,6 +22,27 @@ class TXPlayerValue { ...@@ -22,6 +22,27 @@ class TXPlayerValue {
} }
} }
/// DRM playback information
/// DRM播放信息
class TXPlayerDrmBuilder {
/// URL to play media
/// 播放媒体的url
String licenseUrl;
/// Decrypt key url
/// 解密key url
String playUrl;
/// Certificate provider url
/// 证书提供商url
String? deviceCertificateUrl;
TXPlayerDrmBuilder(this.licenseUrl, this.playUrl, {this.deviceCertificateUrl});
TXPlayerDrmMsg toMsg() {
TXPlayerDrmMsg msg = TXPlayerDrmMsg(licenseUrl: this.licenseUrl, playUrl: this.playUrl);
msg.deviceCertificateUrl = this.deviceCertificateUrl;
return msg;
}
}
/// ///
/// Live stream type. /// Live stream type.
/// ///
...@@ -66,8 +87,8 @@ abstract class TXVodPlayEvent { ...@@ -66,8 +87,8 @@ abstract class TXVodPlayEvent {
// Video resolution changes (resolution is in the EVT_PARAM parameter). // Video resolution changes (resolution is in the EVT_PARAM parameter).
// 视频分辨率发生变化(分辨率在 EVT_PARAM 参数中) // 视频分辨率发生变化(分辨率在 EVT_PARAM 参数中)
static const PLAY_EVT_CHANGE_RESOLUTION = 2009; static const PLAY_EVT_CHANGE_RESOLUTION = 2009;
// If you receive this message during live streaming, it means that you have used TXVodPlayer incorrectly. // Successfully obtained on-demand file information.
// 如果您在直播中收到此消息,说明错用成了 TXVodPlayer // 获取点播文件信息成功
static const PLAY_EVT_GET_PLAYINFO_SUCC = 2010; static const PLAY_EVT_GET_PLAYINFO_SUCC = 2010;
// Get custom SEI message embedded in video stream, message sending needs to use TXLivePusher. // Get custom SEI message embedded in video stream, message sending needs to use TXLivePusher.
// 如果您在直播中收到此消息,说明错用成了 TXVodPlayer // 如果您在直播中收到此消息,说明错用成了 TXVodPlayer
...@@ -149,6 +170,9 @@ abstract class TXVodPlayEvent { ...@@ -149,6 +170,9 @@ abstract class TXVodPlayEvent {
// Seek completed. // Seek completed.
// Seek 完成 // Seek 完成
static const VOD_PLAY_EVT_SEEK_COMPLETE = 2019; static const VOD_PLAY_EVT_SEEK_COMPLETE = 2019;
// Video SEI frame information, Player Premium version 11.6 starts to support
// 视频 SEI 帧信息, 播放器高级版 11.6 版本开始支持
static const VOD_PLAY_EVT_VIDEO_SEI = 2030;
// UTC time // UTC time
// UTC时间 // UTC时间
...@@ -216,6 +240,40 @@ abstract class TXVodPlayEvent { ...@@ -216,6 +240,40 @@ abstract class TXVodPlayEvent {
// Encryption type. // Encryption type.
// 加密类型 // 加密类型
static const EVT_DRM_TYPE = "EVT_DRM_TYPE"; static const EVT_DRM_TYPE = "EVT_DRM_TYPE";
// Ghost watermark text (supported since version 11.5)
// 幽灵水印文本(11.5版本开始支持)
static const EVT_KEY_WATER_MARK_TEXT = "EVT_KEY_WATER_MARK_TEXT";
// SEI data type
static const EVT_KEY_SEI_TYPE = "EVT_KEY_SEI_TYPE";
// SEI data size
static const EVT_KEY_SEI_SIZE = "EVT_KEY_SEI_SIZE";
// SEI data
static const EVT_KEY_SEI_DATA = "EVT_KEY_SEI_DATA";
// Play PDT time, Player Premium version 11.6 starts to support
// 播放PDT时间, 播放器高级版 11.6 版本开始支持
static const EVT_PLAY_PDT_TIME_MS = "EVT_PLAY_PDT_TIME_MS";
/// External subtitle file in SRT format.
/// 外挂字幕SRT格式
static const VOD_PLAY_MIMETYPE_TEXT_SRT = "text/x-subrip";
/// External subtitle file in VTT format.
/// 外挂字幕VTT格式
static const VOD_PLAY_MIMETYPE_TEXT_VTT = "text/vtt";
// AUTO type (default value, adaptive bit rate playback is not supported yet)
// AUTO类型(默认值,自适应码率播放暂不支持)
static const MEDIA_TYPE_AUTO = 0;
// HLS on-demand media assets
// HLS点播媒资
static const MEDIA_TYPE_HLS_VOD = 1;
// HLS Live Media Assets
// HLS直播媒资
static const MEDIA_TYPE_HLS_LIVE = 2;
// MP4 and other general file on-demand media assets
// MP4等通用文件点播媒资
static const MEDIA_TYPE_FILE_VOD = 3;
// DASH on-demand media assets
// DASH点播媒资
static const MEDIA_TYPE_DASH_VOD = 4;
/// superplayer plugin event /// superplayer plugin event
// Volume change // Volume change
...@@ -340,6 +398,24 @@ abstract class TXVodPlayEvent { ...@@ -340,6 +398,24 @@ abstract class TXVodPlayEvent {
static const EVENT_RESULT = "result"; static const EVENT_RESULT = "result";
static const EVENT_REASON = "reason"; static const EVENT_REASON = "reason";
/// Select track complete
/// 切换轨道完成
static const VOD_PLAY_EVT_SELECT_TRACK_COMPLETE = 2020;
/// Switched media track index
/// 切换的媒体轨道index
static const EVT_KEY_SELECT_TRACK_INDEX = "EVT_KEY_SELECT_TRACK_INDEX";
/// Return error code for switching media tracks
/// 切换媒体轨道的返回错误码
static const EVT_KEY_SELECT_TRACK_ERROR_CODE = "EVT_KEY_SELECT_TRACK_ERROR_CODE";
// subtitle data event
// 回调 SubtitleData 事件id
static const EVENT_SUBTITLE_DATA = 601;
// subtitle data extra key
// 回调 SubtitleData 事件对应的key
static const EXTRA_SUBTITLE_DATA = "subtitleData";
static const EXTRA_SUBTITLE_START_POSITION_MS = "startPositionMs";
static const EXTRA_SUBTITLE_DURATION_MS = "durationMs";
static const EXTRA_SUBTITLE_TRACK_INDEX = "trackIndex";
} }
abstract class TXVodNetEvent { abstract class TXVodNetEvent {
...@@ -674,6 +750,115 @@ class TXVodDownloadMediaInfo { ...@@ -674,6 +750,115 @@ class TXVodDownloadMediaInfo {
} }
} }
/// Track details
/// 轨道的详细信息
class TXTrackInfo {
/// Unknown
/// 未知
static const TX_VOD_MEDIA_TRACK_TYPE_UNKNOW = 0;
/// Video track
/// 视频轨
static const TX_VOD_MEDIA_TRACK_TYPE_VIDEO = 1;
/// Audio track
/// 音频轨
static const TX_VOD_MEDIA_TRACK_TYPE_AUDIO = 2;
/// Subtitle track
/// 字幕轨
static const TX_VOD_MEDIA_TRACK_TYPE_SUBTITLE = 3;
/// track type
/// track类型
int trackType;
/// Track index
/// 轨道index
int trackIndex;
/// Track name
/// 轨道名字
String name;
/// Whether the current track is selected
/// 当前轨道是否被选中
bool isSelected = false;
/// If it is true, only one track of this type can be selected at each time. If it is false, multiple tracks of this type can be selected at the same time.
/// 如果是true,该类型轨道每个时刻只有一条能被选中,如果是false,该类型轨道可以同时选中多条
bool isExclusive = true;
/// Whether the current track is the internal original track
/// 当前的轨道是否是内部原始轨道
bool isInternal = true;
TXTrackInfo(this.name, this.trackIndex, this.trackType);
}
class TXVodSubtitleData{
/// 字幕内容
/// subtitle content
String? subtitleData;
/// 字幕持续时间, 单位毫秒
/// Subtitle duration, in milliseconds
int? startPositionMs;
/// 字幕开始时间,也就是视频的position位置,单位毫秒
/// Subtitle start time, which is the position of the video, in milliseconds
int? durationMs;
/// 当前字幕轨道的trackIndex
/// Track Index of the current subtitle track
int? trackIndex;
TXVodSubtitleData(this.subtitleData,this.startPositionMs,this.durationMs,this.trackIndex);
}
class TXSubtitleRenderModel {
/// fontSize
/// 字体大小
double? fontSize;
/// Font color, ARGB format If not set, the default is white opaque (0xFFFFFFFF)
/// 字体颜色,ARGB格式
/// 如果不设置,默认为白色不透明(0xFFFFFFFF)
int? fontColor;
/// Whether it is bold, the default is normal font.
/// 是否是粗体,默认时正常字体
bool? isBondFontStyle;
/// Stroke width. If not set, the default stroke width will be used internally.
/// 描边宽度
double? outlineWidth;
/// Stroke color, ARGB format. If not set, the default is black opaque (0xFF000000).
/// 描边颜色,ARGB格式
/// 如果不设置,默认为黑色不透明(0xFF000000)
int? outlineColor;
/// canvasWidth not support on Flutter platform
int? canvasWidth;
/// canvasHeight not support on Flutter platform
int? canvasHeight;
/// familyName not support on Flutter platform
String? familyName;
/// fontScale not support on Flutter platform
double? fontScale;
/// lineSpace not support on Flutter platform
double? lineSpace;
/// startMargin not support on Flutter platform
double? startMargin;
/// endMargin not support on Flutter platform
double? endMargin;
/// verticalMargin not support on Flutter platform
double? verticalMargin;
SubTitleRenderModelPlayerMsg toMsg() {
SubTitleRenderModelPlayerMsg msg = SubTitleRenderModelPlayerMsg();
msg.canvasWidth = canvasWidth;
msg.canvasHeight = canvasHeight;
msg.familyName = familyName;
msg.fontSize = fontSize;
msg.fontScale = fontScale;
msg.fontColor = fontColor;
msg.isBondFontStyle = isBondFontStyle;
msg.outlineWidth = outlineWidth;
msg.outlineColor = outlineColor;
msg.lineSpace = lineSpace;
msg.startMargin = startMargin;
msg.endMargin = endMargin;
msg.verticalMargin = verticalMargin;
return msg;
}
}
/// Player type. /// Player type.
/// ///
/// 播放器类型 /// 播放器类型
......
...@@ -88,6 +88,10 @@ class FTXVodPlayConfig { ...@@ -88,6 +88,10 @@ class FTXVodPlayConfig {
// 优先播放的分辨率,preferredResolution = width * height // 优先播放的分辨率,preferredResolution = width * height
int preferredResolution = 720 * 1280; int preferredResolution = 720 * 1280;
/// Media asset type, default auto type, refer to value see[TXVodPlayEvent]
/// 媒资类型,默认auto类型, 取值参考 see[TXVodPlayEvent]
int mediaType = TXVodPlayEvent.MEDIA_TYPE_AUTO;
Map<String, dynamic> toJson() { Map<String, dynamic> toJson() {
Map<String, dynamic> json = {}; Map<String, dynamic> json = {};
json["connectRetryCount"] = connectRetryCount; json["connectRetryCount"] = connectRetryCount;
...@@ -109,6 +113,7 @@ class FTXVodPlayConfig { ...@@ -109,6 +113,7 @@ class FTXVodPlayConfig {
json["extInfoMap"] = extInfoMap; json["extInfoMap"] = extInfoMap;
json["enableRenderProcess"] = enableRenderProcess; json["enableRenderProcess"] = enableRenderProcess;
json["preferredResolution"] = preferredResolution.toString(); json["preferredResolution"] = preferredResolution.toString();
json["mediaType"] = mediaType.toString();
return json; return json;
} }
...@@ -133,6 +138,7 @@ class FTXVodPlayConfig { ...@@ -133,6 +138,7 @@ class FTXVodPlayConfig {
extInfoMap: extInfoMap, extInfoMap: extInfoMap,
enableRenderProcess: enableRenderProcess, enableRenderProcess: enableRenderProcess,
preferredResolution: preferredResolution, preferredResolution: preferredResolution,
mediaType: mediaType,
); );
} }
} }
......
...@@ -127,6 +127,12 @@ class TXVodPlayerController extends ChangeNotifier implements ValueListenable<TX ...@@ -127,6 +127,12 @@ class TXVodPlayerController extends ChangeNotifier implements ValueListenable<TX
break; break;
case TXVodPlayEvent.PLAY_WARNING_SHAKE_FAIL: case TXVodPlayEvent.PLAY_WARNING_SHAKE_FAIL:
break; break;
case TXVodPlayEvent.EVENT_SUBTITLE_DATA:
String subtitleDataStr = map[TXVodPlayEvent.EXTRA_SUBTITLE_DATA] ?? "";
if (subtitleDataStr != "") {
map[TXVodPlayEvent.EXTRA_SUBTITLE_DATA] = subtitleDataStr.trim().replaceAll('\\N', '\n');
}
break;
default: default:
break; break;
} }
...@@ -325,6 +331,24 @@ class TXVodPlayerController extends ChangeNotifier implements ValueListenable<TX ...@@ -325,6 +331,24 @@ class TXVodPlayerController extends ChangeNotifier implements ValueListenable<TX
..playerId = _playerId); ..playerId = _playerId);
} }
/// 此接口仅播放器高级版本(Player_Premium)支持,需要购买播放器移动端高级版 License
/// 跳转到视频流指定PDT时间点, 可实现视频快进,快退,进度条跳转等功能
/// 播放器高级版 11.6 版本开始支持
/// @param pdtTimeMs 视频流PDT时间点,单位毫秒(ms)
///
/// This interface is only supported by the premium version of the player (Player_Premium),
/// and you need to purchase the premium version of the player mobile license.
/// Jump to the specified PDT time point of the video stream, which can realize video fast forward, fast rewind, progress bar jump and other functions.
/// Player Premium version 11.6 starts to support
/// @param pdtTimeMs video stream PDT time point, unit millisecond (ms)
Future<void> seekToPdtTime(int pdtTimeMs) async {
if (_isNeedDisposed) return;
await _initPlayer.future;
await _vodPlayerApi.seekToPdtTime(IntPlayerMsg()
..value = pdtTimeMs
..playerId = _playerId);
}
/// Set the playback speed, with a default speed of 1. /// Set the playback speed, with a default speed of 1.
/// ///
/// 设置播放速率,默认速率 1 /// 设置播放速率,默认速率 1
...@@ -578,6 +602,108 @@ class TXVodPlayerController extends ChangeNotifier implements ValueListenable<TX ...@@ -578,6 +602,108 @@ class TXVodPlayerController extends ChangeNotifier implements ValueListenable<TX
await _vodPlayerApi.exitPictureInPictureMode(PlayerMsg()..playerId = _playerId); await _vodPlayerApi.exitPictureInPictureMode(PlayerMsg()..playerId = _playerId);
} }
/// This interface is only supported by the premium version of the player (Player_Premium),
/// and you need to purchase the premium version of the player mobile license.
/// Add external subtitles
/// @param url subtitle address
/// @param name The name of the subtitle. If you add multiple subtitles, please set the subtitle name to a different name to distinguish it from other added subtitles, otherwise it may lead to incorrect subtitle selection.
/// @param mimeType subtitle type, only supports VVT and SRT formats [VOD_PLAY_MIMETYPE_TEXT_SRT] [VOD_PLAY_MIMETYPE_TEXT_VTT]
/// Later, you can get the corresponding name through the name in the result returned by [getSubtitleTrackInfo]
///
/// 此接口仅播放器高级版本(Player_Premium)支持,需要购买播放器移动端高级版 License
/// 添加外挂字幕
/// @param url 字幕地址
/// @param name 字幕的名字。如果添加多个字幕,字幕名称请设置为不同的名字,用于区分与其他添加的字幕,否则可能会导致字幕选择错误。
/// @param mimeType 字幕类型,仅支持VVT和SRT格式 [VOD_PLAY_MIMETYPE_TEXT_SRT] [VOD_PLAY_MIMETYPE_TEXT_VTT]
/// 后面可以通过[getSubtitleTrackInfo]返回结果中的 name 获取对应的名字
Future<void> addSubtitleSource(String url, String name, {String? mimeType}) async {
if (_isNeedDisposed) return;
await _initPlayer.future;
await _vodPlayerApi.addSubtitleSource(SubTitlePlayerMsg(url: url, name: name, mimeType: mimeType)..playerId = _playerId);
}
/// This interface is only supported by the premium version of the player (Player_Premium),
/// and you need to purchase the premium version of the player mobile license.
/// Returns the subtitle track information list
///
/// 此接口仅播放器高级版本(Player_Premium)支持,需要购买播放器移动端高级版 License
/// 返回字幕轨道信息列表
Future<List<TXTrackInfo>> getSubtitleTrackInfo() async {
if (_isNeedDisposed) return [];
await _initPlayer.future;
ListMsg listMsg = await _vodPlayerApi.getSubtitleTrackInfo(PlayerMsg(playerId: _playerId));
if (null != listMsg.value) {
List<dynamic>? transInfoData = listMsg.value!;
List<TXTrackInfo> trackInfoList = [];
for (Map<dynamic, dynamic> map in transInfoData) {
TXTrackInfo trackInfo = TXTrackInfo(map["name"], map["trackIndex"], map["trackType"]);
trackInfo.isSelected = map["isSelected"] ?? false;
trackInfo.isExclusive = map["isExclusive"] ?? true;
trackInfo.isInternal = map["isInternal"] ?? true;
trackInfoList.add(trackInfo);
}
return trackInfoList;
}
return [];
}
/// This interface is only supported by the premium version of the player (Player_Premium),
/// and you need to purchase the premium version of the player mobile license.
/// Returns the audio track information list
///
/// 此接口仅播放器高级版本(Player_Premium)支持,需要购买播放器移动端高级版 License
/// 返回音频轨道信息列表
Future<List<TXTrackInfo>> getAudioTrackInfo() async {
if (_isNeedDisposed) return [];
await _initPlayer.future;
ListMsg listMsg = await _vodPlayerApi.getAudioTrackInfo(PlayerMsg(playerId: _playerId));
if (null != listMsg.value) {
List<dynamic>? transInfoData = listMsg.value!;
List<TXTrackInfo> trackInfoList = [];
for (Map<dynamic, dynamic> map in transInfoData) {
TXTrackInfo trackInfo = TXTrackInfo(map["name"], map["trackIndex"], map["trackType"]);
trackInfo.isSelected = map["isSelected"] ?? false;
trackInfo.isExclusive = map["isExclusive"] ?? true;
trackInfo.isInternal = map["isInternal"] ?? true;
trackInfoList.add(trackInfo);
}
return trackInfoList;
}
return [];
}
/// This interface is only supported by the premium version of the player (Player_Premium),
/// and you need to purchase the premium version of the player mobile license.
/// Select track
/// @param trackIndex track index, obtained through trackIndex of [TXTrackInfo]
///
/// 此接口仅播放器高级版本(Player_Premium)支持,需要购买播放器移动端高级版 License
/// 选择轨道
/// @param trackIndex 轨道index,通过[TXTrackInfo]的trackIndex获取
Future<void> selectTrack(int trackIndex) async {
if (_isNeedDisposed) return;
await _initPlayer.future;
await _vodPlayerApi.selectTrack(IntPlayerMsg()
..playerId = _playerId
..value = trackIndex);
}
/// This interface is only supported by the premium version of the player (Player_Premium),
/// and you need to purchase the premium version of the player mobile license.
/// Deselect track
/// @param trackIndex track index, obtained through trackIndex of [TXTrackInfo]
///
/// 此接口仅播放器高级版本(Player_Premium)支持,需要购买播放器移动端高级版 License
/// 取消选择轨道
/// @param trackIndex 轨道index,通过[TXTrackInfo]的trackIndex获取
Future<void> deselectTrack(int trackIndex) async {
if (_isNeedDisposed) return;
await _initPlayer.future;
await _vodPlayerApi.deselectTrack(IntPlayerMsg()
..playerId = _playerId
..value = trackIndex);
}
/// release controller /// release controller
/// ///
/// 释放controller /// 释放controller
......
...@@ -38,5 +38,10 @@ ...@@ -38,5 +38,10 @@
"tx_spw_1080p" : "HD+ 1080p", "tx_spw_1080p" : "HD+ 1080p",
"tx_spw_2k" : "2K", "tx_spw_2k" : "2K",
"tx_spw_4k" : "4k", "tx_spw_4k" : "4k",
"tx_spw_od" : "OD" "tx_spw_od" : "OD",
"tx_subtitle_title" : "Subtitle",
"tx_subtitle_close" : "Close",
"tx_audio_track_title" : "AudioTrack",
"tx_audio_track_item" : "Track",
"tx_audio_track_close" : "Close"
} }
\ No newline at end of file
...@@ -38,5 +38,10 @@ ...@@ -38,5 +38,10 @@
"tx_spw_1080p" : "全高清1080p", "tx_spw_1080p" : "全高清1080p",
"tx_spw_2k" : "2K", "tx_spw_2k" : "2K",
"tx_spw_4k" : "4k", "tx_spw_4k" : "4k",
"tx_spw_od" : "原画" "tx_spw_od" : "原画",
"tx_subtitle_title" : "字幕",
"tx_subtitle_close" : "关闭",
"tx_audio_track_title" : "音轨",
"tx_audio_track_item" : "音轨",
"tx_audio_track_close" : "关闭"
} }
\ No newline at end of file
...@@ -84,6 +84,11 @@ class FSPLocal { ...@@ -84,6 +84,11 @@ class FSPLocal {
String get txSpw2k => _localStrings["tx_spw_2k"]!; String get txSpw2k => _localStrings["tx_spw_2k"]!;
String get txSpw4k => _localStrings["tx_spw_4k"]!; String get txSpw4k => _localStrings["tx_spw_4k"]!;
String get txSpwOd => _localStrings["tx_spw_od"]!; String get txSpwOd => _localStrings["tx_spw_od"]!;
String get txSubtitleTitle => _localStrings["tx_subtitle_title"]!;
String get txSubtitleTitleClose => _localStrings["tx_subtitle_close"]!;
String get txAudioTrackTitle => _localStrings["tx_audio_track_title"]!;
String get txAudioTrackTitleItem => _localStrings["tx_audio_track_item"]!;
String get txAudioTrackClose => _localStrings["tx_audio_track_close"]!;
// cgi // cgi
String get txSpwErrFileNotExist => _localStrings["tx_spw_err_file_not_exist"]!; String get txSpwErrFileNotExist => _localStrings["tx_spw_err_file_not_exist"]!;
String get txSpwErrInvalidTrialDuration => _localStrings["tx_spw_err_invalid_trial_duration"]!; String get txSpwErrInvalidTrialDuration => _localStrings["tx_spw_err_invalid_trial_duration"]!;
......
...@@ -35,6 +35,9 @@ part 'ui/superplayer_widget.dart'; ...@@ -35,6 +35,9 @@ part 'ui/superplayer_widget.dart';
part 'ui/superplayer_cover_view.dart'; part 'ui/superplayer_cover_view.dart';
part 'ui/superplayer_more_view.dart'; part 'ui/superplayer_more_view.dart';
part 'ui/superplayer_video_slider.dart'; part 'ui/superplayer_video_slider.dart';
part 'ui/superplayer_subtitle_view.dart';
part 'ui/superplayer_subtitle_display_view.dart';
part 'ui/superplayer_audio_view.dart';
part 'common/color_resource.dart'; part 'common/color_resource.dart';
part 'common/player_constants.dart'; part 'common/player_constants.dart';
part 'common/theme_resource.dart'; part 'common/theme_resource.dart';
......
...@@ -78,6 +78,7 @@ class SuperPlayerModel { ...@@ -78,6 +78,7 @@ class SuperPlayerModel {
int appId = 0; int appId = 0;
String videoURL = ""; String videoURL = "";
List<SuperPlayerUrl> multiVideoURLs = []; List<SuperPlayerUrl> multiVideoURLs = [];
List<FSubtitleSourceModel> subtitleSources = [];
int defaultPlayIndex = 0; int defaultPlayIndex = 0;
SuperPlayerVideoId? videoId; SuperPlayerVideoId? videoId;
String title = ""; String title = "";
...@@ -150,3 +151,14 @@ class SliderPoint { ...@@ -150,3 +151,14 @@ class SliderPoint {
Color pointColor = Colors.white; Color pointColor = Colors.white;
double progress = 0; double progress = 0;
} }
class FSubtitleSourceModel {
/// The name of the external subtitle file
String name = "";
/// The url of the external subtitle file
String url = "";
/// mimeType:The mimeType of the external subtitle file,must is one of [VOD_PLAY_MIMETYPE_TEXT_SRT] or [VOD_PLAY_MIMETYPE_TEXT_VTT]
String mimeType = TXVodPlayEvent.VOD_PLAY_MIMETYPE_TEXT_SRT;
FSubtitleSourceModel();
}
...@@ -23,6 +23,12 @@ class SuperPlayerController { ...@@ -23,6 +23,12 @@ class SuperPlayerController {
_SuperPlayerObserver? _observer; _SuperPlayerObserver? _observer;
VideoQuality? currentQuality; VideoQuality? currentQuality;
List<VideoQuality>? currentQualityList; List<VideoQuality>? currentQualityList;
TXTrackInfo? currentAudioTrackInfo;
List<TXTrackInfo>? audioTrackInfoList;
TXTrackInfo? currentSubtitleTrackInfo;
List<TXTrackInfo>? subtitleTrackInfoList;
TXVodSubtitleData? currentSubtitleData;
TXSubtitleRenderModel? _currentSubtitleRenderModel;
StreamController<TXPlayerHolder> playerStreamController = StreamController.broadcast(); StreamController<TXPlayerHolder> playerStreamController = StreamController.broadcast();
SuperPlayerState playerState = SuperPlayerState.INIT; SuperPlayerState playerState = SuperPlayerState.INIT;
SuperPlayerType playerType = SuperPlayerType.VOD; SuperPlayerType playerType = SuperPlayerType.VOD;
...@@ -88,6 +94,11 @@ class SuperPlayerController { ...@@ -88,6 +94,11 @@ class SuperPlayerController {
_vodPlayEventListener = _vodPlayerController.onPlayerEventBroadcast.listen((event) async { _vodPlayEventListener = _vodPlayerController.onPlayerEventBroadcast.listen((event) async {
int eventCode = event['event']; int eventCode = event['event'];
switch (eventCode) { switch (eventCode) {
case TXVodPlayEvent.VOD_PLAY_EVT_SELECT_TRACK_COMPLETE:
int? errorCode = event["EVT_KEY_SELECT_TRACK_ERROR_CODE"];
int? trackIndex = event["EVT_KEY_SELECT_TRACK_INDEX"];
LogUtils.d(TAG, "selectTrack,trackIndex:$trackIndex,errorCode:$errorCode");
break;
case TXVodPlayEvent.PLAY_EVT_GET_PLAYINFO_SUCC: case TXVodPlayEvent.PLAY_EVT_GET_PLAYINFO_SUCC:
_currentPlayUrl = event[TXVodPlayEvent.EVT_PLAY_URL]; _currentPlayUrl = event[TXVodPlayEvent.EVT_PLAY_URL];
PlayImageSpriteInfo playImageSpriteInfo = PlayImageSpriteInfo(); PlayImageSpriteInfo playImageSpriteInfo = PlayImageSpriteInfo();
...@@ -100,6 +111,7 @@ class SuperPlayerController { ...@@ -100,6 +111,7 @@ class SuperPlayerController {
break; break;
case TXVodPlayEvent.PLAY_EVT_VOD_PLAY_PREPARED: // vodPrepared case TXVodPlayEvent.PLAY_EVT_VOD_PLAY_PREPARED: // vodPrepared
isPrepared = true; isPrepared = true;
addSubTitle();
if (_isMultiBitrateStream) { if (_isMultiBitrateStream) {
List<dynamic>? bitrateListTemp = await _vodPlayerController.getSupportedBitrates(); List<dynamic>? bitrateListTemp = await _vodPlayerController.getSupportedBitrates();
List<FTXBitrateItem> bitrateList = []; List<FTXBitrateItem> bitrateList = [];
...@@ -134,6 +146,7 @@ class SuperPlayerController { ...@@ -134,6 +146,7 @@ class SuperPlayerController {
} }
videoDuration = await _vodPlayerController.getDuration(); videoDuration = await _vodPlayerController.getDuration();
currentDuration = await _vodPlayerController.getCurrentPlaybackTime(); currentDuration = await _vodPlayerController.getCurrentPlaybackTime();
_onSelectTrackInfoWhenPrepare();
break; break;
case TXVodPlayEvent.PLAY_EVT_PLAY_LOADING: // PLAY_EVT_PLAY_LOADING case TXVodPlayEvent.PLAY_EVT_PLAY_LOADING: // PLAY_EVT_PLAY_LOADING
if (playerState == SuperPlayerState.PAUSE) { if (playerState == SuperPlayerState.PAUSE) {
...@@ -186,6 +199,19 @@ class SuperPlayerController { ...@@ -186,6 +199,19 @@ class SuperPlayerController {
_configVideoSize(event); _configVideoSize(event);
_observer?.onResolutionChanged(); _observer?.onResolutionChanged();
break; break;
case TXVodPlayEvent.EVENT_SUBTITLE_DATA:
String subtitleDataStr = event[TXVodPlayEvent.EXTRA_SUBTITLE_DATA] ?? "";
int? startPositionMs = event[TXVodPlayEvent.EXTRA_SUBTITLE_START_POSITION_MS];
int? durationMs = event[TXVodPlayEvent.EXTRA_SUBTITLE_DURATION_MS];
int? trackIndex = event[TXVodPlayEvent.EXTRA_SUBTITLE_TRACK_INDEX];
currentSubtitleData = new TXVodSubtitleData(subtitleDataStr, startPositionMs, durationMs, trackIndex);
_observer?.onSubtitleData(currentSubtitleData);
break;
case TXVodPlayEvent.VOD_PLAY_EVT_SELECT_TRACK_COMPLETE: {
int trackIndex = event[TXVodPlayEvent.EVT_KEY_SELECT_TRACK_INDEX];
int errorCode = event[TXVodPlayEvent.EVT_KEY_SELECT_TRACK_ERROR_CODE];
LogUtils.d(TAG, "SELECT_TRACK_COMPLETE trackIndex: ${trackIndex}, errorCode: ${errorCode}");
}
} }
}); });
_vodNetEventListener = _vodPlayerController.onPlayerNetStatusBroadcast.listen((event) { _vodNetEventListener = _vodPlayerController.onPlayerNetStatusBroadcast.listen((event) {
...@@ -242,6 +268,30 @@ class SuperPlayerController { ...@@ -242,6 +268,30 @@ class SuperPlayerController {
}); });
} }
void _onSelectTrackInfoWhenPrepare() async {
// subtitle track info
List<TXTrackInfo> subtitleTrackList = await _vodPlayerController.getSubtitleTrackInfo();
TXTrackInfo? selectedSubtitleTrack;
for (TXTrackInfo track in subtitleTrackList) {
if (track.isSelected) {
selectedSubtitleTrack = track;
break;
}
}
_updateSubtitleTrackList(subtitleTrackList, selectedSubtitleTrack);
// audio track info
List<TXTrackInfo> audioTrackList = await _vodPlayerController.getAudioTrackInfo();
TXTrackInfo? selectedAudioTrack;
for (TXTrackInfo track in audioTrackList) {
if (track.isSelected) {
selectedAudioTrack = track;
break;
}
}
_updateAudioTrackList(audioTrackList, selectedAudioTrack);
}
void _configVideoSize(Map<dynamic, dynamic> event) { void _configVideoSize(Map<dynamic, dynamic> event) {
int? eventVideoWidth = event[TXVodPlayEvent.EVT_VIDEO_WIDTH]; int? eventVideoWidth = event[TXVodPlayEvent.EVT_VIDEO_WIDTH];
int? eventVideoHeight = event[TXVodPlayEvent.EVT_VIDEO_HEIGHT]; int? eventVideoHeight = event[TXVodPlayEvent.EVT_VIDEO_HEIGHT];
...@@ -311,6 +361,14 @@ class SuperPlayerController { ...@@ -311,6 +361,14 @@ class SuperPlayerController {
} }
} }
void addSubTitle() {
if (videoModel != null && videoModel!.subtitleSources.isNotEmpty) {
for (FSubtitleSourceModel sourceModel in videoModel!.subtitleSources) {
_vodPlayerController.addSubtitleSource(sourceModel.url, sourceModel.name, mimeType: sourceModel.mimeType);
}
}
}
void getInfo(SuperPlayerModel videoModel) { void getInfo(SuperPlayerModel videoModel) {
PlayInfoProtocol temp = PlayInfoProtocol(videoModel); PlayInfoProtocol temp = PlayInfoProtocol(videoModel);
temp.sendRequest((protocol, resultModel) async { temp.sendRequest((protocol, resultModel) async {
...@@ -346,6 +404,34 @@ class SuperPlayerController { ...@@ -346,6 +404,34 @@ class SuperPlayerController {
this.keyFrameInfo = keyFrameInfo; this.keyFrameInfo = keyFrameInfo;
} }
/// select audio track
Future<void> selectAudioTrack(TXTrackInfo trackInfo) async {
if (playerType == SuperPlayerType.VOD) {
List<TXTrackInfo> trackInfoList = await _vodPlayerController.getAudioTrackInfo();
for (TXTrackInfo tempInfo in trackInfoList) {
if(tempInfo.trackIndex == trackInfo.trackIndex) {
_vodPlayerController.selectTrack(tempInfo.trackIndex);
} else {
_vodPlayerController.deselectTrack(tempInfo.trackIndex);
}
}
}
}
/// select subtitle track
Future<void> selectSubtitleTrack(TXTrackInfo trackInfo) async {
if (playerType == SuperPlayerType.VOD) {
List<TXTrackInfo> trackInfoList = await _vodPlayerController.getSubtitleTrackInfo();
for (TXTrackInfo tempInfo in trackInfoList) {
if(tempInfo.trackIndex == trackInfo.trackIndex) {
_vodPlayerController.selectTrack(tempInfo.trackIndex);
} else {
_vodPlayerController.deselectTrack(tempInfo.trackIndex);
}
}
}
}
Future<double> getPlayableDuration() async { Future<double> getPlayableDuration() async {
return await _vodPlayerController.getPlayableDuration(); return await _vodPlayerController.getPlayableDuration();
} }
...@@ -609,6 +695,37 @@ class SuperPlayerController { ...@@ -609,6 +695,37 @@ class SuperPlayerController {
_observer?.onVideoQualityListChange(qualityList, defaultQuality); _observer?.onVideoQualityListChange(qualityList, defaultQuality);
} }
void _updateSubtitleTrackList(List<TXTrackInfo>? subtitleTrackData, TXTrackInfo? selectedTrackInfo) {
subtitleTrackInfoList = subtitleTrackData;
currentSubtitleTrackInfo = selectedTrackInfo;
_observer?.onSubtitleTrackListChange(subtitleTrackInfoList!, currentSubtitleTrackInfo);
}
void onSubtitleRenderModelChange(TXSubtitleRenderModel renderModel) {
_currentSubtitleRenderModel = renderModel;
}
void onSelectSubtitleTrack(TXTrackInfo selectedTrack) {
selectSubtitleTrack(selectedTrack);
_updateSubtitleTrackList(subtitleTrackInfoList, selectedTrack);
}
void _updateAudioTrackList(List<TXTrackInfo>? audioTrackData, TXTrackInfo? selectedTrackInfo) {
audioTrackInfoList = audioTrackData;
currentAudioTrackInfo = selectedTrackInfo;
_observer?.onAudioTrackListChange(audioTrackInfoList!, currentAudioTrackInfo);
}
void onSelectAudioTrack(TXTrackInfo selectedTrack) {
if (selectedTrack.trackIndex == -1) {
setMute(true);
} else{
setMute(false);
selectAudioTrack(selectedTrack);
}
_updateAudioTrackList(audioTrackInfoList, selectedTrack);
}
void _addSimpleEvent(String event) { void _addSimpleEvent(String event) {
Map<String, String> eventMap = {}; Map<String, String> eventMap = {};
eventMap['event'] = event; eventMap['event'] = event;
...@@ -658,6 +775,11 @@ class SuperPlayerController { ...@@ -658,6 +775,11 @@ class SuperPlayerController {
currentQuality = null; currentQuality = null;
currentQualityList?.clear(); currentQualityList?.clear();
_currentProtocol = null; _currentProtocol = null;
currentAudioTrackInfo = null;
audioTrackInfoList = null;
currentSubtitleTrackInfo = null;
subtitleTrackInfoList = null;
currentSubtitleData = null;
// cancel all listener // cancel all listener
_vodPlayEventListener?.cancel(); _vodPlayEventListener?.cancel();
_vodNetEventListener?.cancel(); _vodNetEventListener?.cancel();
...@@ -665,6 +787,7 @@ class SuperPlayerController { ...@@ -665,6 +787,7 @@ class SuperPlayerController {
_liveNetEventListener?.cancel(); _liveNetEventListener?.cancel();
await _vodPlayerController.stop(); await _vodPlayerController.stop();
await _livePlayerController.stop(); await _livePlayerController.stop();
await setMute(false);
_updatePlayerState(SuperPlayerState.INIT); _updatePlayerState(SuperPlayerState.INIT);
} }
......
...@@ -22,6 +22,10 @@ class _SuperPlayerObserver { ...@@ -22,6 +22,10 @@ class _SuperPlayerObserver {
Function onSysBackPress; Function onSysBackPress;
Function onPreparePlayVideo; Function onPreparePlayVideo;
Function onDispose; Function onDispose;
Function(List<TXTrackInfo>? audioTrackList, TXTrackInfo? selectedTrack) onAudioTrackListChange;
Function(List<TXTrackInfo>? subtitleTrackList, TXTrackInfo? selectedTrack) onSubtitleTrackListChange;
Function(TXVodSubtitleData?) onSubtitleData;
_SuperPlayerObserver( _SuperPlayerObserver(
this.onPreparePlayVideo, this.onPreparePlayVideo,
...@@ -44,5 +48,8 @@ class _SuperPlayerObserver { ...@@ -44,5 +48,8 @@ class _SuperPlayerObserver {
this.onVideoImageSpriteAndKeyFrameChanged, this.onVideoImageSpriteAndKeyFrameChanged,
this.onResolutionChanged, this.onResolutionChanged,
this.onSysBackPress, this.onSysBackPress,
this.onAudioTrackListChange,
this.onSubtitleTrackListChange,
this.onSubtitleData,
this.onDispose,); this.onDispose,);
} }
// Copyright (c) 2022 Tencent. All rights reserved.
part of demo_super_player_lib;
class AudioListView extends StatefulWidget {
final AudioTrackController _controller;
final List<TXTrackInfo>? _trackInfoList;
final TXTrackInfo? _currentTrackInfo;
AudioListView(this._controller, this._trackInfoList, this._currentTrackInfo, Key key) : super(key: key);
@override
State<StatefulWidget> createState() {
return AudioListState();
}
}
class AudioListState extends State<AudioListView> {
List<TXTrackInfo>? _trackInfoList;
TXTrackInfo? _currentTXTrackInfo;
final TXTrackInfo closeItem = TXTrackInfo(FSPLocal.current.txAudioTrackClose, -1, 0);
@override
void initState() {
super.initState();
}
@override
Widget build(BuildContext context) {
_trackInfoList = List.from(widget._trackInfoList as Iterable);
_trackInfoList?.add(closeItem);
_currentTXTrackInfo = widget._currentTrackInfo;
return Positioned(
right: 0,
top: 0,
bottom: 0,
child: Container(
height: double.infinity,
width: 300,
padding: const EdgeInsets.only(left: 15, right: 15, top: 15, bottom: 15),
decoration: const BoxDecoration(color: Color(ColorResource.COLOR_TRANS_BLACK)),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(FSPLocal.current.txAudioTrackTitle, style: TextStyle(fontSize: 16, color: Colors.grey)),
Expanded(
child: Center(
child: _trackInfoList != null
? ListView.builder(
itemCount: _trackInfoList?.length,
scrollDirection: Axis.vertical,
shrinkWrap: true,
itemBuilder: (context, index) {
return InkWell(
onTap: () => widget._controller.onSelectAudioTrackInfo(_trackInfoList![index]),
child: Container(
margin: const EdgeInsets.only(top: 10, bottom: 10),
child: Text(
getAudioName(_trackInfoList!, index),
textAlign: TextAlign.center,
style: getTexStyle(index),
),
),
);
})
: Container(),
))
],
)),
);
}
TextStyle getTexStyle(int index) {
if ( _currentTXTrackInfo?.trackIndex == _trackInfoList![index].trackIndex) {
return ThemeResource.getCheckedTextStyle();
} else {
return ThemeResource.getCommonTextStyle();
}
}
String getAudioName(List<TXTrackInfo> trackData, int index) {
TXTrackInfo trackInfo = trackData[index];
String name = trackInfo.name;
if (name.isEmpty) {
name = FSPLocal.current.txAudioTrackTitleItem + "${trackInfo.trackIndex}";
}
return name;
}
void updateAudioTrack(List<TXTrackInfo>? trackDataList, TXTrackInfo? trackInfo) {
if (_trackInfoList != trackDataList || _currentTXTrackInfo != trackInfo) {
setState(() {
_trackInfoList = trackDataList;
_currentTXTrackInfo = trackInfo;
});
}
}
}
class AudioTrackController {
Function(TXTrackInfo) onSelectAudioTrackInfo;
AudioTrackController(this.onSelectAudioTrackInfo);
}
...@@ -125,7 +125,7 @@ class _SuperPlayerMoreViewState extends State<SuperPlayerMoreView> { ...@@ -125,7 +125,7 @@ class _SuperPlayerMoreViewState extends State<SuperPlayerMoreView> {
child: Row( child: Row(
children: [ children: [
Text( Text(
FSPLocal.current.txSpwMultiPlay, FSPLocal.current.txSpwHardwareAccel,
textAlign: TextAlign.center, textAlign: TextAlign.center,
style: ThemeResource.getCommonLabelTextStyle(), style: ThemeResource.getCommonLabelTextStyle(),
), ),
......
part of demo_super_player_lib;
class SubtitleDisplayView extends StatefulWidget {
final TXVodSubtitleData subtitleData;
final TXSubtitleRenderModel? renderModel;
final AlignmentGeometry alignment;
SubtitleDisplayView(this.subtitleData, {
this.alignment = Alignment.bottomCenter,
this.renderModel
});
@override
State<StatefulWidget> createState() {
return _SubtitleDisplayViewState();
}
}
class _SubtitleDisplayViewState extends State<SubtitleDisplayView> {
@override
Widget build(BuildContext context) {
String subtitledDataStr = widget.subtitleData.subtitleData ?? "";
int fontColorInt = widget.renderModel?.fontColor ??
SubtitleTrackController.defaultFontColor;
Color fontColor = Color(fontColorInt);
double fontSize = widget.renderModel?.fontSize ??
SubtitleTrackController.defaultFontSize;
int outlineColorInt = widget.renderModel?.outlineColor ??
SubtitleTrackController.defaultOutlineColor;
Color outlineColor = Color(outlineColorInt);
double outlineWidth = widget.renderModel?.outlineWidth ??
SubtitleTrackController.defaultOutlineWidth;
bool isBold = widget.renderModel?.isBondFontStyle ??
SubtitleTrackController.defaultFondBold == "1";
return Align(
alignment: widget.alignment,
child: Stack(
children: <Widget>[
// Stroked text as border.
Text(
subtitledDataStr,
textAlign: TextAlign.center,
style: TextStyle(
fontWeight: isBold ? FontWeight.bold : FontWeight.normal,
fontSize: fontSize,
foreground: Paint()
..style = PaintingStyle.stroke
..strokeWidth = outlineWidth
..color = outlineColor,
),
),
// Solid text as fill.
Text(
subtitledDataStr,
textAlign: TextAlign.center,
style: TextStyle(
fontWeight: isBold ? FontWeight.bold : FontWeight.normal,
fontSize: fontSize,
color: fontColor,
),
),
],
),);
}
}
...@@ -7,9 +7,12 @@ class _VideoTitleView extends StatefulWidget { ...@@ -7,9 +7,12 @@ class _VideoTitleView extends StatefulWidget {
final bool initIsFullScreen; final bool initIsFullScreen;
final bool showDownload; final bool showDownload;
final bool isDownloaded; final bool isDownloaded;
bool showAudioView = false;
bool showSubtitleView = false;
const _VideoTitleView(this._controller, this.initIsFullScreen, this._title, this.showDownload, this.isDownloaded, _VideoTitleView(
GlobalKey<_VideoTitleViewState> key) this._controller, this.initIsFullScreen, this._title, this.showDownload, this.isDownloaded, GlobalKey<_VideoTitleViewState> key,
{this.showAudioView = false, this.showSubtitleView = false})
: super(key: key); : super(key: key);
@override @override
...@@ -54,7 +57,39 @@ class _VideoTitleViewState extends State<_VideoTitleView> { ...@@ -54,7 +57,39 @@ class _VideoTitleViewState extends State<_VideoTitleView> {
_title, _title,
style: const TextStyle(fontSize: 11, color: Colors.white), style: const TextStyle(fontSize: 11, color: Colors.white),
), ),
const Expanded(child: SizedBox()), // const Expanded(child: SizedBox()),
// subtitle
Visibility(
visible: _isFullScreen && widget.showSubtitleView,
child: InkWell(
onTap: _onTapSubtitle,
child: Container(
padding: const EdgeInsets.all(8),
margin: const EdgeInsets.only(left: 8, right: 8),
width: 40,
height: 40,
child: const Image(
image: AssetImage("images/superplayer_multi_subtitle.png", package: PlayerConstants.PKG_NAME),
),
),
),
),
// audio
Visibility(
visible: _isFullScreen && widget.showAudioView,
child: InkWell(
onTap: _onTapAudio,
child: Container(
padding: const EdgeInsets.all(8),
margin: const EdgeInsets.only(left: 8, right: 8),
width: 40,
height: 40,
child: const Image(
image: AssetImage("images/superplayer_multi_audio.png", package: PlayerConstants.PKG_NAME),
),
),
),
),
// download // download
Visibility( Visibility(
visible: _isFullScreen && widget.showDownload, visible: _isFullScreen && widget.showDownload,
...@@ -100,6 +135,14 @@ class _VideoTitleViewState extends State<_VideoTitleView> { ...@@ -100,6 +135,14 @@ class _VideoTitleViewState extends State<_VideoTitleView> {
); );
} }
void _onTapSubtitle() {
widget._controller._onTapSubtitle();
}
void _onTapAudio() {
widget._controller._onTapAudio();
}
void _onTapMore() { void _onTapMore() {
widget._controller._onTapMore(); widget._controller._onTapMore();
} }
...@@ -133,6 +176,8 @@ class _VideoTitleController { ...@@ -133,6 +176,8 @@ class _VideoTitleController {
final Function _onTapBack; final Function _onTapBack;
final Function _onTapMore; final Function _onTapMore;
final Function _onTapDownload; final Function _onTapDownload;
final Function _onTapSubtitle;
final Function _onTapAudio;
_VideoTitleController(this._onTapBack, this._onTapMore, this._onTapDownload); _VideoTitleController(this._onTapBack, this._onTapMore, this._onTapDownload, this._onTapSubtitle, this._onTapAudio);
} }
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论