提交 80f476c9 authored 作者: dokieyang's avatar dokieyang

1、fix bug: play with fileId & psign

2、add more features to SuperPlayer panel 3、update sdk version to latest(10.2)
上级 391bd191
.idea/
.dart_tool/
.packages
\ No newline at end of file
.packages
pubspec.lock
\ No newline at end of file
差异被折叠。
## 腾讯云播放器SDK Flutter插件
简体中文| [English](./README-EN.md)
## 工程目录结构说明
本目录包含腾讯云播放器SDK Flutter 插件 和 Demo 源代码,主要演示接口如何调用以及最基本的功能。
......@@ -116,7 +118,7 @@ pod 'TXLiteAVSDK_Professional' //Professional版
### 集成过程中常见问题
执行`flutter doctor`命令检查运行环境,知道出现”No issues found!“。
执行`flutter doctor`命令检查运行环境,直到出现”No issues found!“。
执行`flutter pub get`确保所有依赖的组件都已更新成功。
......@@ -175,10 +177,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 +310,13 @@ class _DemoSuperplayerState extends State<DemoSuperplayer> {
model.title = "腾讯云音视频";
_controller.playWithModel(model);
}
@override
void dispose() {
// must invoke when page exit.
_controller.releasePlayer();
super.dispose();
}
}
```
......
......@@ -24,32 +24,11 @@ apply plugin: 'com.android.library'
android {
//获取local.properties配置文件
def localProperties = new Properties()
def localPropertiesFile = rootProject.file('local.properties')
if (localPropertiesFile.exists()) {
localPropertiesFile.withReader('UTF-8') { reader ->
localProperties.load(reader)
}
}
//获取flutter的sdk路径
def flutterRoot = localProperties.getProperty('flutter.sdk')
if (flutterRoot == null) {
throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.")
}
compileSdkVersion rootProject.ext.compileSdkVersion
defaultConfig {
minSdkVersion rootProject.ext.minSdkVersion
}
dependencies {
compileOnly 'androidx.annotation:annotation:1.2.0'
//此处仅处理因插件项目内报红显示的处理方式
// compileOnly files("$flutterRoot/bin/cache/artifacts/engine/android-arm/flutter.jar")
}
}
......
......@@ -8,11 +8,6 @@ rootProject.ext {
在此处可以更换需要的SDK版本,替换为专业版为 com.tencent.liteav:LiteAVSDK_Professional:latest.release
其中 latest.release 可指定为自己需要的版本号,例如 "com.tencent.liteav:LiteAVSDK_Player:9.5.29035" ,写成 latest.release 则默认使用最新版本
*/
// liteavSdk="com.tencent.liteav:LiteAVSDK_Player:latest.release"
liteavSdk="com.tencent.liteav:LiteAVSDK_Professional:latest.release"
imSdk = 'com.tencent.imsdk:imsdk:4.9.1'
versionCode = 1
versionName = "v1.0"
ndkAbi = 'armeabi'//,'armeabi-v7a', 'arm64-v8a'
aekit_version = '1.0.10-cloud'
// liteavSdk="com.tencent.liteav:LiteAVSDK_Player:latest.release"
liteavSdk="com.tencent.liteav:LiteAVSDK_Professional:latest.release"
}
\ No newline at end of file
// 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;
......@@ -12,6 +13,7 @@ import com.tencent.rtmp.ITXVodPlayListener;
import com.tencent.rtmp.TXBitrateItem;
import com.tencent.rtmp.TXLiveConstants;
import com.tencent.rtmp.TXLivePlayer;
import com.tencent.rtmp.TXPlayInfoParams;
import com.tencent.rtmp.TXPlayerAuthBuilder;
import com.tencent.rtmp.TXVodPlayConfig;
import com.tencent.rtmp.TXVodPlayer;
......@@ -50,6 +52,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 +92,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 +119,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 +137,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(i, bundle));
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();
}
mSurface = new Surface(mSurfaceTextureEntry.surfaceTexture());
mVodPlayer.setSurface(mSurface);
}
@Override
public void onNetStatus(TXVodPlayer txVodPlayer, Bundle bundle) {
mNetStatusSink.success(getParams(0, bundle));
......@@ -151,7 +175,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);
......@@ -160,8 +184,8 @@ public class FTXVodPlayer extends FTXBasePlayer implements MethodChannel.MethodC
int r = startPlay(url);
result.success(r);
} else if (call.method.equals("startPlayWithParams")) {
int r = startPlayWithParams(call);
result.success(r);
startPlayWithParams(call);
result.success(null);
} else if (call.method.equals("stop")) {
Boolean isNeedClear = call.argument("isNeedClear");
int r = stopPlay(isNeedClear);
......@@ -285,6 +309,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 +318,6 @@ public class FTXVodPlayer extends FTXBasePlayer implements MethodChannel.MethodC
if (mVodPlayer != null) {
mVodPlayer.setSurface(mSurface);
mVodPlayer.enableHardwareDecode(true);
}
}
}
......@@ -305,43 +329,21 @@ public class FTXVodPlayer extends FTXBasePlayer implements MethodChannel.MethodC
return Uninitialized;
}
int startPlayWithParams(MethodCall call) {
void startPlayWithParams(MethodCall call) {
if (mVodPlayer != null) {
TXPlayerAuthBuilder builder = new TXPlayerAuthBuilder();
int appId = call.argument("appId");
builder.setAppId(appId);
String fileId = call.argument("fileId");
builder.setFileId(fileId);
String timeout = call.argument("timeout");
if (!timeout.isEmpty()) {
builder.setTimeout(timeout);
}
int exper = call.argument("exper");
builder.setExper(exper);
String us = call.argument("us");
if (!us.isEmpty()) {
builder.setUs(us);
}
String sign = call.argument("sign");
if (!sign.isEmpty()) {
builder.setSign(sign);
}
boolean https = call.argument("https");
builder.setHttps(https);
return mVodPlayer.startPlay(builder);
String psign = call.argument("psign");
TXPlayInfoParams playInfoParams = new TXPlayInfoParams(appId, fileId, psign);
mVodPlayer.startPlay(playInfoParams);
}
return Uninitialized;
}
int stopPlay(boolean isNeedClearLastImg) {
if (mVodPlayer != null) {
return mVodPlayer.stopPlay(isNeedClearLastImg);
}
mHardwareDecodeFail = false;
return Uninitialized;
}
......@@ -520,6 +522,7 @@ public class FTXVodPlayer extends FTXBasePlayer implements MethodChannel.MethodC
boolean enableHardwareDecode(boolean enable) {
if (mVodPlayer != null) {
mEnableHardwareDecode = enable;
return mVodPlayer.enableHardwareDecode(enable);
}
return false;
......
差异被折叠。
简体中文| [English](./点播播放器-EN.md)
## SDK 下载
腾讯云视立方 Flutter 超级播放器项目的地址是 [SuperPlayer Flutter](https://github.com/LiteAVSDK/Player_Flutter/tree/main/Flutter)
......@@ -734,10 +736,10 @@ _controller.onPlayerState.listen((val) { });
这就是视频播放中无缝切换的背后技术支撑,您可以使用 TXVodPlayerController 中的 isAutoPlay 开关来实现这个功能,具体做法如下:
![](https://mc.qcloudimg.com/static/img/7331417ebbdfe6306fe96f4b76c8d0ad/image.jpg)
![](https://qcloudimg.tencent-cloud.cn/raw/f43ea6105d9f80c4162241ca0605b8f0.png)
```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);
......
差异被折叠。
简体中文| [English](./直播播放器-EN.md)
## 基础知识
本文主要介绍视频云 SDK 的直播播放功能,在此之前,先了解如下一些基本知识会大有裨益:
- **直播和点播**
......
差异被折叠。
简体中文| [English](./超级播放器-EN.md)
## SDK 下载
腾讯云视立方 Flutter 超级播放器项目的地址是 [SuperPlayer Flutter](https://github.com/LiteAVSDK/Player_Flutter/tree/main/Flutter)
......
......@@ -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
}
......
......@@ -149,6 +149,7 @@
97C146EC1CF9000F007C117D /* Resources */,
9705A1C41CF9048500538489 /* Embed Frameworks */,
3B06AD1E1E4923F5004D2608 /* Thin Binary */,
54EBAF69D81CC6E332D3F9A7 /* [CP] Embed Pods Frameworks */,
);
buildRules = (
);
......@@ -242,6 +243,23 @@
shellPath = /bin/sh;
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin";
};
54EBAF69D81CC6E332D3F9A7 /* [CP] Embed Pods Frameworks */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist",
);
name = "[CP] Embed Pods Frameworks";
outputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n";
showEnvVarsInLog = 0;
};
9740EEB61CF901F6004384FC /* Run Script */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
......
// 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,7 @@ class _DemoSuperplayerState extends State<DemoSuperplayer> {
model.appId = 1500005830;
model.videoId = new SuperPlayerVideoId();
model.videoId!.fileId = "8602268011437356984";
model.title = "云点播";
model.playAction = playAction;
models.add(model);
model = SuperPlayerModel();
model.appId = 1252463788;
model.videoId = new SuperPlayerVideoId();
model.videoId!.fileId = "5285890781763144364";
model.title = "云点播(fileId播放)";
model.playAction = playAction;
models.add(model);
......@@ -215,6 +206,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,29 @@ class PlayInfoProtocol {
String? getDRMType() {
return null == _playInfoParser ? null : _playInfoParser?.drmType;
}
static String? getV4ErrorCodeDescription(int errorCode) {
return GETPLAYINFOV4_ERROR_CODE_MAP[errorCode];
}
// 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.getV4ErrorCodeDescription(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);
}
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论