提交 08ad5d2f authored 作者: dokieyang's avatar dokieyang

Update version:1.0.1

1、SuperPlayer用Flutter代码重构 2、新增播放器License设置功能 3、优化点播和直播播放器SDK接口
上级 412c00dd
/.idea/ .idea/
\ No newline at end of file .dart_tool/
.packages
\ No newline at end of file
差异被折叠。
group 'com.example.super_player' group 'com.tencent.vod.flutter'
version '1.0' apply from:'config.gradle'
version '1.0.1'
buildscript { buildscript {
repositories { repositories {
...@@ -21,21 +22,46 @@ rootProject.allprojects { ...@@ -21,21 +22,46 @@ rootProject.allprojects {
apply plugin: 'com.android.library' apply plugin: 'com.android.library'
android { 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 compileSdkVersion rootProject.ext.compileSdkVersion
defaultConfig { defaultConfig {
minSdkVersion rootProject.ext.minSdkVersion minSdkVersion rootProject.ext.minSdkVersion
} }
// lines skipped
dependencies { dependencies {
provided rootProject.findProject(":superplayerkit") compileOnly 'androidx.annotation:annotation:1.2.0'
//此处仅处理因插件项目内报红显示的处理方式
compileOnly files("$flutterRoot/bin/cache/artifacts/engine/android-arm/flutter.jar")
} }
}
/**
* 判断客户是否有自定义liteavSdk版本,如果未定义则默认使用config.gradle配置的最新版本。
* 这里判断,不为null、不为空字符串
*/
def liteavSdk = rootProject.ext.liteavSdk
if(rootProject.ext.liteavSdkCustom) {
liteavSdk = rootProject.ext.liteavSdkCustom
} }
dependencies { dependencies {
compile fileTree(dir: 'libs', include: ['*.jar']) compile fileTree(dir: 'libs', include: ['*.jar'])
implementation project(path: ':superplayerkit') implementation liteavSdk
} }
\ No newline at end of file
rootProject.ext {
compileSdkVersion = 28
buildToolsVersion = "28.0.3"
supportSdkVersion = "26.0.1"
minSdkVersion = 19
targetSdkVersion = 28
/*
在此处可以更换需要的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'
}
\ No newline at end of file
rootProject.name = 'super_player' rootProject.name = 'flutter_super_player'
<manifest xmlns:android="http://schemas.android.com/apk/res/android" <manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.super_player"> package="com.tencent.vod.flutterr">
</manifest> </manifest>
package com.example.super_player;
import android.app.Activity;
import android.content.Context;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;
import io.flutter.embedding.engine.plugins.FlutterPlugin;
import io.flutter.plugin.common.BinaryMessenger;
import io.flutter.plugin.common.StandardMessageCodec;
import io.flutter.plugin.common.MessageCodec;
import io.flutter.plugin.platform.PlatformView;
import io.flutter.plugin.platform.PlatformViewFactory;
public class SuperPlatformViewFactory extends PlatformViewFactory {
/**
* @param createArgsCodec the codec used to decode the args parameter of {@link #create}.
*/
private FlutterPlugin.FlutterPluginBinding mFlutterPluginBinding;
private Activity mActivity;
public SuperPlatformViewFactory(FlutterPlugin.FlutterPluginBinding flutterPluginBinding, Activity activity) {
super(StandardMessageCodec.INSTANCE);
mFlutterPluginBinding = flutterPluginBinding;
mActivity = activity;
}
@Override
public PlatformView create(Context context, int viewId, Object args) {
if (mActivity == null) {
return null;
}
if (args instanceof Map) {
SuperPlatformPlayerView playerView = new SuperPlatformPlayerView(mActivity, ((Map) args), viewId, mFlutterPluginBinding);
return playerView;
}
return null;
}
}
package com.example.super_player;
import android.app.Activity;
import android.util.SparseArray;
import androidx.annotation.NonNull;
import com.tencent.rtmp.TXLiveBase;
import io.flutter.embedding.engine.plugins.FlutterPlugin;
import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding;
import io.flutter.plugin.common.MethodCall;
import io.flutter.plugin.common.MethodChannel;
import io.flutter.plugin.common.MethodChannel.MethodCallHandler;
import io.flutter.plugin.common.MethodChannel.Result;
import io.flutter.plugin.common.PluginRegistry.Registrar;
import io.flutter.plugin.platform.PlatformViewRegistry;
import io.flutter.embedding.engine.plugins.activity.ActivityAware;
/** SuperPlayerPlugin */
public class SuperPlayerPlugin implements FlutterPlugin, MethodCallHandler, ActivityAware {
/// The MethodChannel that will the communication between Flutter and native Android
///
/// This local reference serves to register the plugin with the Flutter Engine and unregister it
/// when the Flutter Engine is detached from the Activity
private MethodChannel channel;
private FlutterPluginBinding mFlutterPluginBinding;
private ActivityPluginBinding mActivityPluginBinding;
private SparseArray<FTXBasePlayer> mPlayers;
@Override
public void onAttachedToEngine(@NonNull FlutterPluginBinding flutterPluginBinding) {
mFlutterPluginBinding = flutterPluginBinding;
channel = new MethodChannel(flutterPluginBinding.getBinaryMessenger(), "super_player");
channel.setMethodCallHandler(this);
mPlayers = new SparseArray();
}
@Override
public void onMethodCall(@NonNull MethodCall call, @NonNull Result result) {
if (call.method.equals("getPlatformVersion")) {
result.success("Android " + android.os.Build.VERSION.RELEASE);
} else if(call.method.equals("createVodPlayer")) {
FTXVodPlayer player = new FTXVodPlayer(mFlutterPluginBinding);
int playerId = player.getPlayerId();
mPlayers.append(playerId, player);
result.success(playerId);
} else if(call.method.equals("createLivePlayer")) {
FTXLivePlayer player = new FTXLivePlayer(mFlutterPluginBinding,mActivityPluginBinding.getActivity());
int playerId = player.getPlayerId();
mPlayers.append(playerId, player);
result.success(playerId);
} else if(call.method.equals("releasePlayer")) {
Integer playerId = call.argument("playerId");
FTXBasePlayer player = mPlayers.get(playerId);
if (player!=null){
player.destory();
mPlayers.remove(playerId);
}
result.success(null);
} else if(call.method.equals("setConsoleEnabled")) {
boolean bnabled = call.argument("enabled");
TXLiveBase.setConsoleEnabled(bnabled);
result.success(null);
} else {
result.notImplemented();
}
}
@Override
public void onDetachedFromEngine(@NonNull FlutterPluginBinding binding) {
channel.setMethodCallHandler(null);
mFlutterPluginBinding = null;
}
@Override
public void onAttachedToActivity(@NonNull ActivityPluginBinding binding) {
mActivityPluginBinding = binding;
mFlutterPluginBinding.getPlatformViewRegistry().registerViewFactory("super_player_view", new SuperPlatformViewFactory(mFlutterPluginBinding, binding.getActivity()));
}
@Override
public void onDetachedFromActivityForConfigChanges() {
}
@Override
public void onReattachedToActivityForConfigChanges(@NonNull ActivityPluginBinding binding) {
}
@Override
public void onDetachedFromActivity() {
}
}
package com.example.super_player; package com.tencent.vod.flutter;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
/**
* player base
*/
public class FTXBasePlayer { public class FTXBasePlayer {
private static final AtomicInteger mAtomicId = new AtomicInteger(0); private static final AtomicInteger mAtomicId = new AtomicInteger(0);
private final int mPlayerId; private final int mPlayerId;
......
package com.example.super_player; package com.tencent.vod.flutter;
import android.app.Activity; import android.app.Activity;
import android.graphics.SurfaceTexture; import android.graphics.SurfaceTexture;
...@@ -10,13 +10,10 @@ import androidx.annotation.NonNull; ...@@ -10,13 +10,10 @@ import androidx.annotation.NonNull;
import com.tencent.rtmp.ITXLivePlayListener; import com.tencent.rtmp.ITXLivePlayListener;
import com.tencent.rtmp.TXLiveBase; import com.tencent.rtmp.TXLiveBase;
import com.tencent.rtmp.TXLiveConstants;
import com.tencent.rtmp.TXLivePlayConfig; import com.tencent.rtmp.TXLivePlayConfig;
import com.tencent.rtmp.TXLivePlayer; import com.tencent.rtmp.TXLivePlayer;
import com.tencent.rtmp.TXVodPlayer;
import java.util.HashMap; import java.util.HashMap;
import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
...@@ -26,6 +23,9 @@ import io.flutter.plugin.common.MethodCall; ...@@ -26,6 +23,9 @@ import io.flutter.plugin.common.MethodCall;
import io.flutter.plugin.common.MethodChannel; import io.flutter.plugin.common.MethodChannel;
import io.flutter.view.TextureRegistry; import io.flutter.view.TextureRegistry;
/**
* live player processor
*/
public class FTXLivePlayer extends FTXBasePlayer implements MethodChannel.MethodCallHandler, ITXLivePlayListener { public class FTXLivePlayer extends FTXBasePlayer implements MethodChannel.MethodCallHandler, ITXLivePlayListener {
private static final String TAG = "FTXLivePlayer"; private static final String TAG = "FTXLivePlayer";
...@@ -156,13 +156,9 @@ public class FTXLivePlayer extends FTXBasePlayer implements MethodChannel.Method ...@@ -156,13 +156,9 @@ public class FTXLivePlayer extends FTXBasePlayer implements MethodChannel.Method
setMute(mute); setMute(mute);
result.success(null); result.success(null);
}else if (call.method.equals("seek")) { }else if (call.method.equals("seek")) {
double progress = call.argument("progress"); result.notImplemented();
seek((float) progress);
result.success(null);
}else if (call.method.equals("setRate")) { }else if (call.method.equals("setRate")) {
double rate = call.argument("rate"); result.notImplemented();
setRate((float) rate);
result.success(null);
}else if(call.method.equals("setVolume")) { }else if(call.method.equals("setVolume")) {
Integer volume = call.argument("volume"); Integer volume = call.argument("volume");
setVolume(volume); setVolume(volume);
...@@ -184,11 +180,11 @@ public class FTXLivePlayer extends FTXBasePlayer implements MethodChannel.Method ...@@ -184,11 +180,11 @@ public class FTXLivePlayer extends FTXBasePlayer implements MethodChannel.Method
setAppID(appId); setAppID(appId);
result.success(null); result.success(null);
}else if(call.method.equals("prepareLiveSeek")) { }else if(call.method.equals("prepareLiveSeek")) {
String domain = call.argument("domain"); result.notImplemented();
int bizId = call.argument("bizId"); }else if(call.method.equals("resumeLive")) {
int r = prepareLiveSeek(domain, bizId); int r = resumeLive();
result.success(r); result.success(r);
} else { }else {
result.notImplemented(); result.notImplemented();
} }
} }
...@@ -334,6 +330,20 @@ public class FTXLivePlayer extends FTXBasePlayer implements MethodChannel.Method ...@@ -334,6 +330,20 @@ public class FTXLivePlayer extends FTXBasePlayer implements MethodChannel.Method
return Uninitialized; return Uninitialized;
} }
private int resumeLive() {
if (mLivePlayer != null) {
return mLivePlayer.resumeLive();
}
return Uninitialized;
}
private void setRenderMode(int renderMode) {
if (mLivePlayer != null) {
mLivePlayer.setRenderMode(renderMode);
}
}
private Map<String, Object> getParams(int event, Bundle bundle) { private Map<String, Object> getParams(int event, Bundle bundle) {
Map<String, Object> param = new HashMap(); Map<String, Object> param = new HashMap();
if (event != 0) { if (event != 0) {
......
package com.example.super_player; package com.tencent.vod.flutter;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.Queue; import java.util.Queue;
import io.flutter.plugin.common.EventChannel; import io.flutter.plugin.common.EventChannel;
/**
* handle flutter event
*/
public class FTXPlayerEventSink implements EventChannel.EventSink { public class FTXPlayerEventSink implements EventChannel.EventSink {
private EventChannel.EventSink eventSink; private EventChannel.EventSink eventSink;
......
package com.tencent.vod.flutter;
import android.text.TextUtils;
import com.tencent.rtmp.TXPlayerGlobalSetting;
import com.tencent.rtmp.TXVodPlayConfig;
import java.util.HashMap;
import java.util.Map;
/**
* 对象转化
*/
public class FTXTransformation {
@SuppressWarnings("unchecked")
public static TXVodPlayConfig transformToConfig(Map<Object, Object> config) {
TXVodPlayConfig playConfig = new TXVodPlayConfig();
Integer connectRetryCount = (Integer) config.get("connectRetryCount");
if (intIsNotEmpty(connectRetryCount)) {
playConfig.setConnectRetryCount(connectRetryCount);
}
Integer connectRetryInterval = (Integer) config.get("connectRetryInterval");
if (intIsNotEmpty(connectRetryInterval)) {
playConfig.setConnectRetryInterval(connectRetryInterval);
}
Integer timeout = (Integer) config.get("timeout");
if (intIsNotEmpty(timeout)) {
playConfig.setTimeout(timeout);
}
Integer playerType = (Integer) config.get("playerType");
if(null != playerType) {
playConfig.setPlayerType(playerType);
}
Map<String, String> headers = (Map<String, String>) config.get("headers");
if (null == headers) {
headers = new HashMap<>();
}
playConfig.setHeaders(headers);
Boolean enableAccurateSeek = (Boolean) config.get("enableAccurateSeek");
if(null != enableAccurateSeek) {
playConfig.setEnableAccurateSeek(enableAccurateSeek);
}
Boolean autoRotate = (Boolean) config.get("autoRotate");
if(null != autoRotate) {
playConfig.setAutoRotate(autoRotate);
}
Boolean smoothSwitchBitrate = (Boolean) config.get("smoothSwitchBitrate");
if(null != smoothSwitchBitrate) {
playConfig.setSmoothSwitchBitrate(smoothSwitchBitrate);
}
String cacheMp4ExtName = (String) config.get("cacheMp4ExtName");
if (!TextUtils.isEmpty(cacheMp4ExtName)) {
playConfig.setCacheMp4ExtName(cacheMp4ExtName);
}
Integer progressInterval = (Integer) config.get("progressInterval");
if (intIsNotEmpty(progressInterval)) {
playConfig.setProgressInterval(progressInterval);
}
Integer maxBufferSize = (Integer) config.get("maxBufferSize");
if (intIsNotEmpty(maxBufferSize)) {
playConfig.setMaxBufferSize(maxBufferSize);
}
Integer maxPreloadSize = (Integer) config.get("maxPreloadSize");
if (intIsNotEmpty(maxPreloadSize)) {
playConfig.setMaxPreloadSize(maxPreloadSize);
}
Integer firstStartPlayBufferTime = (Integer) config.get("firstStartPlayBufferTime");
if(null != firstStartPlayBufferTime) {
playConfig.setFirstStartPlayBufferTime(firstStartPlayBufferTime);
}
Integer nextStartPlayBufferTime = (Integer) config.get("nextStartPlayBufferTime");
if(null != nextStartPlayBufferTime) {
playConfig.setNextStartPlayBufferTime(nextStartPlayBufferTime);
}
String overlayKey = (String) config.get("overlayKey");
if (!TextUtils.isEmpty(overlayKey)) {
playConfig.setOverlayKey(overlayKey);
}
String overlayIv = (String) config.get("overlayIv");
if (!TextUtils.isEmpty(overlayIv)) {
playConfig.setOverlayIv(overlayIv);
}
Map<String, Object> extInfoMap = (Map<String, Object>) config.get("extInfoMap");
if (null == extInfoMap) {
extInfoMap = new HashMap<>();
}
playConfig.setExtInfo(extInfoMap);
Boolean enableRenderProcess = (Boolean) config.get("enableRenderProcess");
if(null != enableRenderProcess) {
playConfig.mEnableRenderProcess = enableRenderProcess;
}
String preferredResolutionStr = (String) config.get("preferredResolution");
if (null != preferredResolutionStr) {
long preferredResolution = Long.parseLong(preferredResolutionStr);
playConfig.setPreferredResolution(preferredResolution);
}
return playConfig;
}
private static boolean intIsNotEmpty(Integer value) {
return null != value && value > 0;
}
}
package com.tencent.vod.flutter;
import android.text.TextUtils;
import android.util.Log;
import android.util.SparseArray;
import androidx.annotation.NonNull;
import com.tencent.rtmp.TXLiveBase;
import com.tencent.rtmp.TXPlayerGlobalSetting;
import io.flutter.embedding.engine.plugins.FlutterPlugin;
import io.flutter.embedding.engine.plugins.activity.ActivityAware;
import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding;
import io.flutter.plugin.common.MethodCall;
import io.flutter.plugin.common.MethodChannel;
import io.flutter.plugin.common.MethodChannel.MethodCallHandler;
import io.flutter.plugin.common.MethodChannel.Result;
/**
* SuperPlayerPlugin
* <p>
* The MethodChannel that will the communication between Flutter and native Android
* This local reference serves to register the plugin with the Flutter Engine and unregister it
* when the Flutter Engine is detached from the Activity
*/
public class SuperPlayerPlugin implements FlutterPlugin, MethodCallHandler, ActivityAware {
static final String TAG = "SuperPlayerPlugin";
private static final int SUPPORT_MAJOR_VERSION = 8;
private static final int SUPPORT_MINOR_VERSION = 5;
private MethodChannel channel;
private FlutterPluginBinding mFlutterPluginBinding;
private ActivityPluginBinding mActivityPluginBinding;
private SparseArray<FTXBasePlayer> mPlayers;
@Override
public void onAttachedToEngine(@NonNull FlutterPluginBinding flutterPluginBinding) {
mFlutterPluginBinding = flutterPluginBinding;
channel = new MethodChannel(flutterPluginBinding.getBinaryMessenger(), "flutter_super_player");
channel.setMethodCallHandler(this);
mPlayers = new SparseArray();
}
@Override
public void onMethodCall(@NonNull MethodCall call, @NonNull Result result) {
if (call.method.equals("getPlatformVersion")) {
result.success("Android " + android.os.Build.VERSION.RELEASE);
} else if (call.method.equals("createVodPlayer")) {
FTXVodPlayer player = new FTXVodPlayer(mFlutterPluginBinding);
int playerId = player.getPlayerId();
mPlayers.append(playerId, player);
result.success(playerId);
} else if (call.method.equals("createLivePlayer")) {
FTXLivePlayer player = new FTXLivePlayer(mFlutterPluginBinding, mActivityPluginBinding.getActivity());
int playerId = player.getPlayerId();
mPlayers.append(playerId, player);
result.success(playerId);
} else if (call.method.equals("releasePlayer")) {
Integer playerId = call.argument("playerId");
FTXBasePlayer player = mPlayers.get(playerId);
if (player != null) {
player.destory();
mPlayers.remove(playerId);
}
result.success(null);
} else if (call.method.equals("setConsoleEnabled")) {
boolean bnabled = call.argument("enabled");
TXLiveBase.setConsoleEnabled(bnabled);
result.success(null);
} else if (call.method.equals("setGlobalMaxCacheSize")) {
Integer size = call.argument("size");
if (null != size && size > 0) {
TXPlayerGlobalSetting.setMaxCacheSize(size);
}
} else if (call.method.equals("setGlobalCacheFolderPath")) {
String path = call.argument("path");
if (!TextUtils.isEmpty(path)) {
TXPlayerGlobalSetting.setCacheFolderPath(path);
}
} else if (call.method.equals("setGlobalLicense")) {
String licenceUrl = call.argument("licenceUrl");
String licenceKey = call.argument("licenceKey");
TXLiveBase.getInstance().setLicence(mFlutterPluginBinding.getApplicationContext(), licenceUrl, licenceKey);
} else {
result.notImplemented();
}
}
private boolean isVersionSupportAppendUrl() {
String strVersion = TXLiveBase.getSDKVersionStr();
String[] strVers = strVersion.split("\\.");
if (strVers.length <= 1) {
return false;
}
int majorVer = 0;
int minorVer = 0;
try {
majorVer = Integer.parseInt(strVers[0]);
minorVer = Integer.parseInt(strVers[1]);
} catch (NumberFormatException e) {
Log.e(TAG, "parse version failed.", e);
majorVer = 0;
minorVer = 0;
}
Log.i(TAG, strVersion + " , " + majorVer + " , " + minorVer);
return majorVer > SUPPORT_MAJOR_VERSION || (majorVer == SUPPORT_MAJOR_VERSION && minorVer >= SUPPORT_MINOR_VERSION);
}
@Override
public void onDetachedFromEngine(@NonNull FlutterPluginBinding binding) {
channel.setMethodCallHandler(null);
mFlutterPluginBinding = null;
}
@Override
public void onAttachedToActivity(@NonNull ActivityPluginBinding binding) {
mActivityPluginBinding = binding;
}
@Override
public void onDetachedFromActivityForConfigChanges() {
}
@Override
public void onReattachedToActivityForConfigChanges(@NonNull ActivityPluginBinding binding) {
}
@Override
public void onDetachedFromActivity() {
}
}
差异被折叠。
...@@ -19,13 +19,26 @@ SDK 早期版本只有 TXLivePlayer 一个 Class 承载直播和点播功能, ...@@ -19,13 +19,26 @@ SDK 早期版本只有 TXLivePlayer 一个 Class 承载直播和点播功能,
## 对接攻略 ## 对接攻略
### step 1: 创建 Player ### 步骤1:申请视频播放能力License和集成
集成播放器前,需要[注册腾讯云账户](https://cloud.tencent.com/login),注册成功后申请视频播放能力License, 然后通过下面方式集成,建议在应用启动时进行。
如果没有集成license,播放过程中可能会出现异常。
```dart
String licenceURL = ""; // 获取到的 licence url
String licenceKey = ""; // 获取到的 licence key
SuperPlayerPlugin.setGlobalLicense(licenceURL, licenceKey);
```
### 步骤2: 创建 Player
视频云 SDK 中的 TXLivePlayer 模块负责实现直播播放功能。对应于 Flutter 是 TXLivePlayerController 视频云 SDK 中的 TXLivePlayer 模块负责实现直播播放功能。对应于 Flutter 是 TXLivePlayerController
```dart ```dart
TXLivePlayerController controller = TXLivePlayerController(); TXLivePlayerController controller = TXLivePlayerController();
``` ```
### step 2: 渲染 View ### 步骤3: 渲染 View
接下来我们要给播放器的视频画面找个地方来显示,Flutter 系统中使用 Widget 作为基本的界面渲染单位,所以您只需要准备一个 Widget 并调整好布局就可以了。你可以直接使用 TXPlayerVideo 或者继承它来显示,也可以参考源码实现自定义视图。 接下来我们要给播放器的视频画面找个地方来显示,Flutter 系统中使用 Widget 作为基本的界面渲染单位,所以您只需要准备一个 Widget 并调整好布局就可以了。你可以直接使用 TXPlayerVideo 或者继承它来显示,也可以参考源码实现自定义视图。
```dart ```dart
...@@ -73,7 +86,7 @@ TXLivePlayerController controller = TXLivePlayerController(); ...@@ -73,7 +86,7 @@ TXLivePlayerController controller = TXLivePlayerController();
3. 将该id通过channel机制传递给dart侧,dart侧就能够通过 `Texture` 这个widget来使用纹理了,参数就是id 3. 将该id通过channel机制传递给dart侧,dart侧就能够通过 `Texture` 这个widget来使用纹理了,参数就是id
4. flutter engine调用 `copyPixelBuffer` 拿到具体的纹理数据,然后交由底层进行gpu渲染 4. flutter engine调用 `copyPixelBuffer` 拿到具体的纹理数据,然后交由底层进行gpu渲染
### step 3: 启动播放 ### 步骤4: 启动播放
```dart ```dart
String flvUrl = "http://liteavapp.qcloud.com/live/liteavdemoplayerstreamid_demo1080p.flv"; String flvUrl = "http://liteavapp.qcloud.com/live/liteavdemoplayerstreamid_demo1080p.flv";
await _controller.play("flvUrl", playType: TXPlayType.LIVE_FLV); await _controller.play("flvUrl", playType: TXPlayType.LIVE_FLV);
...@@ -89,7 +102,7 @@ await _controller.play("flvUrl", playType: TXPlayType.LIVE_FLV); ...@@ -89,7 +102,7 @@ await _controller.play("flvUrl", playType: TXPlayType.LIVE_FLV);
> **关于 HLS(m3u8)** > **关于 HLS(m3u8)**
> 在 App 上我们不推荐使用 HLS 这种播放协议播放直播视频源(虽然它很适合用来做点播),因为延迟太高,在 App 上推荐使用 LIVE_FLV 或者 LIVE_RTMP 播放协议。 > 在 App 上我们不推荐使用 HLS 这种播放协议播放直播视频源(虽然它很适合用来做点播),因为延迟太高,在 App 上推荐使用 LIVE_FLV 或者 LIVE_RTMP 播放协议。
### step 5: 暂停播放 ### 步骤5: 暂停播放
对于直播播放而言,并没有真正意义上的暂停,所谓的直播暂停,只是**画面冻结****关闭声音**,而云端的视频源还在不断地更新着,所以当您调用 resume 的时候,会从最新的时间点开始播放,这跟点播是有很大不同的(点播播放器的暂停和继续与播放本地视频文件时的表现相同)。 对于直播播放而言,并没有真正意义上的暂停,所谓的直播暂停,只是**画面冻结****关闭声音**,而云端的视频源还在不断地更新着,所以当您调用 resume 的时候,会从最新的时间点开始播放,这跟点播是有很大不同的(点播播放器的暂停和继续与播放本地视频文件时的表现相同)。
```dart ```dart
...@@ -99,13 +112,14 @@ _controller.pause(); ...@@ -99,13 +112,14 @@ _controller.pause();
_controller.resume(); _controller.resume();
``` ```
### step 6: 结束播放 ### 步骤6: 结束播放
```dart ```dart
// 停止播放 // 停止播放
_controller.stop(); _controller.stop();
``` ```
<h3 id="Message">step 7: 消息接收</h3> <h3 id="Message">步骤7: 消息接收</h3>
此功能可以在推流端将一些自定义 message 随着音视频线路直接下发到观众端,适用场景例如: 此功能可以在推流端将一些自定义 message 随着音视频线路直接下发到观众端,适用场景例如:
(1)冲顶大会:推流端将<strong>题目</strong>下发到观众端,可以做到“音-画-题”完美同步。 (1)冲顶大会:推流端将<strong>题目</strong>下发到观众端,可以做到“音-画-题”完美同步。
(2)秀场直播:推流端将<strong>歌词</strong>下发到观众端,可以在播放端实时绘制出歌词特效,因而不受视频编码的降质影响。 (2)秀场直播:推流端将<strong>歌词</strong>下发到观众端,可以在播放端实时绘制出歌词特效,因而不受视频编码的降质影响。
...@@ -122,7 +136,7 @@ _controller.onPlayerEventBroadcast.listen((event) {//订阅事件分发 ...@@ -122,7 +136,7 @@ _controller.onPlayerEventBroadcast.listen((event) {//订阅事件分发
}); });
``` ```
### step 8: 清晰度无缝切换 ### 步骤8: 清晰度无缝切换
日常使用中,网络情况在不断发生变化。在网络较差的情况下,最好适度降低画质,以减少卡顿;反之,网速比较好,可以观看更高画质。 日常使用中,网络情况在不断发生变化。在网络较差的情况下,最好适度降低画质,以减少卡顿;反之,网速比较好,可以观看更高画质。
传统切流方式一般是重新播放,会导致切换前后画面衔接不上、黑屏、卡顿等问题。使用无缝切换方案,在不中断直播的情况下,能直接切到另条流上。 传统切流方式一般是重新播放,会导致切换前后画面衔接不上、黑屏、卡顿等问题。使用无缝切换方案,在不中断直播的情况下,能直接切到另条流上。
...@@ -136,7 +150,7 @@ _controller.switchStream("http://5815.liveplay.myqcloud.com/live/5815_62fe94d692 ...@@ -136,7 +150,7 @@ _controller.switchStream("http://5815.liveplay.myqcloud.com/live/5815_62fe94d692
>清晰度无缝切换功能需要在后台配置 PTS 对齐,如您需要可 [提交工单](https://console.cloud.tencent.com/workorder) 申请使用。 >清晰度无缝切换功能需要在后台配置 PTS 对齐,如您需要可 [提交工单](https://console.cloud.tencent.com/workorder) 申请使用。
### step 9: 直播回看 ### 步骤9: 直播回看
时移功能是腾讯云推出的特色能力,可以在直播过程中,随时观看回退到任意直播历史时间点,并能在此时间点一直观看直播。非常适合游戏、球赛等互动性不高,但观看连续性较强的场景。 时移功能是腾讯云推出的特色能力,可以在直播过程中,随时观看回退到任意直播历史时间点,并能在此时间点一直观看直播。非常适合游戏、球赛等互动性不高,但观看连续性较强的场景。
```dart ```dart
...@@ -207,6 +221,193 @@ _controller.setLiveMode(TXPlayerLiveMode.Smooth); ...@@ -207,6 +221,193 @@ _controller.setLiveMode(TXPlayerLiveMode.Smooth);
- **该功能按播放时长收费** - **该功能按播放时长收费**
本功能按照播放时长收费,费用跟拉流的路数有关系,跟音视频流的码率无关,具体价格请参考 [价格总览](https://cloud.tencent.com/document/product/454/8008) 本功能按照播放时长收费,费用跟拉流的路数有关系,跟音视频流的码率无关,具体价格请参考 [价格总览](https://cloud.tencent.com/document/product/454/8008)
## SDK 接口列表[](id:sdkList)
#### 初始化播放器
**说明**
初始化controller,请求分配共享纹理
**接口**
```dart
await _controller.initialize();
```
#### url播放
**说明**
通过播视频url进行播放
**接口**
```dart
_controller.startPlay(url);
```
**参数说明**
| 参数名 | 类型 | 描述 |
| ------ | ------ | ------------------ |
| url | String | 要播放的视频url |
| playType | int | 支持的直播类型,默认RTMP直播 可选|
#### 暂停播放
**说明**
暂停当前正在播放的视频
**接口**
```dart
_controller.pause();
```
#### 恢复播放
**说明**
将当前处于暂停状态的视频恢复播放
**接口**
```dart
_controller.resume();
```
#### 停止播放
**说明**
停止当前正在播放的视频
**接口**
```dart
_controller.stop();
```
**参数说明**
| 参数名 | 类型 | 描述 |
| ------ | ------ | ------------------ |
| isNeedClear | bool | 是否清除最后一帧画面 |
#### 设置是否自动播放
**说明**
设置即将播放的视频,在startPlay加载视频地址之后,是否直接自动播放
**接口**
```dart
_controller.setIsAutoPlay(true);
```
**参数说明**
| 参数名 | 类型 | 描述 |
| ------ | ------ | ------------------ |
| isAutoPlay | bool | 是否自动播放 |
#### 播放器是否在播放中
**说明**
当前播放器是否正在播放
**接口**
```dart
_controller.isPlaying();
```
#### 设置静音
**说明**
设置当前播放是否静音
**接口**
```dart
_controller.setMute(true);
```
**参数说明**
| 参数名 | 类型 | 描述 |
| ------ | ------ | ------------------ |
| mute | bool | 是否静音 |
#### 设置视频声音大小
**说明**
设置视频的声音大小
**接口**
```dart
_controller.setVolume(volume);
```
**参数说明**
| 参数名 | 类型 | 描述 |
| ------ | ------ | ------------------ |
| volume | int | 视频声音大小,范围0~100 |
#### 设置直播模式
**说明**
设置直播模式
**接口**
```dart
_controller.setLiveMode(mode);
```
**参数说明**
| 参数名 | 类型 | 描述 |
| ------ | ------ | ------------------ |
| mode | int | 直播模式,自动模式、极速模式、流畅模式 |
#### 设置 appID
**说明**
设置 appID,云控使用
**接口**
```dart
_controller.setAppID(appId);
```
**参数说明**
| 参数名 | 类型 | 描述 |
| ------ | ------ | ------------------ |
| appId | int | appId |
#### 返回直播
**说明**
停止时移播放,返回直播
**接口**
```dart
_controller.resumeLive();
```
## SDK 事件监听 ## SDK 事件监听
SDK 的内部状态信息均会通过 onPlayerEventBroadcast(事件通知)和 onPlayerNetStatusBroadcast(状态反馈)通知给您。 SDK 的内部状态信息均会通过 onPlayerEventBroadcast(事件通知)和 onPlayerNetStatusBroadcast(状态反馈)通知给您。
......
差异被折叠。
...@@ -16,6 +16,12 @@ allprojects { ...@@ -16,6 +16,12 @@ allprojects {
} }
} }
/**
* 在此处可以更换需要的SDK版本,替换为专业版为 com.tencent.liteav:LiteAVSDK_Professional:latest.release
* 其中 latest.release 可指定为自己需要的版本号,例如 "com.tencent.liteav:LiteAVSDK_Player:9.5.29035" ,版本号写成 latest.release 则默认使用最新版本。
* 如果赋值为空字符串或者null,则sdk中默认依赖com.tencent.liteav:LiteAVSDK_Player的最新版本
*/
rootProject.ext.liteavSdkCustom=""
rootProject.buildDir = '../build' rootProject.buildDir = '../build'
subprojects { subprojects {
project.buildDir = "${rootProject.buildDir}/${project.name}" project.buildDir = "${rootProject.buildDir}/${project.name}"
...@@ -27,17 +33,3 @@ subprojects { ...@@ -27,17 +33,3 @@ subprojects {
task clean(type: Delete) { task clean(type: Delete) {
delete rootProject.buildDir delete rootProject.buildDir
} }
\ No newline at end of file
ext {
compileSdkVersion = 28
buildToolsVersion = "28.0.3"
supportSdkVersion = "26.0.1"
minSdkVersion = 19
targetSdkVersion = 28
liteavSdk="com.tencent.liteav:LiteAVSDK_Player: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'
}
include ':superplayerkit'
include ':app' include ':app'
def localPropertiesFile = new File(rootProject.projectDir, "local.properties") def localPropertiesFile = new File(rootProject.projectDir, "local.properties")
......
apply plugin: 'com.android.library'
android {
compileSdkVersion rootProject.ext.compileSdkVersion
buildToolsVersion rootProject.ext.buildToolsVersion
defaultConfig {
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
}
profile {
}
}
}
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
// compile project(':liteav_sdk')
compile rootProject.ext.liteavSdk
compile 'com.github.ctiao:DanmakuFlameMaster:0.5.3'
}
\ No newline at end of file
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
-keep class com.tencent.** { *; }
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.tencent.liteav.demo.superplayer" />
package com.tencent.liteav.demo.superplayer;
public class SuperPlayerCode {
public static final int OK = 0;
public static final int NET_ERROR = 10001;
public static final int PLAY_URL_EMPTY = 20001;
public static final int LIVE_PLAY_END = 30001;
public static final int LIVE_SHIFT_FAIL = 30002;
public static final int VOD_PLAY_FAIL = 40001;
public static final int VOD_REQUEST_FILE_ID_FAIL = 40002;
}
package com.tencent.liteav.demo.superplayer;
public class SuperPlayerDef {
public enum PlayerMode {
WINDOW, // 窗口模式
FULLSCREEN, // 全屏模式
FLOAT // 悬浮窗模式
}
public enum PlayerState {
PLAYING, // 播放中
PAUSE, // 暂停中
LOADING, // 缓冲中
END // 结束播放
}
public enum PlayerType {
VOD, // 点播
LIVE, // 直播
LIVE_SHIFT // 直播会看
}
public enum Orientation {
LANDSCAPE, // 横屏
PORTRAIT // 竖屏
}
}
package com.tencent.liteav.demo.superplayer;
import com.tencent.rtmp.TXLiveConstants;
import java.util.HashMap;
import java.util.Map;
/**
* Created by yuejiaoli on 2018/7/4.
* <p>
* 超级播放器全局配置类
*/
public class SuperPlayerGlobalConfig {
private static class Singleton {
private static SuperPlayerGlobalConfig sInstance = new SuperPlayerGlobalConfig();
}
public static SuperPlayerGlobalConfig getInstance() {
return Singleton.sInstance;
}
/**
* 默认播放填充模式 ( 默认播放模式为 自适应模式 )
*/
public int renderMode = TXLiveConstants.RENDER_MODE_ADJUST_RESOLUTION;
/**
* 播放器最大缓存个数 ( 默认缓存 5 )
*/
public int maxCacheItem = 5;
/**
* 是否启用悬浮窗 ( 默认开启 true )
*/
public boolean enableFloatWindow = true;
/**
* 是否开启硬件加速 ( 默认开启硬件加速 )
*/
public boolean enableHWAcceleration = true;
/**
* 时移域名 (修改为自己app的时移域名)
*/
public String playShiftDomain = "liteavapp.timeshift.qcloud.com";
/**
* 悬浮窗位置 ( 默认在左上角,初始化一个宽为 810,高为 540的悬浮窗口 )
*/
public TXRect floatViewRect = new TXRect(0, 0, 810, 540);
/**
* 是否静音
*/
public boolean mute = false;
/**
* 是否开启log,与原有逻辑保持一直,默认开启
*/
public boolean enableLog = true;
/**
* 是否开启镜面
*/
public boolean mirror = false;
/**
* 请求header
*/
public Map<String,String> headers = new HashMap<>();
/**
* 播放速率
*/
public float playRate = 1.0F;
public final static class TXRect {
public int x;
public int y;
public int width;
public int height;
TXRect(int x, int y, int width, int height) {
this.x = x;
this.y = y;
this.width = width;
this.height = height;
}
public TXRect() {
}
}
}
package com.tencent.liteav.demo.superplayer;
import com.tencent.liteav.demo.superplayer.model.entity.SuperPlayerVideoIdV2;
import java.util.List;
/**
* 超级播放器支持三种方式播放视频:
* 1. 视频 URL
* 填写视频 URL, 如需使用直播时移功能,还需填写appId
* 2. 腾讯云点播 File ID 播放
* 填写 appId 及 videoId (如果使用旧版本V2, 请填写videoIdV2)
* 3. 多码率视频播放
* 是URL播放方式扩展,可同时传入多条URL,用于进行码率切换
*/
public class SuperPlayerModel {
public int appId; // AppId 用于腾讯云点播 File ID 播放及腾讯云直播时移功能
/**
* ------------------------------------------------------------------
* 直接使用URL播放
* <p>
* 支持 RTMP、FLV、MP4、HLS 封装格式
* 使用腾讯云直播时移功能则需要填写appId
* ------------------------------------------------------------------
*/
public String url = ""; // 视频URL
/**
* ------------------------------------------------------------------
* 多码率视频 URL
* <p>
* 用于拥有多个播放地址的多清晰度视频播放
* ------------------------------------------------------------------
*/
public List<SuperPlayerURL> multiURLs;
public int playDefaultIndex; // 指定多码率情况下,默认播放的连接Index
/**
* ------------------------------------------------------------------
* 腾讯云点播 File ID 播放参数
* ------------------------------------------------------------------
*/
public SuperPlayerVideoId videoId;
/*
* 用于兼容旧版本(V2)腾讯云点播 File ID 播放参数(即将废弃,不推荐使用)
*/
@Deprecated
public SuperPlayerVideoIdV2 videoIdV2;
public String title = ""; // 视频文件名 (用于显示在UI层);使用file id播放,若未指定title,则使用FileId返回的Title;使用url播放需要指定title,否则title显示为空
public static class SuperPlayerURL {
public SuperPlayerURL(String url, String qualityName) {
this.qualityName = qualityName;
this.url = url;
}
public SuperPlayerURL() {
}
public String qualityName = "原画"; // 清晰度名称(用于显示在UI层)
public String url = ""; // 该清晰度对应的地址
}
}
package com.tencent.liteav.demo.superplayer;
/**
* Created by hans on 2019/3/25.
* 使用腾讯云fileId播放
*/
public class SuperPlayerVideoId {
public String fileId; // 腾讯云视频fileId
public String pSign; // v4 开启防盗链必填
@Override
public String toString() {
return "SuperPlayerVideoId{" +
", fileId='" + fileId + '\'' +
", pSign='" + pSign + '\'' +
'}';
}
}
package com.tencent.liteav.demo.superplayer.model;
import android.os.Bundle;
import com.tencent.rtmp.TXVodPlayer;
public interface ISuperPlayerListener {
public void onVodPlayEvent(final TXVodPlayer player, final int event, final Bundle param);
public void onVodNetStatus(final TXVodPlayer player, final Bundle status);
public void onLivePlayEvent(final int event, final Bundle param);
public void onLiveNetStatus(final Bundle status);
}
package com.tencent.liteav.demo.superplayer.model;
import com.tencent.liteav.demo.superplayer.SuperPlayerDef;
import com.tencent.liteav.demo.superplayer.SuperPlayerModel;
import com.tencent.liteav.demo.superplayer.model.entity.VideoQuality;
import com.tencent.rtmp.TXLivePlayer;
import com.tencent.rtmp.ui.TXCloudVideoView;
import java.util.List;
public interface SuperPlayer {
/**
* 开始播放
*
* @param url 视频地址
*/
void play(String url);
/**
* 开始播放
*
* @param appId 腾讯云视频appId
* @param url 直播播放地址
*/
void play(int appId, String url);
/**
* 开始播放
*
* @param appId 腾讯云视频appId
* @param fileId 腾讯云视频fileId
* @param psign 防盗链签名,开启防盗链的视频必填,非防盗链视频可不填
*/
void play(int appId, String fileId, String psign);
/**
* 多分辨率播放
* @param appId 腾讯云视频appId
* @param superPlayerURLS 不同分辨率数据
* @param defaultIndex 默认播放Index
*/
void play(int appId, List<SuperPlayerModel.SuperPlayerURL> superPlayerURLS, int defaultIndex);
/**
* 重播
*/
void reStart();
/**
* 暂停播放
*/
void pause();
/**
* 暂停点播视频
*/
void pauseVod();
/**
* 恢复播放
*/
void resume();
/**
* 恢复直播播放,从直播时移播放中,恢复到直播播放。
*/
void resumeLive();
/**
* 停止播放
*/
void stop();
/**
* 销毁播放器
*/
void destroy();
/**
* 切换播放器模式
*
* @param playerMode {@link SuperPlayerDef.PlayerMode#WINDOW } 窗口模式
* {@link SuperPlayerDef.PlayerMode#FULLSCREEN } 全屏模式
* {@link SuperPlayerDef.PlayerMode#FLOAT } 悬浮窗模式
*/
void switchPlayMode(SuperPlayerDef.PlayerMode playerMode);
void enableHardwareDecode(boolean enable);
void setPlayerView(TXCloudVideoView videoView);
void seek(int position);
void snapshot(TXLivePlayer.ITXSnapshotListener listener);
void setRate(float speedLevel);
void setMirror(boolean isMirror);
void switchStream(VideoQuality quality);
String getPlayURL();
/**
* 获取当前播放器模式
*
* @return {@link SuperPlayerDef.PlayerMode#WINDOW } 窗口模式
* {@link SuperPlayerDef.PlayerMode#FULLSCREEN } 全屏模式
* {@link SuperPlayerDef.PlayerMode#FLOAT } 悬浮窗模式
*/
SuperPlayerDef.PlayerMode getPlayerMode();
/**
* 获取当前播放器状态
*
* @return {@link SuperPlayerDef.PlayerState#PLAYING } 播放中
* {@link SuperPlayerDef.PlayerState#PAUSE } 暂停中
* {@link SuperPlayerDef.PlayerState#LOADING } 缓冲中
* {@link SuperPlayerDef.PlayerState#END } 结束播放
*/
SuperPlayerDef.PlayerState getPlayerState();
/**
* 获取当前播放器类型
*
* @return {@link SuperPlayerDef.PlayerType#LIVE } 直播
* {@link SuperPlayerDef.PlayerType#LIVE_SHIFT } 直播时移
* {@link SuperPlayerDef.PlayerType#VOD } 点播
*/
SuperPlayerDef.PlayerType getPlayerType();
/**
* 设置播放器状态回调
*
* @param observer {@link SuperPlayerObserver}
*/
void setObserver(SuperPlayerObserver observer);
/**
* 设置超级播放器中点播事件和直播事件的回调
* @param superPlayerListener
*/
void setSuperPlayerListener(ISuperPlayerListener superPlayerListener);
/**
* 设置是否循环
* @param isLoop true循环,false不循环
*/
void setLoop(boolean isLoop);
/**
* 设置开始时间
* @param startPos 开始时间
*/
void setStartTime(float startPos);
/**
* 设置是否自动播放
* @param isAutoPlay true自动播放,false不自动播放
*/
void setAutoPlay(boolean isAutoPlay);
}
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论