提交 83a8f6a8 authored 作者: dokieyang's avatar dokieyang

1、fix bug: play with fileId & psign

2、add more features to SuperPlayer panel
上级 4ac57839
......@@ -116,7 +116,7 @@ pod 'TXLiteAVSDK_Professional' //Professional版
### 集成过程中常见问题
执行`flutter doctor`命令检查运行环境,知道出现”No issues found!“。
执行`flutter doctor`命令检查运行环境,直到出现”No issues found!“。
执行`flutter pub get`确保所有依赖的组件都已更新成功。
......@@ -175,10 +175,17 @@ class _TestState extends State<Test> {
@override
Widget build(BuildContext context) {
return Container(
height: 220,
color: Colors.black,
child: AspectRatio(aspectRatio: _aspectRatio, child: TXPlayerVideo(controller: _controller)));
return Scaffold(
body: Container(
height: 220,
color: Colors.black,
child: AspectRatio(aspectRatio: _aspectRatio, child: TXPlayerVideo(controller: _controller))));
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
}
```
......@@ -301,6 +308,13 @@ class _DemoSuperplayerState extends State<DemoSuperplayer> {
model.title = "腾讯云音视频";
_controller.playWithModel(model);
}
@override
void dispose() {
// must invoke when page exit.
_controller.releasePlayer();
super.dispose();
}
}
```
......
......@@ -48,7 +48,7 @@ android {
dependencies {
compileOnly 'androidx.annotation:annotation:1.2.0'
//此处仅处理因插件项目内报红显示的处理方式
// compileOnly files("$flutterRoot/bin/cache/artifacts/engine/android-arm/flutter.jar")
//compileOnly files("$flutterRoot/bin/cache/artifacts/engine/android-arm/flutter.jar")
}
}
......
// Copyright (c) 2022 Tencent. All rights reserved.
package com.tencent.vod.flutter;
import java.util.concurrent.atomic.AtomicInteger;
......@@ -16,7 +17,7 @@ public class FTXBasePlayer {
mPlayerId = mAtomicId.incrementAndGet();
}
public void destory() {
public void destroy() {
}
......
// Copyright (c) 2022 Tencent. All rights reserved.
package com.tencent.vod.flutter;
/**
* 通用事件码
*/
public class FTXEvent {
/*
音量变化
*/
public static final int EVENT_VOLUME_CHANGED = 0x01;
/*
失去音量输出播放焦点
*/
public static final int EVENT_AUDIO_FOCUS_PAUSE = 0x02;
/*
获得音量输出焦点
*/
public static final int EVENT_AUDIO_FOCUS_PLAY = 0x03;
}
// Copyright (c) 2022 Tencent. All rights reserved.
package com.tencent.vod.flutter;
import android.app.Activity;
......@@ -87,7 +88,7 @@ public class FTXLivePlayer extends FTXBasePlayer implements MethodChannel.Method
}
@Override
public void destory() {
public void destroy() {
if (mLivePlayer != null) {
mLivePlayer.stopPlay(true);
mLivePlayer = null;
......@@ -203,7 +204,6 @@ public class FTXLivePlayer extends FTXBasePlayer implements MethodChannel.Method
Log.d(TAG, "startPlay:");
if (mLivePlayer != null) {
mLivePlayer.setSurface(mSurface);
mLivePlayer.enableHardwareDecode(true);
mLivePlayer.setPlayListener(this);
TXLivePlayConfig config = new TXLivePlayConfig();
config.setEnableMessage(true);
......
// Copyright (c) 2022 Tencent. All rights reserved.
package com.tencent.vod.flutter;
import java.util.LinkedList;
......
// Copyright (c) 2022 Tencent. All rights reserved.
package com.tencent.vod.flutter;
import android.text.TextUtils;
......
// Copyright (c) 2022 Tencent. All rights reserved.
package com.tencent.vod.flutter;
import android.graphics.Bitmap;
......@@ -50,6 +51,9 @@ public class FTXVodPlayer extends FTXBasePlayer implements MethodChannel.MethodC
private static final int Uninitialized = -101;
private TextureRegistry.SurfaceTextureEntry mSurfaceTextureEntry;
private boolean mEnableHardwareDecode = true;
private boolean mHardwareDecodeFail = false;
public FTXVodPlayer(FlutterPlugin.FlutterPluginBinding flutterPluginBinding) {
super();
......@@ -87,7 +91,7 @@ public class FTXVodPlayer extends FTXBasePlayer implements MethodChannel.MethodC
@Override
public void destory() {
public void destroy() {
if (mVodPlayer != null) {
mVodPlayer.stopPlay(true);
mVodPlayer = null;
......@@ -114,8 +118,8 @@ public class FTXVodPlayer extends FTXBasePlayer implements MethodChannel.MethodC
}
@Override
public void onPlayEvent(TXVodPlayer txVodPlayer, int i, Bundle bundle) {
if (i == TXLiveConstants.PLAY_EVT_CHANGE_RESOLUTION) {
public void onPlayEvent(TXVodPlayer txVodPlayer, int event, Bundle bundle) {
if (event == TXLiveConstants.PLAY_EVT_CHANGE_RESOLUTION) {
String EVT_PARAM3 = bundle.getString("EVT_PARAM3");
if (!TextUtils.isEmpty(EVT_PARAM3)) {
String[] array = EVT_PARAM3.split(",");
......@@ -132,14 +136,33 @@ public class FTXVodPlayer extends FTXBasePlayer implements MethodChannel.MethodC
bundle.putInt("videoTop", videoTop);
bundle.putInt("videoRight", videoRight);
bundle.putInt("videoBottom", videoBottom);
mEventSink.success(getParams(i, bundle));
mEventSink.success(getParams(event, bundle));
return;
}
}
int width = bundle.getInt(TXLiveConstants.EVT_PARAM1, 0);
int height = bundle.getInt(TXLiveConstants.EVT_PARAM2, 0);
if (!mEnableHardwareDecode || mHardwareDecodeFail) {
setDefaultBufferSizeForSoftDecode(width, height);
}
} else if (event == TXLiveConstants.PLAY_WARNING_HW_ACCELERATION_FAIL) {
mHardwareDecodeFail = true;
}
mEventSink.success(getParams(event, bundle));
}
// surface 的大小默认是宽高为1,当硬解失败时或使用软解时,软解会依赖surface的窗口渲染,不更新会导致只有1px的内容
private void setDefaultBufferSizeForSoftDecode(int width, int height) {
mSurfaceTextureEntry.surfaceTexture().setDefaultBufferSize(width, height);
if (mSurface != null) {
mSurface.release();
}
mEventSink.success(getParams(i, bundle));
mSurface = new Surface(mSurfaceTextureEntry.surfaceTexture());
mVodPlayer.setSurface(mSurface);
}
@Override
public void onNetStatus(TXVodPlayer txVodPlayer, Bundle bundle) {
mNetStatusSink.success(getParams(0, bundle));
......@@ -151,7 +174,7 @@ public class FTXVodPlayer extends FTXBasePlayer implements MethodChannel.MethodC
boolean onlyAudio = call.argument("onlyAudio");
long id = init(onlyAudio);
result.success(id);
} else if (call.method.equals("setIsAutoPlay")) {
} else if (call.method.equals("setAutoPlay")) {
boolean loop = call.argument("isAutoPlay");
setIsAutoPlay(loop);
result.success(null);
......@@ -285,6 +308,7 @@ public class FTXVodPlayer extends FTXBasePlayer implements MethodChannel.MethodC
return mSurfaceTextureEntry == null ? -1 : mSurfaceTextureEntry.id();
}
void setPlayer(boolean onlyAudio) {
if (!onlyAudio) {
mSurfaceTextureEntry = mFlutterPluginBinding.getTextureRegistry().createSurfaceTexture();
......@@ -293,7 +317,6 @@ public class FTXVodPlayer extends FTXBasePlayer implements MethodChannel.MethodC
if (mVodPlayer != null) {
mVodPlayer.setSurface(mSurface);
mVodPlayer.enableHardwareDecode(true);
}
}
}
......@@ -342,6 +365,7 @@ public class FTXVodPlayer extends FTXBasePlayer implements MethodChannel.MethodC
if (mVodPlayer != null) {
return mVodPlayer.stopPlay(isNeedClearLastImg);
}
mHardwareDecodeFail = false;
return Uninitialized;
}
......@@ -520,6 +544,7 @@ public class FTXVodPlayer extends FTXBasePlayer implements MethodChannel.MethodC
boolean enableHardwareDecode(boolean enable) {
if (mVodPlayer != null) {
mEnableHardwareDecode = enable;
return mVodPlayer.enableHardwareDecode(enable);
}
return false;
......
......@@ -737,7 +737,7 @@ _controller.onPlayerState.listen((val) { });
![](https://mc.qcloudimg.com/static/img/7331417ebbdfe6306fe96f4b76c8d0ad/image.jpg)
```dart
// 播放视频 A: 如果将 isAutoPlay 设置为 YES, 那么 startPlay 调用会立刻开始视频的加载和播放
// 播放视频 A: 如果将 isAutoPlay 设置为 true, 那么 startPlay 调用会立刻开始视频的加载和播放
String url_A = "http://1252463788.vod2.myqcloud.com/xxxxx/v.f10.mp4";
await _controller_A.setIsAutoPlay(isAutoPlay: true);
await _controller_A.play(url_A);
......
......@@ -44,3 +44,4 @@ app.*.map.json
/android/app/debug
/android/app/profile
/android/app/release
pubspec.lock
\ No newline at end of file
......@@ -25,13 +25,13 @@ apply plugin: 'com.android.application'
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
android {
compileSdkVersion 28
compileSdkVersion 31
defaultConfig {
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
applicationId "com.example.super_player_example"
minSdkVersion 19
targetSdkVersion 28
targetSdkVersion 31
versionCode flutterVersionCode.toInteger()
versionName flutterVersionName
}
......
......@@ -24,7 +24,7 @@ Flutter/flutter_assets/
Flutter/flutter_export_environment.sh
ServiceDefinitions.json
Runner/GeneratedPluginRegistrant.*
/Podfile.lock
# Exceptions to above rules.
!default.mode1v3
!default.mode2v3
......
PODS:
- auto_orientation (0.0.1):
- Flutter
- Flutter (1.0.0)
- super_player (1.0.1):
- Flutter
- TXLiteAVSDK_Player
- TXLiteAVSDK_Player (9.5.29016)
DEPENDENCIES:
- auto_orientation (from `.symlinks/plugins/auto_orientation/ios`)
- Flutter (from `Flutter`)
- super_player (from `.symlinks/plugins/super_player/ios`)
SPEC REPOS:
trunk:
- TXLiteAVSDK_Player
EXTERNAL SOURCES:
auto_orientation:
:path: ".symlinks/plugins/auto_orientation/ios"
Flutter:
:path: Flutter
super_player:
:path: ".symlinks/plugins/super_player/ios"
SPEC CHECKSUMS:
auto_orientation: 2941c44ebe5c3d41016001597ab03e81a92a26ce
Flutter: 50d75fe2f02b26cc09d224853bb45737f8b3214a
super_player: 7704aa01779b7093d5667994f382729b5e0b3ed6
TXLiteAVSDK_Player: 5a9cea3ba4e77fc8993b19523afbecdfc1a803ce
PODFILE CHECKSUM: 503cb461ebd1e4de100eb5092af3ca3f013abcd5
COCOAPODS: 1.11.3
// Copyright (c) 2022 Tencent. All rights reserved.
import 'package:flutter/material.dart';
import 'package:flutter_easyloading/flutter_easyloading.dart';
import 'package:super_player/super_player.dart';
......@@ -26,13 +27,7 @@ class _DemoSuperplayerState extends State<DemoSuperplayer> {
_controller.onSimplePlayerEventBroadcast.listen((event) {
String evtName = event["event"];
if (evtName == SuperPlayerViewEvent.onStartFullScreenPlay) {
setState(() {
_isFullScreen = true;
});
} else if (evtName == SuperPlayerViewEvent.onStopFullScreenPlay) {
setState(() {
_isFullScreen = false;
});
} else {
print(evtName);
}
......@@ -122,7 +117,7 @@ class _DemoSuperplayerState extends State<DemoSuperplayer> {
showDialog(
context: context,
builder: (context) {
return DemoInputDialog("", 0, "", (String url, int appId, String fileId) {
return DemoInputDialog("", 0, "", (String url, int appId, String fileId, String pSign) {
SuperPlayerModel model = new SuperPlayerModel();
model.appId = appId;
if (url.isNotEmpty) {
......@@ -130,13 +125,16 @@ class _DemoSuperplayerState extends State<DemoSuperplayer> {
} else if (appId != 0 && fileId.isNotEmpty) {
model.videoId = new SuperPlayerVideoId();
model.videoId!.fileId = fileId;
if (pSign.isNotEmpty) {
model.videoId!.psign = pSign;
}
} else {
EasyLoading.showError("请输入播放地址!");
return;
}
playCurrentModel(model);
});
}, needPisgn: true);
});
}
......@@ -153,14 +151,17 @@ class _DemoSuperplayerState extends State<DemoSuperplayer> {
model.appId = 1500005830;
model.videoId = new SuperPlayerVideoId();
model.videoId!.fileId = "8602268011437356984";
model.title = "云点播";
model.title = "云点播(fileId播放)";
model.playAction = playAction;
models.add(model);
model = SuperPlayerModel();
model.appId = 1252463788;
model.appId = 1254432039;
model.title = "为什么贫穷(fileId+psign播放)";
model.videoId = new SuperPlayerVideoId();
model.videoId!.fileId = "5285890781763144364";
model.videoId!.fileId = '5285890816303742312';
model.videoId!.psign =
'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhcHBJZCI6MTI1NDQzMjAzOSwiZmlsZUlkIjoiNTI4NTg5MDgxNjMwMzc0MjMxMiIsImN1cnJlbnRUaW1lU3RhbXAiOjE2MTcyNTc0ODMsInBjZmciOiJiYXNpY0RybVByZXNldCIsInVybEFjY2Vzc0luZm8iOnt9LCJkcm1MaWNlbnNlSW5mbyI6e319.2H1t9dKPpdA41a8t1WwI631OWC18HGl60ccBDLylCKE';
model.playAction = playAction;
models.add(model);
......@@ -215,6 +216,8 @@ class _DemoSuperplayerState extends State<DemoSuperplayer> {
void dispose() {
// must invoke when page exit.
_controller.releasePlayer();
// restore page brightness
SuperPlayerPlugin.restorePageBrightness();
super.dispose();
}
}
// Copyright (c) 2022 Tencent. All rights reserved.
import 'dart:async';
import 'package:flutter/material.dart';
......@@ -13,10 +14,9 @@ class DemoTXLivePlayer extends StatefulWidget {
_DemoTXLivelayerState createState() => _DemoTXLivelayerState();
}
class _DemoTXLivelayerState extends State<DemoTXLivePlayer> with WidgetsBindingObserver{
class _DemoTXLivelayerState extends State<DemoTXLivePlayer> with WidgetsBindingObserver {
late TXLivePlayerController _controller;
double _aspectRatio = 0;
double _aspectRatio = 16.0 / 9.0;
double _progress = 0.0;
int _volume = 100;
bool _isMute = false;
......@@ -32,22 +32,31 @@ class _DemoTXLivelayerState extends State<DemoTXLivePlayer> with WidgetsBindingO
_controller = TXLivePlayerController();
_controller.onPlayerEventBroadcast.listen((event) {//订阅事件分发
if(event["event"] == TXVodPlayEvent.PLAY_EVT_PLAY_PROGRESS) {
_controller.onPlayerEventBroadcast.listen((event) {
//订阅事件分发
if (event["event"] == TXVodPlayEvent.PLAY_EVT_PLAY_PROGRESS) {
_progress = event["EVT_PLAY_PROGRESS"].toDouble();
_maxLiveProgressTime = _progress >= _maxLiveProgressTime ? _progress : _maxLiveProgressTime;
progressSliderKey.currentState?.updatePorgess(1, _maxLiveProgressTime);
}else if (event["event"] == TXVodPlayEvent.PLAY_EVT_PLAY_BEGIN || event["event"] == TXVodPlayEvent.PLAY_EVT_RCV_FIRST_I_FRAME) {//首帧出现
} else if (event["event"] == TXVodPlayEvent.PLAY_EVT_PLAY_BEGIN ||
event["event"] == TXVodPlayEvent.PLAY_EVT_RCV_FIRST_I_FRAME) {
//首帧出现
_isStop = false;
EasyLoading.dismiss();
}else if (event["event"] == TXVodPlayEvent.PLAY_EVT_STREAM_SWITCH_SUCC) {//切换流成功
} else if (event["event"] == TXVodPlayEvent.PLAY_EVT_STREAM_SWITCH_SUCC) {
//切换流成功
EasyLoading.dismiss();
if (_url == "http://liteavapp.qcloud.com/live/liteavdemoplayerstreamid_demo1080p.flv") {
EasyLoading.showSuccess('切换到1080p!');
}else {
} else {
EasyLoading.showSuccess('切换到480p!');
}
} else if (event["event"] == TXVodPlayEvent.PLAY_ERR_STREAM_SWITCH_FAIL) {
EasyLoading.dismiss();
EasyLoading.showError("切流失败");
switchUrl();
}else if(event["event"] == TXVodPlayEvent.PLAY_EVT_CHANGE_RESOLUTION) {
LogUtils.w("PLAY_EVT_CHANGE_RESOLUTION", event);
}
});
......@@ -55,14 +64,15 @@ class _DemoTXLivelayerState extends State<DemoTXLivePlayer> with WidgetsBindingO
double w = (event[TXVodNetEvent.NET_STATUS_VIDEO_WIDTH]).toDouble();
double h = (event[TXVodNetEvent.NET_STATUS_VIDEO_HEIGHT]).toDouble();
if(w > 0 && h > 0) {
if (w > 0 && h > 0) {
setState(() {
_aspectRatio = 1.0 * w / h;
});
}
});
_controller.onPlayerState.listen((event) {//订阅状态变化
_controller.onPlayerState.listen((event) {
//订阅状态变化
debugPrint("播放状态 ${event!.name}");
});
......@@ -98,6 +108,14 @@ class _DemoTXLivelayerState extends State<DemoTXLivePlayer> with WidgetsBindingO
}
}
void switchUrl() {
if (_url == "http://liteavapp.qcloud.com/live/liteavdemoplayerstreamid_demo480p.flv") {
_url = "http://liteavapp.qcloud.com/live/liteavdemoplayerstreamid_demo1080p.flv";
} else {
_url = "http://liteavapp.qcloud.com/live/liteavdemoplayerstreamid_demo480p.flv";
}
}
@override
Widget build(BuildContext context) {
return Container(
......@@ -116,7 +134,7 @@ class _DemoTXLivelayerState extends State<DemoTXLivePlayer> with WidgetsBindingO
child: Column(
children: [
Container(
height: 250,
height: 220,
color: Colors.black,
child: Center(
child: _aspectRatio > 0
......@@ -203,17 +221,8 @@ class _DemoTXLivelayerState extends State<DemoTXLivePlayer> with WidgetsBindingO
EasyLoading.showError('已经停止播放,请重新播放');
return;
}
if (_url ==
"http://liteavapp.qcloud.com/live/liteavdemoplayerstreamid_demo480p.flv") {
_url =
"http://liteavapp.qcloud.com/live/liteavdemoplayerstreamid_demo1080p.flv";
_controller.switchStream(_url);
} else {
_url =
"http://liteavapp.qcloud.com/live/liteavdemoplayerstreamid_demo480p.flv";
_controller.switchStream(_url);
}
switchUrl();
_controller.switchStream(_url);
EasyLoading.show(status: 'loading...');
},
......@@ -241,17 +250,17 @@ class _DemoTXLivelayerState extends State<DemoTXLivePlayer> with WidgetsBindingO
),
),
new GestureDetector(
onTap: () {
onClickVolume();
},
child: Container(
alignment: Alignment.center,
child: Text(
"调整音量",
style: TextStyle(fontSize: 18, color: Colors.blue),
),
onTap: () {
onClickVolume();
},
child: Container(
alignment: Alignment.center,
child: Text(
"调整音量",
style: TextStyle(fontSize: 18, color: Colors.blue),
),
),
),
],
)),
Expanded(
......@@ -260,9 +269,7 @@ class _DemoTXLivelayerState extends State<DemoTXLivePlayer> with WidgetsBindingO
children: [
Container(
height: 100,
child: IconButton(
icon: new Image.asset('images/addp.png'),
onPressed: () => {onPressed()}),
child: IconButton(icon: new Image.asset('images/addp.png'), onPressed: () => {onPressed()}),
)
],
)),
......@@ -285,8 +292,7 @@ class _DemoTXLivelayerState extends State<DemoTXLivePlayer> with WidgetsBindingO
showDialog(
context: context,
builder: (context) {
return DemoInputDialog("", 0, "",
(String url, int appId, String fileId) {
return DemoInputDialog("", 0, "", (String url, int appId, String fileId,String pSign) {
_url = url;
_controller.stop();
if (url.isNotEmpty) {
......
// Copyright (c) 2022 Tencent. All rights reserved.
import 'dart:async';
import 'package:flutter/material.dart';
......@@ -25,6 +26,7 @@ class _MyAppState extends State<MyApp> {
super.initState();
initPlatformState();
initPlayerLicense();
SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]);
LogUtils.logOpen = true;
}
......
// Copyright (c) 2022 Tencent. All rights reserved.
part of demo_super_player_lib;
/// v2 request data parser
......@@ -58,7 +59,6 @@ class PlayInfoParserV2 implements PlayInfoParser {
int code = root['code'];
String message = root['message'];
String warning = root['warning'];
LogUtils.d(TAG, "_getVodListData,code=$code,message=$message,warning=$warning");
if (code != 0) {
return;
}
......
// Copyright (c) 2022 Tencent. All rights reserved.
part of demo_super_player_lib;
/// v4 request data parser
......@@ -95,9 +96,13 @@ class PlayInfoParserV4 implements PlayInfoParser {
if (null != media['imageSpriteInfo']) {
Map<String, dynamic> imageSpriteInfoJson = media['imageSpriteInfo'];
imageSpriteInfo = PlayImageSpriteInfo();
imageSpriteInfo?.webVttUrl = imageSpriteInfoJson['webVttUrl'] ?? "";
List<String> imageUrls = imageSpriteInfoJson['imageUrls'];
imageSpriteInfo?.imageUrls = imageUrls;
if (imageSpriteInfoJson != null) {
imageSpriteInfo?.webVttUrl = imageSpriteInfoJson['webVttUrl'] ?? "";
List<String> imageUrls = imageSpriteInfoJson['imageUrls'] == null
? List.empty()
: imageSpriteInfoJson['imageUrls'].cast<String>();
imageSpriteInfo?.imageUrls = imageUrls;
}
}
_parseKeyFrameDescList(media);
......
// Copyright (c) 2022 Tencent. All rights reserved.
part of demo_super_player_lib;
/// play info parser interface
......
// Copyright (c) 2022 Tencent. All rights reserved.
part of demo_super_player_lib;
/// request handler with tencent fileId
......@@ -32,7 +33,7 @@ class PlayInfoProtocol {
int code = root['code'];
String message = root['message'];
String warning = root['warning'];
LogUtils.d(TAG, "_getVodListData,code=$code,message=$message,warning=$warning");
LogUtils.d(TAG, "_getVodListData,code=($code, ${PlayInfoProtocol.GETPLAYINFOV4_ERROR_CODE_MAP[code]}),message=$message,warning=$warning");
if (code != 0) {
onError(code, message);
return;
......@@ -66,7 +67,7 @@ class PlayInfoProtocol {
return null;
}
String makeQueryString(String? pcfg, String? psign, String? content) {
static String makeQueryString(String? pcfg, String? psign, String? content) {
String result = "";
if (null != pcfg) {
result += "pcfg=$pcfg&";
......@@ -137,4 +138,24 @@ class PlayInfoProtocol {
String? getDRMType() {
return null == _playInfoParser ? null : _playInfoParser?.drmType;
}
// getplayinfo/v4错误码
// http状态码 200 403
// 403一般鉴权信息不通过或者请求不合法
// 状态码为200的时候才会有http body
// code错误码[1000-2000)请求有问题,
// code错误码[2000-3000)服务端错误,可发起重试
static Map<int, String> GETPLAYINFOV4_ERROR_CODE_MAP = {
0 : 'success',
1001 : '文件不存在',
1002 : '试看时长不合法',
1003 : 'pcfg不唯一',
1004 : 'license过期',
1005 : '没有自适应码流',
1006 : '请求格式不合法',
1007 : '用户存在',
1008 : '没带防盗链信息',
1009 : 'psign检查失败',
1010 : '其他错误',
2001 : '内部错误',
};
}
// Copyright (c) 2022 Tencent. All rights reserved.
part of demo_super_player_lib;
/// request data handler
......@@ -13,8 +14,11 @@ class SuperVodDataLoader {
String field = model.videoId != null
? (model.videoId as SuperPlayerVideoId).fileId
: "";
String psign = model.videoId != null
? (model.videoId as SuperPlayerVideoId).psign
: "";
var url = _BASE_URL + "/$appId/$field";
var query = makeQueryString(null, null, -1, null);
var query = PlayInfoProtocol.makeQueryString(null, psign, null);
if (query != null) {
url = url + "?" + query;
}
......@@ -29,7 +33,7 @@ class SuperVodDataLoader {
int code = root['code'];
String message = root['message'];
String warning = root['warning'];
LogUtils.d(TAG, "_getVodListData,code=$code,message=$message,warning=$warning");
LogUtils.d(TAG, "_getVodListData,code=($code, ${PlayInfoProtocol.GETPLAYINFOV4_ERROR_CODE_MAP[code]}),message=$message,warning=$warning");
if (code != 0) {
return;
}
......@@ -80,26 +84,4 @@ class SuperVodDataLoader {
model.title = newTitle;
}
}
/// make fileId request url
String makeQueryString(String? timeout, String? us, int exper, String? sign) {
var str = new StringBuffer();
if (timeout != null) {
str.write("t=" + timeout + "&");
}
if (us != null) {
str.write("us=" + us + "&");
}
if (sign != null) {
str.write("sign=" + sign + "&");
}
if (exper >= 0) {
str.write("exper=$exper" + "&");
}
String result = str.toString();
if (result.length > 1) {
result = result.substring(0, result.length - 1);
}
return result;
}
}
// Copyright (c) 2022 Tencent. All rights reserved.
part of demo_super_player_lib;
/// colors resource
class ColorResource {
static const COLOR_MAIN_THEME = 0xFFFF4C58;
static const COLOR_GRAY = 0xFFBBBBBB;
static const COLOR_TRANS_BLACK = 0xBB000000;
static const COLOR_WHITE = 0xFFFFFFFF;
}
\ No newline at end of file
// Copyright (c) 2022 Tencent. All rights reserved.
part of demo_super_player_lib;
/// string resource
......@@ -10,4 +11,9 @@ class StringResource {
static const QUALITY_FHD2 = "超清";
static const QUALITY_2K = "2K";
static const QUALITY_4K = "4k";
static const VOICE_LABEL = "声音";
static const BRIGHTNESS_LABEL = "亮度";
static const MULITIPE_SPEED_PLAY_LABEL = "多倍速播放";
static const HARDWARE_ACCE_LABEL = "硬件加速";
}
// Copyright (c) 2022 Tencent. All rights reserved.
part of demo_super_player_lib;
/// 样式资源
class ThemeResource {
/// 获得通用进度条样式
static ThemeData getCommonSliderTheme() {
return ThemeData(
sliderTheme: SliderThemeData(
trackHeight: 2,
thumbColor: Color(ColorResource.COLOR_MAIN_THEME),
thumbShape: RoundSliderThumbShape(enabledThumbRadius: 4),
overlayColor: Colors.white,
overlayShape: RoundSliderOverlayShape(overlayRadius: 10),
activeTrackColor: Color(ColorResource.COLOR_MAIN_THEME),
inactiveTrackColor: Color(ColorResource.COLOR_GRAY),
));
}
static TextStyle getCommonLabelTextStyle() {
return TextStyle(fontSize: 14, color: Colors.white);
}
static TextStyle getCheckedLabelTextStyle() {
return TextStyle(fontSize: 14, color: Color(ColorResource.COLOR_MAIN_THEME));
}
static TextStyle getCommonTextStyle() {
return TextStyle(fontSize: 13, color: Colors.white);
}
static TextStyle getCheckedTextStyle() {
return TextStyle(fontSize: 13, color: Color(ColorResource.COLOR_MAIN_THEME));
}
}
// Copyright (c) 2022 Tencent. All rights reserved.
library demo_super_player_lib;
import 'dart:async';
......@@ -8,6 +9,7 @@ import 'dart:math';
import 'package:auto_orientation/auto_orientation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/services.dart';
import 'package:super_player/super_player.dart';
......@@ -25,5 +27,8 @@ part 'ui/superplayer_bottom_view.dart';
part 'ui/superplayer_quality_view.dart';
part 'ui/superplayer_title_view.dart';
part 'ui/superplayer_widget.dart';
part 'ui/superplayer_cover_view.dart';
part 'ui/superplayer_more_view.dart';
part 'common/color_resource.dart';
part 'common/string_resource.dart';
\ No newline at end of file
part 'common/string_resource.dart';
part 'common/theme_resource.dart';
\ No newline at end of file
// Copyright (c) 2022 Tencent. All rights reserved.
part of demo_super_player_lib;
enum SuperPlayerState {
......
// Copyright (c) 2022 Tencent. All rights reserved.
part of demo_super_player_lib;
class PlayInfoStream {
......
// Copyright (c) 2022 Tencent. All rights reserved.
part of demo_super_player_lib;
/// superplayer play controller
class SuperPlayerController {
......@@ -32,6 +33,7 @@ class SuperPlayerController {
bool _isMultiBitrateStream = false; // 是否是多码流url播放
bool _changeHWAcceleration = false; // 切换硬解后接收到第一个关键帧前的标记位
bool _isFullScreen = false;
bool _isOpenHWAcceleration = true;
final BuildContext _context;
int currentDuration = 0;
......@@ -42,6 +44,7 @@ class SuperPlayerController {
double startPos = 0;
double videoWidth = 0;
double videoHeight = 0;
double currentPlayRate = 1.0;
SuperPlayerController(this._context) {
_initVodPlayer();
......@@ -89,7 +92,16 @@ class SuperPlayerController {
}
break;
case TXVodPlayEvent.PLAY_EVT_PLAY_LOADING: // PLAY_EVT_PLAY_LOADING
_updatePlayerState(SuperPlayerState.LOADING);
if (playerState == SuperPlayerState.PAUSE) {
_updatePlayerState(SuperPlayerState.PAUSE);
} else {
_updatePlayerState(SuperPlayerState.LOADING);
}
break;
case TXVodPlayEvent.PLAY_EVT_VOD_LOADING_END:
if (playerState == SuperPlayerState.LOADING) {
_updatePlayerState(SuperPlayerState.PLAYING);
}
break;
case TXVodPlayEvent.PLAY_EVT_PLAY_BEGIN: // PLAY_EVT_PLAY_BEGIN
if (_needToPause) {
......@@ -107,6 +119,7 @@ class SuperPlayerController {
seek(_seekPos);
_changeHWAcceleration = false;
}
_updatePlayerState(SuperPlayerState.PLAYING);
_observer?.onRcvFirstIframe();
break;
......@@ -114,25 +127,19 @@ class SuperPlayerController {
_updatePlayerState(SuperPlayerState.END);
break;
case TXVodPlayEvent.PLAY_EVT_PLAY_PROGRESS:
dynamic progress = event[TXVodPlayEvent.EVT_PLAY_PROGRESS_MS];
dynamic duration = event[TXVodPlayEvent.EVT_PLAY_DURATION_MS];
if(null != progress) {
currentDuration = (progress / 1000).toInt(); // 当前时间,转换后的单位 秒
dynamic progress = event[TXVodPlayEvent.EVT_PLAY_PROGRESS];
dynamic duration = event[TXVodPlayEvent.EVT_PLAY_DURATION];
if (null != progress) {
currentDuration = progress.toInt(); // 当前时间,转换后的单位 秒
}
if(null != duration) {
videoDuration = (duration / 1000).toInt(); // 总播放时长,转换后的单位 秒
if (null != duration) {
videoDuration = duration.toInt(); // 总播放时长,转换后的单位 秒
}
if (videoDuration != 0) {
_observer?.onPlayProgress(currentDuration, videoDuration);
}
break;
}
if (eventCode < 0) {
_stopPlay();
_updatePlayerState(SuperPlayerState.PAUSE);
_observer?.onError(SuperPlayerCode.VOD_PLAY_FAIL, event[TXVodPlayEvent.EVT_DESCRIPTION]);
_addSimpleEvent(SuperPlayerViewEvent.onSuperPlayerError);
}
_eventStreamController.add(event);
});
_vodPlayerController?.onPlayerNetStatusBroadcast.listen((event) {
......@@ -164,10 +171,10 @@ class SuperPlayerController {
void playWithModel(SuperPlayerModel videoModel) {
this.videoModel = videoModel;
_playAction = videoModel.playAction;
resetPlayer();
if (_playAction == SuperPlayerModel.PLAY_ACTION_AUTO_PLAY || _playAction == SuperPlayerModel.PLAY_ACTION_PRELOAD) {
_playWithModelInner(videoModel);
} else {
resetPlayer();
_observer?.onNewVideoPlay();
}
}
......@@ -267,17 +274,17 @@ class SuperPlayerController {
if (null != _vodPlayerController) {
await _vodPlayerController?.setStartTime(startPos);
if (_playAction == SuperPlayerModel.PLAY_ACTION_PRELOAD) {
await _vodPlayerController?.setIsAutoPlay(isAutoPlay: false);
await _vodPlayerController?.setAutoPlay(isAutoPlay: false);
_playAction = SuperPlayerModel.PLAY_ACTION_AUTO_PLAY;
} else if (_playAction == SuperPlayerModel.PLAY_ACTION_AUTO_PLAY ||
_playAction == SuperPlayerModel.PLAY_ACTION_MANUAL_PLAY) {
await _vodPlayerController?.setIsAutoPlay(isAutoPlay: true);
await _vodPlayerController?.setAutoPlay(isAutoPlay: true);
}
String drmType = "plain";
if (_currentProtocol != null) {
LogUtils.d(TAG, "TOKEN: ${_currentProtocol!.getToken()}");
await _vodPlayerController?.setToken(_currentProtocol!.getToken());
if(_currentProtocol!.getDRMType() != null && _currentProtocol!.getDRMType()!.isNotEmpty) {
if (_currentProtocol!.getDRMType() != null && _currentProtocol!.getDRMType()!.isNotEmpty) {
drmType = _currentProtocol!.getDRMType()!;
}
} else {
......@@ -286,14 +293,14 @@ class SuperPlayerController {
if (videoModel!.videoId != null && videoModel!.appId != 0) {
Uri uri = Uri.parse(url);
String query = uri.query;
if(query == null || query.isEmpty) {
if (query == null || query.isEmpty) {
query = "";
} else {
query = query + "&";
if (query.contains("spfileid") || query.contains("spdrmtype") || query.contains("spappid")) {
LogUtils.d(TAG, "url contains superplay key. $query");
}
query += "spfileid=${videoModel!.videoId!.fileId}""&spdrmtype=$drmType&spappid=${videoModel!.appId}";
query += "spfileid=${videoModel!.videoId!.fileId}" "&spdrmtype=$drmType&spappid=${videoModel!.appId}";
}
}
LogUtils.d(TAG, "play url:$url");
......@@ -316,11 +323,11 @@ class SuperPlayerController {
}
/// 继续播放视频
void resume() {
void resume() {
if (playerType == SuperPlayerType.VOD) {
_needToResume = true;
if (isPrepared) {
_vodPlayerController?.resume();
_vodPlayerController?.resume();
}
} else {
// todo implements live player
......@@ -413,13 +420,14 @@ class SuperPlayerController {
void resetPlayer() async {
isPrepared = false;
_needToResume = false;
_needToPause = false;
currentDuration = 0;
videoDuration = 0;
currentQuality = null;
currentQualiyList?.clear();
_currentProtocol = null;
await _vodPlayerController?.stop(isNeedClear: false);
await _vodPlayerController?.stop(isNeedClear: true);
_updatePlayerState(SuperPlayerState.INIT);
}
......@@ -435,7 +443,7 @@ class SuperPlayerController {
/// return true : 执行了退出全屏等操作,消耗了返回事件 false:未消耗事件
bool onBackPress() {
if(null != _vodPlayerController && _isFullScreen) {
if (null != _vodPlayerController && _isFullScreen) {
_observer?.onSysBackPress();
return true;
}
......@@ -454,7 +462,7 @@ class SuperPlayerController {
if (videoQuality.url.isNotEmpty) {
// url stream need manual seek
double currentTime = await _vodPlayerController!.getCurrentPlaybackTime();
await _vodPlayerController?.stop(isNeedClear: true);
await _vodPlayerController?.stop(isNeedClear: false);
LogUtils.d(TAG, "onQualitySelect quality.url:${videoQuality.url}");
await _vodPlayerController?.setStartTime(currentTime);
await _vodPlayerController?.startPlay(videoQuality.url);
......@@ -476,7 +484,7 @@ class SuperPlayerController {
bool? isPlaying = await _vodPlayerController?.isPlaying();
// resume when not playing.if isPlaying is null,not resume
if (!(isPlaying ?? true)) {
_vodPlayerController?.resume();
resume();
}
} else {
// todo implements live player
......@@ -491,6 +499,7 @@ class SuperPlayerController {
/// 开关硬解编码播放
Future<void> enableHardwareDecode(bool enable) async {
_isOpenHWAcceleration = enable;
if (playerType == SuperPlayerType.VOD) {
if (null != _vodPlayerController) {
await _vodPlayerController?.enableHardwareDecode(enable);
......@@ -512,6 +521,11 @@ class SuperPlayerController {
}
}
Future<void> setPlayRate(double rate) async {
currentPlayRate = rate;
_vodPlayerController?.setRate(rate);
}
/// 获得当前播放器状态
SuperPlayerState getPlayerState() {
return playerState;
......
// Copyright (c) 2022 Tencent. All rights reserved.
part of demo_super_player_lib;
/// superplayer's bridge between widget and controller
......
// Copyright (c) 2022 Tencent. All rights reserved.
part of demo_super_player_lib;
/// video quality utils
......
// Copyright (c) 2022 Tencent. All rights reserved.
part of demo_super_player_lib;
/// slider
......@@ -123,16 +124,7 @@ class _VideoBottomViewState extends State<VideoBottomView> {
Widget _getSlider() {
return Expanded(
child: Theme(
data: ThemeData(
sliderTheme: SliderThemeData(
trackHeight: 2,
thumbColor: Color(ColorResource.COLOR_MAIN_THEME),
thumbShape: RoundSliderThumbShape(enabledThumbRadius: 4),
overlayColor: Colors.white,
overlayShape: RoundSliderOverlayShape(overlayRadius: 10),
activeTrackColor: Color(ColorResource.COLOR_MAIN_THEME),
inactiveTrackColor: Color(ColorResource.COLOR_GRAY),
)),
data: ThemeResource.getCommonSliderTheme(),
child: Slider(
min: 0,
max: 1,
......
// Copyright (c) 2022 Tencent. All rights reserved.
part of demo_super_player_lib;
class SuperPlayerCoverView extends StatefulWidget {
final _CoverViewController _controller;
SuperPlayerModel? videoModel;
SuperPlayerCoverView(this._controller, GlobalKey<_SuperPlayerCoverViewState> key, this.videoModel) : super(key: key);
@override
State<StatefulWidget> createState() => _SuperPlayerCoverViewState();
}
class _SuperPlayerCoverViewState extends State<SuperPlayerCoverView> {
bool _isShowCover = true;
SuperPlayerModel? _videoModel;
@override
void initState() {
super.initState();
if (widget.videoModel != null) {
_isShowCover = true;
_videoModel = widget.videoModel;
} else {
_isShowCover = false;
}
}
@override
Widget build(BuildContext context) {
bool hasCover = false;
String coverUrl = "";
if (null != _videoModel) {
SuperPlayerModel model = _videoModel!;
// custom cover is preferred
if (model.customeCoverUrl.isNotEmpty) {
coverUrl = model.customeCoverUrl;
hasCover = true;
} else if (model.coverUrl.isNotEmpty) {
coverUrl = model.coverUrl;
hasCover = true;
}
}
return Visibility(
visible: _isShowCover,
child: Positioned.fill(
top: topBottomOffset,
bottom: topBottomOffset,
left: 0,
right: 0,
child: InkWell(
onDoubleTap: _onDoubleTapVideo,
onTap: _onSingleTapVideo,
child: Container(
child: hasCover ? Image.network(coverUrl,fit: BoxFit.cover,) : Container(),
)
)),
);
}
void _onDoubleTapVideo() {
widget._controller.onDoubleTapVideo();
}
void _onSingleTapVideo() {
widget._controller.onSingleTapVideo();
}
void showCover(SuperPlayerModel model) {
setState(() {
_videoModel = model;
_isShowCover = true;
});
}
void hideCover() {
setState(() {
_isShowCover = false;
});
}
}
class _CoverViewController {
Function onDoubleTapVideo;
Function onSingleTapVideo;
_CoverViewController(this.onDoubleTapVideo, this.onSingleTapVideo);
}
// Copyright (c) 2022 Tencent. All rights reserved.
part of demo_super_player_lib;
typedef BoolFunction = bool Function();
typedef DoubleFunction = double Function();
/// 超级播放器更多菜单
class SuperPlayerMoreView extends StatefulWidget {
_MoreViewController controller;
SuperPlayerMoreView(this.controller, {Key? key}) : super(key: key);
@override
State<StatefulWidget> createState() => _SuperPlayerMoreViewState();
}
class _SuperPlayerMoreViewState extends State<SuperPlayerMoreView> {
double _currentBrightness = 0.01;
double _currentVolumn = 0;
bool _isShowMoreView = false;
bool _isOpenAccelerate = true;
String _currentRate = "";
Map<String, double> playRateStr = {"1.0x": 1.0, "1.25x": 1.25, "1.5x": 1.5, "2.0x": 2.0};
@override
void initState() {
super.initState();
double playerPlayRate = widget.controller.getPlayRate();
for(String rateStr in playRateStr.keys) {
if(playerPlayRate == playRateStr[rateStr]) {
_currentRate = rateStr;
break;
}
}
// if not found in playRateStr,set 1.0
if(_currentRate.isEmpty) {
_currentRate = playRateStr.keys.first;
}
_isOpenAccelerate = widget.controller.getAccelerateIsOpen();
// regist system volume changed event
SuperPlayerPlugin.instance.onEventBroadcast.listen((event) {
int code = event["event"];
if (code == TXVodPlayEvent.EVENT_VOLUME_CHANGED) {
refreshVolume();
}
});
_initData();
}
void refreshVolume() async {
_currentVolumn = await SuperPlayerPlugin.getSystemVolume();
setState(() {});
}
void _initData() async {
double tempBrightness = await SuperPlayerPlugin.getBrightness();
if (tempBrightness == -1) {
_onChangeBrightness(1);
} else {
_currentBrightness = tempBrightness;
}
_currentVolumn = await SuperPlayerPlugin.getSystemVolume();
setState(() {});
}
@override
Widget build(BuildContext context) {
return Visibility(
visible: _isShowMoreView,
child: Positioned(
right: 0,
bottom: 0,
top: 0,
child: Container(
height: double.infinity,
width: 320,
padding: EdgeInsets.only(left: 15, right: 20, top: 15, bottom: 15),
decoration: BoxDecoration(color: Color(ColorResource.COLOR_TRANS_BLACK)),
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
_getVolumeWidget(),
_getBrightnessWidget(),
_getPlayRateWidget(),
_getSwitchHardwareWidget(),
],
),
)),
));
}
Widget _getSwitchHardwareWidget() {
return Container(
margin: EdgeInsets.only(top: 10, bottom: 10),
child: Row(
children: [
Text(
StringResource.HARDWARE_ACCE_LABEL,
textAlign: TextAlign.center,
style: ThemeResource.getCommonLabelTextStyle(),
),
Switch(
activeColor: Color(ColorResource.COLOR_MAIN_THEME),
value: _isOpenAccelerate,
onChanged: _onChangeAccelerate)
],
),
);
}
Widget _getPlayRateWidget() {
List<Widget> playRateChild = [
Text(
StringResource.MULITIPE_SPEED_PLAY_LABEL,
textAlign: TextAlign.center,
style: ThemeResource.getCommonLabelTextStyle(),
)
];
for (String rateStr in playRateStr.keys) {
playRateChild.add(Container(
padding: EdgeInsets.only(left: 5, right: 5),
child: InkWell(
onTap: () => _onChangePlayRate(rateStr),
child: Text(
rateStr,
textAlign: TextAlign.center,
style: rateStr == _currentRate ? ThemeResource.getCheckedLabelTextStyle() : ThemeResource.getCommonLabelTextStyle(),
),
),
));
}
return Container(
margin: EdgeInsets.only(top: 10, bottom: 10),
child: Row(
children: playRateChild,
),
);
}
Widget _getBrightnessWidget() {
return Container(
margin: EdgeInsets.only(top: 10, bottom: 10),
child: Row(children: [
Text(
StringResource.BRIGHTNESS_LABEL,
textAlign: TextAlign.center,
style: ThemeResource.getCommonLabelTextStyle(),
),
Image(width: 30, height: 30, image: AssetImage("images/superplayer_ic_light_min.png")),
Expanded(
child: Theme(
data: ThemeResource.getCommonSliderTheme(),
child: Slider(
min: 0,
max: 1,
value: _currentBrightness,
onChanged: _onChangeBrightness,
)),
),
Image(width: 30, height: 30, image: AssetImage("images/superplayer_ic_light_max.png")),
]),
);
}
Widget _getVolumeWidget() {
return Container(
margin: EdgeInsets.only(top: 10, bottom: 10),
child: Row(
children: [
Text(
StringResource.VOICE_LABEL,
textAlign: TextAlign.center,
style: ThemeResource.getCommonLabelTextStyle(),
),
Image(width: 30, height: 30, image: AssetImage("images/superplayer_ic_volume_min.png")),
Expanded(
child: Theme(
data: ThemeResource.getCommonSliderTheme(),
child: Slider(
min: 0,
max: 1,
value: _currentVolumn,
onChanged: _onChangeVolume,
)),
),
Image(width: 30, height: 30, image: AssetImage("images/superplayer_ic_volume_max.png")),
],
),
);
}
void _onChangePlayRate(String rateKey) {
if (_currentRate != rateKey) {
setState(() {
_currentRate = rateKey;
});
double rate = playRateStr[_currentRate]!;
widget.controller.onChangedPlayRate(rate);
}
}
void _onChangeBrightness(double value) {
if (_currentBrightness != value) {
setState(() {
_currentBrightness = value;
});
SuperPlayerPlugin.setBrightness(value);
}
}
void _onChangeVolume(double value) {
if (_currentVolumn != value) {
setState(() {
_currentVolumn = value;
});
SuperPlayerPlugin.setSystemVolume(value);
}
}
void _onChangeAccelerate(bool value) {
if (value != _isOpenAccelerate) {
setState(() {
_isOpenAccelerate = value;
});
widget.controller.siwtchAccelerate(value);
}
}
void toggleShowMoreView() {
setState(() {
_isShowMoreView = !_isShowMoreView;
});
}
void hideShowMoreView() {
if (_isShowMoreView) {
setState(() {
_isShowMoreView = false;
});
}
}
}
class _MoreViewController {
BoolFunction getAccelerateIsOpen;
DoubleFunction getPlayRate;
Function(bool value) siwtchAccelerate;
Function(double playRate) onChangedPlayRate;
_MoreViewController(this.getAccelerateIsOpen, this.getPlayRate, this.siwtchAccelerate, this.onChangedPlayRate);
}
// Copyright (c) 2022 Tencent. All rights reserved.
part of demo_super_player_lib;
class QualityListView extends StatefulWidget {
......@@ -48,8 +49,8 @@ class _QualityListViewState extends State<QualityListView> {
_qualityList![index].title,
textAlign: TextAlign.center,
style: _currentQuality == _qualityList![index]
? TextStyle(fontSize: 12, color: Color(ColorResource.COLOR_MAIN_THEME))
: TextStyle(fontSize: 12, color: Colors.white),
? ThemeResource.getCheckedTextStyle()
: ThemeResource.getCommonTextStyle(),
),
),
);
......
// Copyright (c) 2022 Tencent. All rights reserved.
part of demo_super_player_lib;
class _VideoTitleView extends StatefulWidget {
final String _title;
final _VideoTitleController _controller;
final bool initIsFullScreen;
_VideoTitleView(this._controller ,this._title, GlobalKey<_VideoTitleViewState> key):super(key: key);
_VideoTitleView(this._controller, this.initIsFullScreen, this._title, GlobalKey<_VideoTitleViewState> key)
: super(key: key);
@override
State<StatefulWidget> createState() => _VideoTitleViewState();
}
class _VideoTitleViewState extends State<_VideoTitleView> {
String _title = "";
bool _isFullScreen = false;
@override
void initState() {
super.initState();
_title = widget._title;
_isFullScreen = widget.initIsFullScreen;
}
@override
......@@ -39,26 +43,49 @@ class _VideoTitleViewState extends State<_VideoTitleView> {
Text(
_title,
style: TextStyle(fontSize: 11, color: Colors.white),
)
),
Expanded(child: SizedBox()),
Visibility(
visible: _isFullScreen,
child: InkWell(
onTap: _onTapMore,
child: Image(
width: 30,
height: 30,
image: AssetImage("images/superplayer_ic_vod_more_normal.png"),
),
))
],
),
);
}
void _onTapMore() {
widget._controller._onTapMore();
}
void _onTapBackBtn() {
widget._controller._onTapBack();
}
void updateTitle(String name) {
if(mounted) {
if (mounted) {
setState(() {
_title = name;
});
}
}
void updateFullScreen(bool showFullScreen) {
setState(() {
_isFullScreen = showFullScreen;
});
}
}
class _VideoTitleController {
Function _onTapBack;
_VideoTitleController(this._onTapBack);
}
\ No newline at end of file
Function _onTapMore;
_VideoTitleController(this._onTapBack, this._onTapMore);
}
import 'package:flutter/cupertino.dart';
// Copyright (c) 2022 Tencent. All rights reserved.
import 'package:flutter/material.dart';
import 'dart:async';
typedef void TestBitrateCheckboxFinishCallback(
int value);
......
// Copyright (c) 2022 Tencent. All rights reserved.
import 'package:flutter/material.dart';
abstract class DemoDefine{
......
// Copyright (c) 2022 Tencent. All rights reserved.
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
......
// Copyright (c) 2022 Tencent. All rights reserved.
import 'package:flutter/material.dart';
import 'demo_define.dart';
......
// Copyright (c) 2022 Tencent. All rights reserved.
import 'package:flutter/material.dart';
typedef void DemoInputDialogFinishCallback(
String url, int appId, String fileId);
String url, int appId, String fileId,String pSign);
class DemoInputDialog extends StatefulWidget {
String url = "";
......@@ -10,8 +10,10 @@ class DemoInputDialog extends StatefulWidget {
String fileId = "";
DemoInputDialogFinishCallback callback;
bool showFileEdited = true;
bool needPisgn = false;
DemoInputDialog(this.url, this.appId, this.fileId, this.callback, {bool showFileEdited = true}) : super () {this.showFileEdited = showFileEdited;}
DemoInputDialog(this.url, this.appId, this.fileId, this.callback, {bool showFileEdited = true, bool needPisgn = false})
: super () {this.showFileEdited = showFileEdited;this.needPisgn = needPisgn;}
@override
_DemoInputDialogState createState() => _DemoInputDialogState();
......@@ -21,6 +23,7 @@ class _DemoInputDialogState extends State<DemoInputDialog> {
late TextEditingController _urlController;
late TextEditingController _appIdController;
late TextEditingController _fileIdController;
late TextEditingController _pSignController;
@override
void initState() {
......@@ -30,6 +33,7 @@ class _DemoInputDialogState extends State<DemoInputDialog> {
_appIdController = TextEditingController(
text: widget.appId > 0 ? widget.appId.toString() : null);
_fileIdController = TextEditingController(text: widget.fileId);
_pSignController = TextEditingController(text: "");
}
_buildActionWidget(BuildContext context) {
......@@ -43,7 +47,8 @@ class _DemoInputDialogState extends State<DemoInputDialog> {
_appIdController.text.isNotEmpty
? int.parse(_appIdController.text)
: 0,
_fileIdController.text);
_fileIdController.text,
_pSignController.text);
}, // 关闭对话框
),
// Padding(padding: EdgeInsets.only(left: 15)),
......@@ -131,6 +136,30 @@ class _DemoInputDialogState extends State<DemoInputDialog> {
},
)),
):Container(),
widget.needPisgn?Padding(padding: EdgeInsets.only(bottom: 15)):Container(),
widget.needPisgn?Container(
color: Colors.white,
child: Theme(
data: new ThemeData(primaryColor: Colors.green),
child: TextField(
minLines: 1,
maxLines: 10,
controller: _pSignController,
cursorColor: Colors.green,
decoration: InputDecoration(
enabledBorder: OutlineInputBorder(borderRadius: BorderRadius.all(Radius.circular(10.0)), borderSide: BorderSide(color: Colors.green.withOpacity(0.4), width: 3.0)),
focusedBorder: OutlineInputBorder(borderRadius: BorderRadius.all(Radius.circular(10.0)), borderSide: BorderSide(color: Colors.green, width: 4.0)),
labelText:"请输入pSign", labelStyle: TextStyle(color: Colors.grey),
suffixIcon: IconButton(
icon: Icon(Icons.close),
onPressed: () {
_pSignController.clear();
})),
onChanged: (text) {
// _fileId = text;
},
)),
):Container(),
],
);
}
......
// Copyright (c) 2022 Tencent. All rights reserved.
import 'package:flutter/material.dart';
typedef void DemoSpeedSliderFinishCallback(
......
// Copyright (c) 2022 Tencent. All rights reserved.
import 'package:flutter/material.dart';
import 'package:super_player/super_player.dart';
......
// Copyright (c) 2022 Tencent. All rights reserved.
import 'package:flutter/material.dart';
typedef void DemoVolumeSliderFinishCallback(
......
// Copyright (c) 2022 Tencent. All rights reserved.
import 'dart:math';
import 'package:flutter/material.dart';
......
# Generated by pub
# See https://dart.dev/tools/pub/glossary#lockfile
packages:
async:
dependency: transitive
description:
name: async
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.8.2"
auto_orientation:
dependency: "direct main"
description:
name: auto_orientation
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.2.1"
boolean_selector:
dependency: transitive
description:
name: boolean_selector
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.1.0"
characters:
dependency: transitive
description:
name: characters
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.2.0"
charcode:
dependency: transitive
description:
name: charcode
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.3.1"
clock:
dependency: transitive
description:
name: clock
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.1.0"
collection:
dependency: transitive
description:
name: collection
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.15.0"
cupertino_icons:
dependency: "direct main"
description:
name: cupertino_icons
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.0.4"
fake_async:
dependency: transitive
description:
name: fake_async
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.2.0"
flutter:
dependency: "direct main"
description: flutter
source: sdk
version: "0.0.0"
flutter_easyloading:
dependency: "direct main"
description:
name: flutter_easyloading
url: "https://pub.flutter-io.cn"
source: hosted
version: "3.0.5"
flutter_spinkit:
dependency: transitive
description:
name: flutter_spinkit
url: "https://pub.flutter-io.cn"
source: hosted
version: "5.1.0"
flutter_test:
dependency: "direct dev"
description: flutter
source: sdk
version: "0.0.0"
matcher:
dependency: transitive
description:
name: matcher
url: "https://pub.flutter-io.cn"
source: hosted
version: "0.12.11"
meta:
dependency: transitive
description:
name: meta
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.7.0"
path:
dependency: transitive
description:
name: path
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.8.0"
sky_engine:
dependency: transitive
description: flutter
source: sdk
version: "0.0.99"
source_span:
dependency: transitive
description:
name: source_span
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.8.1"
stack_trace:
dependency: transitive
description:
name: stack_trace
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.10.0"
stream_channel:
dependency: transitive
description:
name: stream_channel
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.1.0"
string_scanner:
dependency: transitive
description:
name: string_scanner
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.1.0"
super_player:
dependency: "direct main"
description:
path: ".."
relative: true
source: path
version: "1.0.2"
term_glyph:
dependency: transitive
description:
name: term_glyph
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.2.0"
test_api:
dependency: transitive
description:
name: test_api
url: "https://pub.flutter-io.cn"
source: hosted
version: "0.4.3"
typed_data:
dependency: transitive
description:
name: typed_data
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.3.0"
vector_math:
dependency: transitive
description:
name: vector_math
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.1.1"
sdks:
dart: ">=2.14.0 <3.0.0"
flutter: ">=2.0.0"
......@@ -44,6 +44,11 @@ flutter:
# the material Icons class.
uses-material-design: true
assets:
- images/superplayer_ic_vod_more_normal.png
- images/superplayer_ic_light_max.png
- images/superplayer_ic_light_min.png
- images/superplayer_ic_volume_max.png
- images/superplayer_ic_volume_min.png
- images/superplayer_ic_vod_pause_normal.png
- images/superplayer_ic_vod_play_normal.png
- images/superplayer_bottom_shadow.png
......
//
// FTXBasePlayer.h
// super_player
//
// Created by Zhirui Ou on 2021/3/23.
//
// Copyright (c) 2022 Tencent. All rights reserved.
#import <Foundation/Foundation.h>
......
//
// FTXBasePlayer.m
// super_player
//
// Created by Zhirui Ou on 2021/3/23.
//
// Copyright (c) 2022 Tencent. All rights reserved.
#import "FTXBasePlayer.h"
#import <stdatomic.h>
......
// Copyright (c) 2022 Tencent. All rights reserved.
#ifndef FTXEvent_h
#define FTXEvent_h
// 音频变化事件code
#define EVENT_VOLUME_CHANGED 0x01
#define EVENT_AUDIO_FOCUS_PAUSE 0x02
#define EVENT_AUDIO_FOCUS_PLAY 0x03
#endif /* FTXEvent_h */
//
// FTXLivePlayer.h
// super_player
//
// Created by Zhirui Ou on 2021/3/15.
//
// Copyright (c) 2022 Tencent. All rights reserved.
#import <Foundation/Foundation.h>
#import "FTXBasePlayer.h"
......
//
// FTXLivePlayer.m
// super_player
//
// Created by Zhirui Ou on 2021/3/15.
//
// Copyright (c) 2022 Tencent. All rights reserved.
#import "FTXLivePlayer.h"
#import "FTXPlayerEventSinkQueue.h"
......@@ -112,7 +107,6 @@ static const int uninitialized = -1;
[config setPlayerPixelFormatType:kCVPixelFormatType_32BGRA];
[_txLivePlayer setConfig:config];
[_txLivePlayer setVideoProcessDelegate:self];
_txLivePlayer.enableHWAcceleration = YES;
}
}
}
......
//
// FTXPlayerEventSink.h
// super_player
//
// Created by Zhirui Ou on 2021/3/16.
//
// Copyright (c) 2022 Tencent. All rights reserved.
#import <Foundation/Foundation.h>
#import <Flutter/Flutter.h>
......
//
// FTXPlayerEventSink.m
// super_player
//
// Created by Zhirui Ou on 2021/3/16.
//
// Copyright (c) 2022 Tencent. All rights reserved./
#import "FTXPlayerEventSinkQueue.h"
......
// Copyright (c) 2022 Tencent. All rights reserved.
#import <Foundation/Foundation.h>
#import <TXLiteAVSDK_Player/TXLiteAVSDK.h>
......
// Copyright (c) 2022 Tencent. All rights reserved.
#import <Foundation/Foundation.h>
#import "FTXTransformation.h"
......
//
// FTXVodPlayer.h
// super_player
//
// Created by Zhirui Ou on 2021/3/15.
//
// Copyright (c) 2022 Tencent. All rights reserved.
#import <Foundation/Foundation.h>
#import "FTXBasePlayer.h"
......
//
// FTXVodPlayer.m
// super_player
//
// Created by Zhirui Ou on 2021/3/15.
//
// Copyright (c) 2022 Tencent. All rights reserved.
#import "FTXVodPlayer.h"
#import "FTXPlayerEventSinkQueue.h"
......@@ -14,6 +9,7 @@
#import <Flutter/Flutter.h>
static const int uninitialized = -1;
static const int CODE_ON_RECEIVE_FIRST_FRAME = 2003;
@interface FTXVodPlayer ()<FlutterStreamHandler, FlutterTexture, TXVodPlayListener, TXVideoCustomProcessDelegate>
......@@ -38,6 +34,9 @@ static const int uninitialized = -1;
id<FlutterTextureRegistry> _textureRegistry;
}
BOOL volatile isStop = false;
- (instancetype)initWithRegistrar:(id<FlutterPluginRegistrar>)registrar
{
if (self = [self init]) {
......@@ -116,7 +115,6 @@ static const int uninitialized = -1;
if (_txVodPlayer != nil) {
[_txVodPlayer setVideoProcessDelegate:self];
_txVodPlayer.enableHWAcceleration = YES;
}
}
}
......@@ -182,6 +180,7 @@ static const int uninitialized = -1;
- (BOOL)stopPlay
{
if (_txVodPlayer != nil) {
isStop = true;
return [_txVodPlayer stopPlay];
}
return NO;
......@@ -240,14 +239,13 @@ static const int uninitialized = -1;
- (NSArray *)supportedBitrates
{
if (_txVodPlayer != nil) {
NSArray *itemList = _txVodPlayer.supportedBitrates;
NSArray *itemList = [_txVodPlayer supportedBitrates];
NSMutableArray *bitrates = @[].mutableCopy;
for (TXBitrateItem *item in itemList) {
[bitrates addObject:@{@"index": @(item.index), @"width": @(item.width), @"height": @(item.height), @"bitrate": @(item.bitrate)}];
}
return bitrates;
}
return @[];
}
......@@ -295,7 +293,7 @@ static const int uninitialized = -1;
BOOL onlyAudio = [args[@"onlyAudio"] boolValue];
NSNumber* textureId = [self createPlayer:onlyAudio];
result(textureId);
}else if([@"setIsAutoPlay" isEqualToString:call.method]) {
}else if([@"setAutoPlay" isEqualToString:call.method]) {
BOOL isAutoPlay = [args[@"isAutoPlay"] boolValue];
[self setIsAutoPlay:isAutoPlay];
result(nil);
......@@ -343,8 +341,8 @@ static const int uninitialized = -1;
[self setBitrateIndex:index];
result(nil);
}else if([@"setStartTime" isEqualToString:call.method]) {
// float startTime = [args[@"startTime"] floatValue];
// [self setStartTime:startTime];
float startTime = [args[@"startTime"] floatValue];
[self setStartTime:startTime];
result(nil);
}else if([@"setAudioPlayoutVolume" isEqualToString:call.method]) {
int volume = [args[@"volume"] intValue];
......@@ -469,13 +467,21 @@ static const int uninitialized = -1;
(void **)&_latestPixelBuffer)) {
pixelBuffer = _latestPixelBuffer;
}
return pixelBuffer;
if(isStop && nil != pixelBuffer) {
isStop = false;
[_eventSink success:[FTXVodPlayer getParamsWithEvent:CODE_ON_RECEIVE_FIRST_FRAME withParams:@{}]];
}
return isStop ? nil : pixelBuffer;
}
#pragma mark - TXVodPlayListener
- (void)onPlayEvent:(TXVodPlayer *)player event:(int)EvtID withParam:(NSDictionary*)param
{
// 交给flutter共享纹理处理首帧事件返回时机
if(EvtID == CODE_ON_RECEIVE_FIRST_FRAME) {
return;
}
[_eventSink success:[FTXVodPlayer getParamsWithEvent:EvtID withParams:param]];
}
......@@ -509,7 +515,7 @@ static const int uninitialized = -1;
_lastBuffer = CVPixelBufferRetain(pixelBuffer);
CFRetain(pixelBuffer);
}
CVPixelBufferRef newBuffer = pixelBuffer;
CVPixelBufferRef old = _latestPixelBuffer;
......
// Copyright (c) 2022 Tencent. All rights reserved.
#import <Flutter/Flutter.h>
@interface SuperPlayerPlugin : NSObject<FlutterPlugin>
......
// Copyright (c) 2022 Tencent. All rights reserved.
part of SuperPlayer;
class SuperPlayerPlugin {
static SuperPlayerPlugin? _instance;
static SuperPlayerPlugin get instance => _sharedInstance();
/// SuperPlayerPlugin单例
static SuperPlayerPlugin _sharedInstance() {
if (_instance == null) {
_instance = SuperPlayerPlugin._internal();
}
return _instance!;
}
final StreamController<Map<dynamic, dynamic>> _eventStreamController = StreamController.broadcast();
/// 原生交互,通用事件监听
Stream<Map<dynamic, dynamic>> get onEventBroadcast => _eventStreamController.stream;
SuperPlayerPlugin._internal() {
EventChannel eventChannel = EventChannel("cloud.tencent.com/playerPlugin/event");
eventChannel.receiveBroadcastStream("event").listen(_eventHandler, onError: _errorHandler);
}
_eventHandler(event) {
if (null == event) {
return;
}
_eventStreamController.add(event);
}
_errorHandler(error) {}
static const MethodChannel _channel = const MethodChannel('flutter_super_player');
static Future<String?> get platformVersion async {
......@@ -8,18 +40,22 @@ class SuperPlayerPlugin {
return version;
}
/// 创建直播播放器
static Future<int?> createLivePlayer() async {
return await _channel.invokeMethod('createLivePlayer');
}
/// 创建点播播放器
static Future<int?> createVodPlayer() async {
return await _channel.invokeMethod('createVodPlayer');
}
/// 开关log输出
static Future<int?> setConsoleEnabled(bool enabled) async {
return await _channel.invokeMethod('setConsoleEnabled', {"enabled": enabled});
}
/// 释放播放器资源
static Future<int?> releasePlayer(int? playerId) async {
return await _channel.invokeMethod('releasePlayer', {"playerId": playerId});
}
......@@ -38,4 +74,44 @@ class SuperPlayerPlugin {
static Future<void> setGlobalLicense(String licenceUrl, String licenceKey) async {
return await _channel.invokeMethod("setGlobalLicense", {"licenceUrl": licenceUrl, "licenceKey": licenceKey});
}
/// 设置log输出级别 [TXLogLevel]
static Future<void> setLogLevel(int logLevel) async {
return await _channel.invokeMethod("setLogLevel", {"logLevel": logLevel});
}
/// 修改当前界面亮度
static Future<void> setBrightness(double brightness) async {
return await _channel.invokeMethod("setBrightness", {"brightness": brightness});
}
/// 恢复当前界面亮度
static Future<void> restorePageBrightness() async {
return await _channel.invokeMethod("setBrightness", {"brightness": -1});
}
/// 获得当前界面亮度 0.0 ~ 1.0
static Future<double> getBrightness() async {
return await _channel.invokeMethod("getBrightness");
}
/// 设置当前系统音量,0.0 ~ 1.0
static Future<void> setSystemVolume(double volume) async {
return await _channel.invokeMethod("setSystemVolume", {"volume": volume});
}
/// 获得当前系统音量,范围:0.0 ~ 1.0
static Future<double> getSystemVolume() async {
return await _channel.invokeMethod("getSystemVolume");
}
/// 释放音频焦点,只用于安卓端
static Future<double> abandonAudioFocus() async {
return await _channel.invokeMethod("abandonAudioFocus");
}
/// 请求获得音频焦点,只用于安卓端
static Future<double> requestAudioFocus() async {
return await _channel.invokeMethod("requestAudioFocus");
}
}
// Copyright (c) 2022 Tencent. All rights reserved.
part of SuperPlayer;
/// log print tools
......
// Copyright (c) 2022 Tencent. All rights reserved.
part of SuperPlayer;
class TXLivePlayerController extends ChangeNotifier implements ValueListenable<TXPlayerValue?>, TXPlayerController {
......
// Copyright (c) 2022 Tencent. All rights reserved.
part of SuperPlayer;
abstract class TXPlayerController {
......
// Copyright (c) 2022 Tencent. All rights reserved.
part of SuperPlayer;
class TXPlayerValue {
......@@ -80,6 +81,12 @@ abstract class TXVodPlayEvent {
static const EVT_PLAY_DURATION = "EVT_PLAY_DURATION"; // 播放总长
static const EVT_PLAYABLE_DURATION_MS = "EVT_PLAYABLE_DURATION_MS"; // 点播可播放时长(毫秒)
static const EVT_PLAYABLE_RATE = "EVT_PLAYABLE_RATE"; //播放速率
/// superplayer plugin volume evnet
static const EVENT_VOLUME_CHANGED = 0x01; // 音量变化
static const EVENT_AUDIO_FOCUS_PAUSE = 0x02; // 失去音量输出播放焦点 only for android
static const EVENT_AUDIO_FOCUS_PLAY = 0x03; // 获得音量输出焦点 only for android
}
abstract class TXVodNetEvent {
......@@ -132,6 +139,16 @@ enum TXPlayerEvent {
progress // 进度
}
class TXLogLevel {
static const int LOG_LEVEL_VERBOSE = 0; // 输出所有级别的log
static const int LOG_LEVEL_DEBUG = 1; // 输出 DEBUG,INFO,WARNING,ERROR 和 FATAL 级别的log
static const int LOG_LEVEL_INFO = 2; // 输出 INFO,WARNNING,ERROR 和 FATAL 级别的log
static const int LOG_LEVEL_WARN = 3; // 输出WARNNING,ERROR 和 FATAL 级别的log
static const int LOG_LEVEL_ERROR = 4; // 输出ERROR 和 FATAL 级别的log
static const int LOG_LEVEL_FATAL = 5; // 只输出FATAL 级别的log
static const int LOG_LEVEL_NULL = 6; // 不输出任何sdk log
}
class TXPlayerAuthParams {
int appId = 0;
String fileId = "";
......
// Copyright (c) 2022 Tencent. All rights reserved.
part of SuperPlayer;
class TXPlayerVideo extends StatefulWidget {
final TXPlayerController controller;
TXPlayerVideo({required this.controller}):assert(controller != null);
TXPlayerVideo({required this.controller, Key? key}) : super(key: key) {
assert(controller != null);
}
@override
_TXPlayerVideoState createState() => _TXPlayerVideoState();
TXPlayerVideoState createState() => TXPlayerVideoState();
}
class _TXPlayerVideoState extends State<TXPlayerVideo> {
class TXPlayerVideoState extends State<TXPlayerVideo> {
static const TAG = "TXPlayerVideo";
int _textureId = -1;
......@@ -18,49 +20,48 @@ class _TXPlayerVideoState extends State<TXPlayerVideo> {
void initState() {
super.initState();
widget.controller.textureId.then((val) {
setState(() {
LogUtils.d(TAG, "_textureId = $val");
_textureId = val;
});
widget.controller.textureId.then((newTextureId) {
if (_textureId != newTextureId) {
setState(() {
LogUtils.d(TAG, "_textureId = $newTextureId");
_textureId = newTextureId;
});
}
});
}
@override
Widget build(BuildContext context) {
if ((defaultTargetPlatform == TargetPlatform.android) && (widget.controller.resizeVideoHeight! > 0 && widget.controller.resizeVideoWidth! > 0)) {
if ((defaultTargetPlatform == TargetPlatform.android) &&
(widget.controller.resizeVideoHeight! > 0 && widget.controller.resizeVideoWidth! > 0)) {
return _textureId == -1
? Container()
: LayoutBuilder(builder: (context, constrains) {
var viewWidth = constrains.maxWidth;
var viewHeight = constrains.maxHeight;
var videoWidth = widget.controller.resizeVideoWidth!;
var videoHeight = widget.controller.resizeVideoHeight!;
var viewWidth = constrains.maxWidth;
var viewHeight = constrains.maxHeight;
var videoWidth = widget.controller.resizeVideoWidth!;
var videoHeight = widget.controller.resizeVideoHeight!;
double left = widget.controller.videoLeft! * viewWidth/videoWidth;
double top = widget.controller.videoTop! * viewHeight/videoHeight;
double right = widget.controller.videoRight! * viewWidth/videoWidth;
double bottom = widget.controller.videoBottom! * viewHeight/videoHeight;
return Stack(
children: [
Positioned(
top: top,
left: left,
right: right,
bottom: bottom,
child:Texture(
textureId: _textureId,
)
)
],
);
});
}else {
double left = widget.controller.videoLeft! * viewWidth / videoWidth;
double top = widget.controller.videoTop! * viewHeight / videoHeight;
double right = widget.controller.videoRight! * viewWidth / videoWidth;
double bottom = widget.controller.videoBottom! * viewHeight / videoHeight;
return Stack(
children: [
Positioned(
top: top,
left: left,
right: right,
bottom: bottom,
child: Texture(textureId: _textureId)
)
],
);
});
} else {
return _textureId == -1
? Container()
: Texture(
textureId: _textureId,
);
: Texture(textureId: _textureId);
}
}
}
// Copyright (c) 2022 Tencent. All rights reserved.
part of SuperPlayer;
/// TXVodPlayer config
......
// Copyright (c) 2022 Tencent. All rights reserved.
part of SuperPlayer;
class TXVodPlayerController extends ChangeNotifier implements ValueListenable<TXPlayerValue?>, TXPlayerController {
......@@ -145,7 +146,7 @@ class TXVodPlayerController extends ChangeNotifier implements ValueListenable<TX
return result == 0;
}
/// 通过field播放视频
/// 通过fileld播放视频
/// @params : TXPlayerAuthParams
/// return 是否播放成功
Future<bool> startPlayWithParams(TXPlayerAuthParams params) async {
......@@ -176,10 +177,10 @@ class TXVodPlayerController extends ChangeNotifier implements ValueListenable<TX
}
/// 设置是否自动播放
Future<void> setIsAutoPlay({bool? isAutoPlay}) async {
Future<void> setAutoPlay({bool? isAutoPlay}) async {
if (_isNeedDisposed) return;
await _initPlayer.future;
await _channel.invokeMethod("setIsAutoPlay", {"isAutoPlay": isAutoPlay ?? false});
await _channel.invokeMethod("setAutoPlay", {"isAutoPlay": isAutoPlay ?? false});
}
/// 停止播放
......@@ -394,14 +395,6 @@ class TXVodPlayerController extends ChangeNotifier implements ValueListenable<TX
notifyListeners();
}
Future<void> _switchScreenIOS(bool toFullScreen) async {
if (toFullScreen) {
await _channel.invokeMethod("orientationToLandscape");
} else {
await _channel.invokeMethod("orientationToPortrait");
}
}
double? resizeVideoWidth = 0;
double? resizeVideoHeight = 0;
double? videoLeft = 0;
......
// Copyright (c) 2022 Tencent. All rights reserved.
library SuperPlayer;
import 'dart:async';
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论