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

1、update version to 1.0.3

2、add PIP for iOS
上级 613b1868
#### Version: 1.0.2 当前最新版本
#### Version: 1.0.3 当前最新版本
##### 分支描述:
......@@ -6,8 +6,20 @@ main 分支:Android & iOS 端集成TXLiteAVSDK_Player lastest版本
Professional 分支:Android & iOS 端集成TXLiteAVSDK_Professional lastest版本
版本特性:
- iOS 端新增画中画(PIP) 功能
#### Version: 1.0.2 2022.07.05
##### 版本特性:
- Android 端新增画中画(PIP) 功能
- 播放器组件(superplayer)用Dart重写,方便自定义集成
- 修复通过appId 、fileId和 psign 播放失败问题
- set Android TXLiteAVSDK_Professional to 10.2.0.11131,tag:release_player_v1.0.2
- set iOS TXLiteAVSDK_Professional to 10.2.11418, tag:release_pro_v1.0.2
group 'com.tencent.vod.flutter'
apply from:'config.gradle'
version '1.0.2'
version '1.0.3'
buildscript {
repositories {
......
// Copyright (c) 2022 Tencent. All rights reserved.
package com.tencent.vod.flutter;
import android.os.Bundle;
import com.tencent.rtmp.downloader.TXVodDownloadDataSource;
import com.tencent.rtmp.downloader.TXVodDownloadMediaInfo;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
/**
* 通用工具类
*/
public class CommonUtil {
static final Map<Integer, Integer> DOWNLOAD_STATE_MAP = new HashMap<Integer, Integer>() {{
put(TXVodDownloadMediaInfo.STATE_INIT, FTXEvent.EVENT_DOWNLOAD_START);
put(TXVodDownloadMediaInfo.STATE_START, FTXEvent.EVENT_DOWNLOAD_PROGRESS);
put(TXVodDownloadMediaInfo.STATE_FINISH, FTXEvent.EVENT_DOWNLOAD_FINISH);
put(TXVodDownloadMediaInfo.STATE_STOP, FTXEvent.EVENT_DOWNLOAD_STOP);
put(TXVodDownloadMediaInfo.STATE_ERROR, FTXEvent.EVENT_DOWNLOAD_ERROR);
}};
public static Map<String, Object> getParams(int event, Bundle bundle) {
Map<String, Object> param = new HashMap<>();
if (event != 0) {
......@@ -24,4 +39,35 @@ public class CommonUtil {
return param;
}
public static int getCacheVideoQuality(Integer width, Integer height) {
if (width == null || height == null) {
return TXVodDownloadDataSource.QUALITY_FLU;
}
int minValue = Math.min(width, height);
int cacheQualityIndex;
if (minValue == 240 || minValue == 180) {
cacheQualityIndex = TXVodDownloadDataSource.QUALITY_FLU;
} else if (minValue == 480 || minValue == 360) {
cacheQualityIndex = TXVodDownloadDataSource.QUALITY_SD;
} else if (minValue == 540) {
cacheQualityIndex = TXVodDownloadDataSource.QUALITY_SD;
} else if (minValue == 720) {
cacheQualityIndex = TXVodDownloadDataSource.QUALITY_HD;
} else if (minValue == 1080) {
cacheQualityIndex = TXVodDownloadDataSource.QUALITY_FHD;
} else if (minValue == 1440) {
cacheQualityIndex = TXVodDownloadDataSource.QUALITY_2K;
} else if (minValue == 2160) {
cacheQualityIndex = TXVodDownloadDataSource.QUALITY_4K;
} else {
cacheQualityIndex = TXVodDownloadDataSource.QUALITY_UNK;
}
return cacheQualityIndex;
}
public static int getDownloadEventByState(int mediaInfoDownloadState) {
Integer event = DOWNLOAD_STATE_MAP.get(mediaInfoDownloadState);
return null != event ? event : FTXEvent.EVENT_DOWNLOAD_ERROR;
}
}
......@@ -24,6 +24,17 @@ public class FTXEvent {
// 视频预下载出错
public static final int EVENT_PREDOWNLOAD_ON_ERROR = 201;
// 视频下载开始
public static final int EVENT_DOWNLOAD_START = 301;
// 视频下载进度
public static final int EVENT_DOWNLOAD_PROGRESS = 302;
// 视频下载停止
public static final int EVENT_DOWNLOAD_STOP = 303;
// 视频下载完成
public static final int EVENT_DOWNLOAD_FINISH = 304;
// 视频下载错误
public static final int EVENT_DOWNLOAD_ERROR = 305;
public static final int NO_ERROR = 0;
/**
* pip 事件
......
......@@ -564,23 +564,24 @@ loader.getVideoData(model, (resultModel) {
#### 画中画模式的使用
##### android平台
1. 安卓平台配置
1. 添加原生配置
1.1 在自己项目的android包下,找到 AndroidManifest.xml ,在项目入口activity节点下,增加如下配置
在自己项目的android包下,找到 AndroidManifest.xml ,在项目入口activity节点下,增加如下配置
```xml
android:supportsPictureInPicture="true"
android:resizeableActivity="true"
android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation"
```
```xml
android:supportsPictureInPicture="true"
android:resizeableActivity="true"
android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation"
```
在自己项目android包下,找到build.gralde,确认 compileSdkVersion 和 targetSdkVersion 的版本为31或以上
在自己项目android包下,找到build.gralde,确认 compileSdkVersion 和 targetSdkVersion 的版本为31或以上
1.2 继承pip activity
2. 继承pip activity
将github项目中example/android 中的 FTXFlutterPipActivity.java 复制到自己入口 Activity 的同目录下,并将自己 Activity 的父类修改为该类。
将github项目中example/android 中的 FTXFlutterPipActivity.java 复制到自己入口 Activity 的同目录下,并将自己 Activity 的父类修改为该类。
2. iOS平台配置
2.1 在自己项目的target下选择Signing & Capabilities 添加Background Modes,勾选Audio,AirPlay,and Picture in Picture
3. 复制superPlayer示例代码
......@@ -600,7 +601,11 @@ SuperPlayerPlugin.instance.onExtraEventBroadcast.listen((event) {
// enter pip mode
} else if (eventCode == TXVodPlayEvent.EVENT_PIP_MODE_ALREADY_ENTER) {
// already enter pip mode
}
} else if (eventCode == TXVodPlayEvent.EVENT_IOS_PIP_MODE_WILL_EXIT) {
// will exit pip mode
} else if (eventCode == TXVodPlayEvent.EVENT_IOS_PIP_MODE_RESTORE_UI) {
// restore UI only support iOS
}
});
```
......@@ -614,6 +619,14 @@ SuperPlayerPlugin.instance.onExtraEventBroadcast.listen((event) {
| ERROR_PIP_LOWER_VERSION | -101 | android版本过低,不支持画中画模式 |
| ERROR_PIP_DENIED_PERMISSION | -102 | 画中画模式权限未打开,或者当前设备不支持画中画 |
| ERROR_PIP_ACTIVITY_DESTROYED | -103 | 当前界面已经销毁 |
| ERROR_IOS_PIP_DEVICE_NOT_SUPPORT | -104 | 设备或系统版本不支持(iPad iOS9+ 才支持PIP) | only support iOS
| ERROR_IOS_PIP_PLAYER_NOT_SUPPORT | -105 | 播放器不支持 | only support iOS
| ERROR_IOS_PIP_VIDEO_NOT_SUPPORT | -106 | 视频不支持 | only support iOS
| ERROR_IOS_PIP_IS_NOT_POSSIBLE | -107 | PIP控制器不可用 | only support iOS
| ERROR_IOS_PIP_FROM_SYSTEM | -108 | PIP控制器报错 | only support iOS
| ERROR_IOS_PIP_PLAYER_NOT_EXIST | -109 | 播放器对象不存在 | only support iOS
| ERROR_IOS_PIP_IS_RUNNING | -110 | PIP功能已经运行 | only support iOS
| ERROR_IOS_PIP_NOT_RUNNING | -111 | PIP功能没有启动 | only support iOS
6. 判断当前设备是否支持画中画
......@@ -628,5 +641,3 @@ if(result == TXVodPlayEvent.NO_ERROR) {
result的返回结果的含义和画中画模式错误码一致
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="com.example.super_player_example">
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<application
android:label="super_player_example"
android:icon="@mipmap/ic_launcher"
android:requestLegacyExternalStorage="true"
tools:replace="android:label">
<activity
android:name=".MainActivity"
......
......@@ -149,6 +149,8 @@
97C146EC1CF9000F007C117D /* Resources */,
9705A1C41CF9048500538489 /* Embed Frameworks */,
3B06AD1E1E4923F5004D2608 /* Thin Binary */,
A040688731A45DDEFDE947A6 /* [CP] Embed Pods Frameworks */,
07B0A88CAEDB5F32ECB22F06 /* [CP] Copy Pods Resources */,
);
buildRules = (
);
......@@ -206,6 +208,23 @@
/* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */
07B0A88CAEDB5F32ECB22F06 /* [CP] Copy Pods Resources */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-input-files.xcfilelist",
);
name = "[CP] Copy Pods Resources";
outputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-output-files.xcfilelist",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh\"\n";
showEnvVarsInLog = 0;
};
2758E5B40301380ECA281800 /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
......@@ -256,6 +275,23 @@
shellPath = /bin/sh;
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build\n";
};
A040688731A45DDEFDE947A6 /* [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;
};
/* End PBXShellScriptBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
......@@ -295,6 +331,7 @@
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
CLANG_ANALYZER_NONNULL = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
......@@ -367,6 +404,7 @@
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
CLANG_ANALYZER_NONNULL = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
......@@ -422,6 +460,7 @@
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
CLANG_ANALYZER_NONNULL = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
......
......@@ -27,6 +27,10 @@
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>
<key>UIBackgroundModes</key>
<array>
<string>audio</string>
</array>
<key>UILaunchStoryboardName</key>
<string>LaunchScreen</string>
<key>UIMainStoryboardFile</key>
......@@ -48,5 +52,7 @@
<false/>
<key>io.flutter.embedded_views_preview</key>
<true/>
<key>CADisableMinimumFrameDurationOnPhone</key>
<true/>
</dict>
</plist>
......@@ -56,8 +56,11 @@ class _DemoTXVodlayerState extends State<DemoTXVodPlayer>
} else if (event["event"] == TXVodPlayEvent.PLAY_EVT_PLAY_PROGRESS) {
_currentProgress = event[TXVodPlayEvent.EVT_PLAY_PROGRESS].toDouble();
double videoDuration = event[TXVodPlayEvent.EVT_PLAY_DURATION].toDouble(); // 总播放时长,转换后的单位 秒
progressSliderKey.currentState?.updatePorgess(_currentProgress / videoDuration, videoDuration);
if (videoDuration == 0.0) {
progressSliderKey.currentState?.updatePorgess(0.0, 0.0);
} else {
progressSliderKey.currentState?.updatePorgess(_currentProgress / videoDuration, videoDuration);
}
} else if (event["event"] == TXVodPlayEvent.PLAY_EVT_GET_PLAYINFO_SUCC) {
String? playUrl = event[TXVodPlayEvent.EVT_PLAY_URL]?.toString();
}
......
......@@ -16,4 +16,8 @@ class StringResource {
static const BRIGHTNESS_LABEL = "亮度";
static const MULITIPE_SPEED_PLAY_LABEL = "多倍速播放";
static const HARDWARE_ACCE_LABEL = "硬件加速";
static const OPEN_PIP = "正在开启画中画";
static const CLOSE_PIP = "正在关闭画中画";
static const ERROR_PIP = "画中画开启失败";
}
......@@ -11,6 +11,7 @@ import 'package:auto_orientation/auto_orientation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/services.dart';
import 'package:flutter_easyloading/flutter_easyloading.dart';
import 'package:super_player/super_player.dart';
part 'superplayer_observer.dart';
......
......@@ -90,27 +90,51 @@ class SuperPlayerViewState extends State<SuperPlayerView> with WidgetsBindingObs
}
});
// only register listen once
_pipSubscription = SuperPlayerPlugin.instance.onExtraEventBroadcast.listen((event) {
_pipSubscription =
SuperPlayerPlugin.instance.onExtraEventBroadcast.listen((event) {
int eventCode = event["event"];
if (eventCode == TXVodPlayEvent.EVENT_PIP_MODE_ALREADY_EXIT) {
// exit floatingMode
Navigator.of(context).pop();
_isFloatingMode = false;
if (_isPlaying) {
// pause play when exit PIP, prevent user just close PIP, but not back to app
_playController._vodPlayerController?.pause();
if (Platform.isAndroid) {
Navigator.of(context).pop();
if (_isPlaying) {
// pause play when exit PIP, prevent user just close PIP, but not back to app
_playController._vodPlayerController?.pause();
}
} else if (Platform.isIOS) {
EasyLoading.dismiss();
}
_isFloatingMode = false;
} else if (eventCode == TXVodPlayEvent.EVENT_PIP_MODE_REQUEST_START) {
// EVENT_PIP_MODE_ALREADY_ENTER 的状态变化有滞后性,进入PIP之后才会通知,这里需要监听EVENT_PIP_MODE_REQUEST_START,
// 在即将进入PIP模式下就要开始进行PIP模式的UI准备
// enter floatingMode
Navigator.of(context).push(MaterialPageRoute(builder: (context) {
return SuperPlayerFloatView(_playController, _aspectRatio);
}));
if (Platform.isAndroid) {
Navigator.of(context).push(MaterialPageRoute(builder: (context) {
return SuperPlayerFloatView(_playController, _aspectRatio);
}));
} else if (Platform.isIOS) {
EasyLoading.showToast(StringResource.OPEN_PIP);
}
_isFloatingMode = true;
} else if (eventCode == TXVodPlayEvent.EVENT_PIP_MODE_ALREADY_ENTER) {
if (Platform.isIOS) {
EasyLoading.dismiss();
}
} else if (eventCode == TXVodPlayEvent.EVENT_IOS_PIP_MODE_WILL_EXIT) {
if (Platform.isIOS) {
EasyLoading.showToast(StringResource.CLOSE_PIP);
}
} else {
if (Platform.isIOS) {
EasyLoading.showToast(StringResource.ERROR_PIP);
}
_isFloatingMode = false;
print('$eventCode');
}
});
_volumeSubscription = SuperPlayerPlugin.instance.onEventBroadcast.listen((event) {
_volumeSubscription =
SuperPlayerPlugin.instance.onEventBroadcast.listen((event) {
int eventCode = event["event"];
if (_isFloatingMode && _isPlaying) {
if (eventCode == TXVodPlayEvent.EVENT_AUDIO_FOCUS_PAUSE) {
......@@ -327,7 +351,7 @@ class SuperPlayerViewState extends State<SuperPlayerView> with WidgetsBindingObs
Widget _getPipEnterView() {
return Visibility(
visible: _isShowControlView && !_isFullScreen && Platform.isAndroid, // PIP 暂时只支持Android
visible: _isShowControlView && !_isFullScreen,
child: Positioned(
right: 10,
top: 0,
......
......@@ -67,6 +67,12 @@ class VideoSliderState extends State<VideoSliderView> {
void updatePorgess(double progress,double totalDuration) {
if(!isSliding) {
setState(() {
if(progress > 1.0) {
progress = 1.0;
}
if(progress < 0) {
progress = 0;
}
_currentProgress = progress;
_videoDuration = totalDuration;
});
......
// Copyright (c) 2022 Tencent. All rights reserved.
#import "TXVodDownloadManager.h"
#import <Foundation/Foundation.h>
#import "FTXEvent.h"
@interface CommonUtil : NSObject
+(int)getCacheVideoQuality:(int)width height:(int)pHeight;
+(int)getDownloadEventByState:(int)downloadState;
@end
// Copyright (c) 2022 Tencent. All rights reserved.
#import "CommonUtil.h"
@implementation CommonUtil
+ (int)getCacheVideoQuality:(int)width height:(int)pHeight{
int minValue = MIN(width, pHeight);
int cacheQualityIndex;
if (minValue == 240 || minValue == 180) {
cacheQualityIndex = TXVodQualityFLU;
} else if (minValue == 480 || minValue == 360) {
cacheQualityIndex = TXVodQualitySD;
} else if (minValue == 540) {
cacheQualityIndex = TXVodQualitySD;
} else if (minValue == 720) {
cacheQualityIndex = TXVodQualityHD;
} else if (minValue == 1080) {
cacheQualityIndex = TXVodQualityFHD;
} else if (minValue == 1440) {
cacheQualityIndex = TXVodQuality2K;
} else if (minValue == 2160) {
cacheQualityIndex = TXVodQuality4K;
} else {
cacheQualityIndex = TXVodQualityFLU;
}
return cacheQualityIndex;
}
+ (int)getDownloadEventByState:(int)downloadState{
int result;
switch (downloadState) {
case TXVodDownloadMediaInfoStateInit:
result = EVENT_DOWNLOAD_START;
break;
case TXVodDownloadMediaInfoStateStart:
result = EVENT_DOWNLOAD_PROGRESS;
break;
case TXVodDownloadMediaInfoStateStop:
result = EVENT_DOWNLOAD_STOP;
break;
case TXVodDownloadMediaInfoStateError:
result = EVENT_DOWNLOAD_ERROR;
break;
case TXVodDownloadMediaInfoStateFinish:
result = EVENT_DOWNLOAD_FINISH;
break;
default:
result = EVENT_DOWNLOAD_ERROR;
break;
}
return result;
}
@end
......@@ -5,9 +5,11 @@
#import "FTXEvent.h"
#import "FTXDownloadManager.h"
#import "TXVodPreloadManager.h"
#import "TXVodDownloadManager.h"
#import "FTXEvent.h"
#import "CommonUtil.h"
@interface FTXDownloadManager ()<FlutterStreamHandler, TXVodPreloadManagerDelegate>
@interface FTXDownloadManager ()<FlutterStreamHandler, TXVodPreloadManagerDelegate, TXVodDownloadDelegate>
@end
......@@ -30,6 +32,11 @@
_eventChannel = [FlutterEventChannel eventChannelWithName:@"cloud.tencent.com/txvodplayer/download/event" binaryMessenger:[registrar messenger]];
[_eventChannel setStreamHandler:self];
NSLog(@"dokie initWithRegistrar");
[[TXVodDownloadManager shareInstance] setDelegate:self];
// 设置下载存储路径
NSString *cachesDir = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) firstObject];
NSString *path = [NSString stringWithFormat:@"%@/videoCache",cachesDir];
[[TXVodDownloadManager shareInstance] setDownloadPath:path];
}
return self;
}
......@@ -61,6 +68,53 @@
int taskId = [args[@"taskId"] intValue];
[[TXVodPreloadManager sharedManager] stopPreload:taskId];
result(nil);
} else if([@"startDownload" isEqualToString:call.method]) {
NSNumber *quality = args[@"quality"];
NSString *videoUrl = args[@"url"];
NSNumber *appIdNum = args[@"appId"];
NSString *fileId = args[@"fileId"];
NSString *pSign = args[@"pSign"];
NSString *userName = args[@"userName"];
if([NSNull null] != (NSNull *)videoUrl) {
[[TXVodDownloadManager shareInstance] startDownload:userName url:videoUrl];
} else if([NSNull null] != (NSNull *)appIdNum && [NSNull null] != (NSNull *)fileId) {
TXVodDownloadDataSource *dataSource = [[TXVodDownloadDataSource alloc] init];
dataSource.appId = [appIdNum intValue];
dataSource.fileId = fileId;
dataSource.userName = userName;
dataSource.quality = [self optQuality:quality];
if([NSNull null] != (NSNull *)pSign) {
dataSource.pSign = pSign;
}
[[TXVodDownloadManager shareInstance] startDownload:dataSource];
}
result(nil);
} else if([@"stopDownload" isEqualToString:call.method]) {
NSNumber *quality = args[@"quality"];
NSString *videoUrl = args[@"url"];
NSNumber *appIdNum = args[@"appId"];
NSString *fileId = args[@"fileId"];
TXVodDownloadMediaInfo *mediaInfo = [self parseMediaInfoFromInfo:quality url:videoUrl appId:appIdNum fileId:fileId];
[[TXVodDownloadManager shareInstance] stopDownload:mediaInfo];
result(nil);
} else if([@"setDownloadHeaders" isEqualToString:call.method]) {
[[TXVodDownloadManager shareInstance] setHeaders:args];
result(nil);
} else if([@"getDownloadList" isEqualToString:call.method]) {
NSArray<TXVodDownloadMediaInfo *> *mediaInfoList = [[TXVodDownloadManager shareInstance] getDownloadMediaInfoList];
NSMutableArray<NSDictionary *> *resultDicArray = [[NSMutableArray alloc] init];
for (int i = 0; i < mediaInfoList.count; i++) {
[resultDicArray addObject:[self buildMapFromDownloadMediaInfo:mediaInfoList[i]]];
}
result(resultDicArray);
} else if([@"getDownloadInfo" isEqualToString:call.method]) {
NSNumber *quality = args[@"quality"];
NSString *videoUrl = args[@"url"];
NSNumber *appIdNum = args[@"appId"];
NSString *fileId = args[@"fileId"];
TXVodDownloadMediaInfo *mediaInfo = [self parseMediaInfoFromInfo:quality url:videoUrl appId:appIdNum fileId:fileId];
NSDictionary *resultDic = [self buildMapFromDownloadMediaInfo:mediaInfo];
result(resultDic);
}
}
......@@ -120,4 +174,94 @@
[_eventSink success:[FTXDownloadManager getParamsWithEvent:EVENT_PREDOWNLOAD_ON_ERROR withParams:dict]];
}
#pragma mark - TXDownloadManager
- (int)optQuality:(NSNumber *)quality {
return ([NSNull null] == (NSNull *)quality || nil == quality) ? TXVodQualityFLU : [quality intValue];
}
- (TXVodDownloadMediaInfo *)parseMediaInfoFromInfo:(NSNumber *)quality url:(NSString *)videoUrl appId:(NSNumber *)pAppId fileId:(NSString *)pFileId {
TXVodDownloadMediaInfo *mediaInfo = nil;
if(nil != videoUrl && [NSNull null] != (NSNull *)videoUrl) {
TXVodDownloadMediaInfo *urlInfo = [[TXVodDownloadMediaInfo alloc] init];
urlInfo.url = videoUrl;
mediaInfo = [[TXVodDownloadManager shareInstance] getDownloadMediaInfo:urlInfo];
} else if([NSNull null] != (NSNull *)pFileId && [NSNull null] != (NSNull *)pAppId) {
TXVodDownloadMediaInfo *fileIdInfo = [[TXVodDownloadMediaInfo alloc] init];
TXVodDownloadDataSource *dataSource = [[TXVodDownloadDataSource alloc] init];
dataSource.appId = [pAppId intValue];
dataSource.fileId = pFileId;;
dataSource.quality = [self optQuality:quality];
fileIdInfo.dataSource = dataSource;
mediaInfo = [[TXVodDownloadManager shareInstance] getDownloadMediaInfo:fileIdInfo];
}
return mediaInfo;
}
- (NSMutableDictionary *)buildMapFromDownloadMediaInfo:(TXVodDownloadMediaInfo *)info{
NSMutableDictionary *dict = [[NSMutableDictionary alloc] init];
if(nil != info && [NSNull null] != (NSNull *)info) {
[dict setValue:info.playPath forKey:@"playPath"];
[dict setValue:@(info.progress) forKey:@"progress"];
[dict setValue:@([CommonUtil getDownloadEventByState:(int)info.downloadState]) forKey:@"downloadState"];
[dict setValue:info.userName forKey:@"userName"];
[dict setValue:@(info.duration) forKey:@"duration"];
[dict setValue:@(info.playableDuration) forKey:@"playableDuration"];
[dict setValue:@(info.size) forKey:@"size"];
[dict setValue:@(info.downloadSize) forKey:@"downloadSize"];
if([NSNull null] != (NSNull *)info.url && info.url.length > 0) {
[dict setValue:info.url forKey:@"url"];
}
if(nil != info.dataSource && [NSNull null] != (NSNull *)info.dataSource) {
TXVodDownloadDataSource *dataSource = info.dataSource;
[dict setValue:@(dataSource.appId) forKey:@"appId"];
[dict setValue:dataSource.fileId forKey:@"fileId"];
[dict setValue:dataSource.pSign forKey:@"pSign"];
[dict setValue:@(dataSource.quality) forKey:@"quality"];
[dict setValue:dataSource.token forKey:@"token"];
}
}
return dict;
}
/// 下载开始
- (void)onDownloadStart:(TXVodDownloadMediaInfo *)mediaInfo {
[_eventSink success:[FTXDownloadManager getParamsWithEvent:EVENT_DOWNLOAD_START withParams:[self buildMapFromDownloadMediaInfo:mediaInfo]]];
}
/// 下载进度
- (void)onDownloadProgress:(TXVodDownloadMediaInfo *)mediaInfo {
[_eventSink success:[FTXDownloadManager getParamsWithEvent:EVENT_DOWNLOAD_PROGRESS withParams:[self buildMapFromDownloadMediaInfo:mediaInfo]]];
}
/// 下载停止
- (void)onDownloadStop:(TXVodDownloadMediaInfo *)mediaInfo {
[_eventSink success:[FTXDownloadManager getParamsWithEvent:EVENT_DOWNLOAD_STOP withParams:[self buildMapFromDownloadMediaInfo:mediaInfo]]];
}
/// 下载完成
- (void)onDownloadFinish:(TXVodDownloadMediaInfo *)mediaInfo {
[_eventSink success:[FTXDownloadManager getParamsWithEvent:EVENT_DOWNLOAD_FINISH withParams:[self buildMapFromDownloadMediaInfo:mediaInfo]]];
}
/// 下载错误
- (void)onDownloadError:(TXVodDownloadMediaInfo *)mediaInfo errorCode:(TXDownloadError)code errorMsg:(NSString *)msg {
NSMutableDictionary *dict = [self buildMapFromDownloadMediaInfo:mediaInfo];
[dict setValue:@(code) forKey:@"errorCode"];
[dict setValue:msg forKey:@"errorMsg"];
[_eventSink success:[FTXDownloadManager getParamsWithEvent:EVENT_DOWNLOAD_ERROR withParams:dict]];
}
/**
* 下载HLS,遇到加密的文件,将解密key给外部校验,ijk遗留,暂时弃用
* @param mediaInfo 下载对象
* @param url Url地址
* @param data 服务器返回
* @return 0:校验正确,继续下载;否则校验失败,抛出下载错误(SDK 获取失败)
*/
- (int)hlsKeyVerify:(TXVodDownloadMediaInfo *)mediaInfo url:(NSString *)url data:(NSData *)data {
return 0;
}
@end
......@@ -3,12 +3,45 @@
#define FTXEvent_h
// 音频变化事件code
#define EVENT_VOLUME_CHANGED 0x01
#define EVENT_AUDIO_FOCUS_PAUSE 0x02
#define EVENT_AUDIO_FOCUS_PLAY 0x03
#define EVENT_VOLUME_CHANGED 1
#define EVENT_AUDIO_FOCUS_PAUSE 2
#define EVENT_AUDIO_FOCUS_PLAY 3
// 画中画事件code
#define EVENT_PIP_MODE_ALREADY_ENTER 1
#define EVENT_PIP_MODE_ALREADY_EXIT 2
#define EVENT_PIP_MODE_REQUEST_START 3
#define EVENT_PIP_MODE_UI_STATE_CHANGED 4
#define EVENT_PIP_MODE_RESTORE_UI 5
#define EVENT_PIP_MODE_WILL_EXIT 6
// 画中画错误事件code
#define NO_ERROR 0 ///< 无错误
#define ERROR_IOS_PIP_DEVICE_NOT_SUPPORT -104 ///< 设备或系统版本不支持(iPad iOS9+ 才支持PIP)
#define ERROR_IOS_PIP_PLAYER_NOT_SUPPORT -105 ///< 播放器不支持
#define ERROR_IOS_PIP_VIDEO_NOT_SUPPORT -106 ///< 视频不支持
#define ERROR_IOS_PIP_IS_NOT_POSSIBLE -107 ///< PIP控制器不可用
#define ERROR_IOS_PIP_FROM_SYSTEM -108 ///< PIP控制器报错
#define ERROR_IOS_PIP_PLAYER_NOT_EXIST -109 ///< 播放器对象不存在
#define ERROR_IOS_PIP_IS_RUNNING -110 ///< PIP功能已经运行
#define ERROR_IOS_PIP_NOT_RUNNING -111 ///< PIP功能没有启动
// 视频预下载完成
#define EVENT_PREDOWNLOAD_ON_COMPLETE 200
// 视频预下载出错
#define EVENT_PREDOWNLOAD_ON_ERROR 201
// 视频下载开始
#define EVENT_DOWNLOAD_START 301
// 视频下载进度
#define EVENT_DOWNLOAD_PROGRESS 302
// 视频下载停止
#define EVENT_DOWNLOAD_STOP 303
// 视频下载完成
#define EVENT_DOWNLOAD_FINISH 304
// 视频下载错误
#define EVENT_DOWNLOAD_ERROR 305
#endif /* FTXEvent_h */
......@@ -7,8 +7,26 @@
NS_ASSUME_NONNULL_BEGIN
@protocol FTXVodPlayerDelegate <NSObject>
- (void)onPlayerPipRequestStart;
- (void)onPlayerPipStateDidStart;
- (void)onPlayerPipStateWillStop;
- (void)onPlayerPipStateDidStop;
- (void)onPlayerPipStateRestoreUI;
- (void)onPlayerPipStateError:(NSInteger)errorId;
@end
@interface FTXVodPlayer : FTXBasePlayer
@property (nonatomic, weak) id<FTXVodPlayerDelegate> delegate;
- (instancetype)initWithRegistrar:(id<FlutterPluginRegistrar>)registrar;
@end
......
......@@ -9,7 +9,7 @@
#import <TXLiteAVSDK_Player/TXLiteAVSDK.h>
#import "FTXDownloadManager.h"
@interface SuperPlayerPlugin ()<FlutterStreamHandler>
@interface SuperPlayerPlugin ()<FlutterStreamHandler,FTXVodPlayerDelegate>
@property (nonatomic, strong) NSObject<FlutterPluginRegistrar>* registrar;
@property (nonatomic, strong) NSMutableDictionary *players;
......@@ -19,7 +19,9 @@
@implementation SuperPlayerPlugin {
float orginBrightness;
FlutterEventChannel *_eventChannel;
FlutterEventChannel *_pipEventChannel;
FTXPlayerEventSinkQueue *_eventSink;
FTXPlayerEventSinkQueue *_pipEventSink;
FTXAudioManager *audioManager;
FTXDownloadManager *_FTXDownloadManager;
}
......@@ -56,8 +58,11 @@ SuperPlayerPlugin* instance;
audioManager = [[FTXAudioManager alloc] init];
// volume event stream
_eventSink = [FTXPlayerEventSinkQueue new];
_pipEventSink = [FTXPlayerEventSinkQueue new];
_eventChannel = [FlutterEventChannel eventChannelWithName:@"cloud.tencent.com/playerPlugin/event" binaryMessenger:[registrar messenger]];
_pipEventChannel = [FlutterEventChannel eventChannelWithName:@"cloud.tencent.com/playerPlugin/pipEvent" binaryMessenger:[registrar messenger]];
[_eventChannel setStreamHandler:self];
[_pipEventChannel setStreamHandler:self];
[audioManager registerVolumeChangeListener:self selector:@selector(systemVolumeDidChangeNoti:) name:@"AVSystemController_SystemVolumeDidChangeNotification" object:nil];
_FTXDownloadManager = [[FTXDownloadManager alloc] initWithRegistrar:registrar];
......@@ -78,6 +83,7 @@ SuperPlayerPlugin* instance;
result(playerId);
}else if([@"createVodPlayer" isEqualToString:call.method]){
FTXVodPlayer* player = [[FTXVodPlayer alloc] initWithRegistrar:self.registrar];
player.delegate = self;
NSNumber *playerId = player.playerId;
_players[playerId] = player;
result(playerId);
......@@ -166,8 +172,11 @@ SuperPlayerPlugin* instance;
int logLevel = [args[@"logLevel"] intValue];
[TXLiveBase setLogLevel:logLevel];
result(nil);
} else if([@"getLiteAVSDKVersion" isEqualToString:call.method]) {
} else if([@"getLiteAVSDKVersion" isEqualToString:call.method]) {
result([TXLiveBase getSDKVersionStr]);
} else if ([@"isDeviceSupportPip" isEqualToString:call.method]) {
BOOL isSupport = [TXVodPlayer isSupportPictureInPicture];
result([NSNumber numberWithBool:isSupport]);
} else {
result(FlutterMethodNotImplemented);
}
......@@ -194,6 +203,7 @@ SuperPlayerPlugin* instance;
if ([arguments isKindOfClass:NSString.class]) {
if ([arguments isEqualToString:@"event"]) {
[_eventSink setDelegate:events];
[_pipEventSink setDelegate:events];
}
}
......@@ -205,10 +215,36 @@ SuperPlayerPlugin* instance;
if ([arguments isKindOfClass:NSString.class]) {
if ([arguments isEqualToString:@"event"]) {
[_eventSink setDelegate:nil];
[_pipEventSink setDelegate:nil];
}
}
return nil;
}
#pragma mark - FTXVodPlayerDelegate
- (void)onPlayerPipRequestStart {
[_pipEventSink success:@{@"event" : @(EVENT_PIP_MODE_REQUEST_START)}];
}
- (void)onPlayerPipStateDidStart {
[_pipEventSink success:@{@"event" : @(EVENT_PIP_MODE_ALREADY_ENTER)}];
}
- (void)onPlayerPipStateWillStop {
[_pipEventSink success:@{@"event" : @(EVENT_PIP_MODE_WILL_EXIT)}];
}
- (void)onPlayerPipStateDidStop {
[_pipEventSink success:@{@"event" : @(EVENT_PIP_MODE_ALREADY_EXIT)}];
}
- (void)onPlayerPipStateError:(NSInteger)errorId {
[_pipEventSink success:@{@"event" : @(errorId)}];
}
- (void)onPlayerPipStateRestoreUI {
[_pipEventSink success:@{@"event" : @(EVENT_PIP_MODE_RESTORE_UI)}];
}
@end
......@@ -4,7 +4,7 @@
#
Pod::Spec.new do |s|
s.name = 'super_player'
s.version = '1.0.2'
s.version = '1.0.3'
s.summary = 'player plugin.'
s.description = <<-DESC
player plugin.
......@@ -20,6 +20,7 @@ player plugin.
s.ios.framework = ['MobileCoreServices']
s.platform = :ios, '9.0'
s.static_framework = true
s.resources = ['Classes/TXResource/**/*']
# Flutter.framework does not contain a i386 slice.
s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES', 'EXCLUDED_ARCHS[sdk=iphonesimulator*]' => 'i386' }
......
// Copyright (c) 2022 Tencent. All rights reserved.
part of SuperPlayer;
/// 通用工具类
class CommonUtils {
/// 通过分辨率获取下载对应的qualityId
static int getDownloadQualityBySize(int width, int height) {
if (width == null || height == null) {
return DownloadQuality.QUALITY_FLU;
}
int minValue = min(width, height);
int cacheQualityIndex;
if (minValue == 240 || minValue == 180) {
cacheQualityIndex = DownloadQuality.QUALITY_FLU;
} else if (minValue == 480 || minValue == 360) {
cacheQualityIndex = DownloadQuality.QUALITY_SD;
} else if (minValue == 540) {
cacheQualityIndex = DownloadQuality.QUALITY_SD;
} else if (minValue == 720) {
cacheQualityIndex = DownloadQuality.QUALITY_HD;
} else if (minValue == 1080) {
cacheQualityIndex = DownloadQuality.QUALITY_FHD;
} else if (minValue == 1440) {
cacheQualityIndex = DownloadQuality.QUALITY_2K;
} else if (minValue == 2160) {
cacheQualityIndex = DownloadQuality.QUALITY_4K;
} else {
cacheQualityIndex = DownloadQuality.QUALITY_UNK;
}
return cacheQualityIndex;
}
}
......@@ -81,9 +81,9 @@ 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"; //播放速率
static const String EVT_IMAGESPRIT_WEBVTTURL = "EVT_IMAGESPRIT_WEBVTTURL"; // 雪碧图web vtt描述文件下载URL
static const String EVT_IMAGESPRIT_IMAGEURL_LIST = "EVT_IMAGESPRIT_IMAGEURL_LIST"; // 雪碧图图片下载URL
static const String EVT_DRM_TYPE = "EVT_DRM_TYPE"; // 加密类型
static const EVT_IMAGESPRIT_WEBVTTURL = "EVT_IMAGESPRIT_WEBVTTURL"; // 雪碧图web vtt描述文件下载URL
static const EVT_IMAGESPRIT_IMAGEURL_LIST = "EVT_IMAGESPRIT_IMAGEURL_LIST"; // 雪碧图图片下载URL
static const EVT_DRM_TYPE = "EVT_DRM_TYPE"; // 加密类型
/// superplayer plugin volume event
static const EVENT_VOLUME_CHANGED = 1; // 音量变化
......@@ -94,15 +94,33 @@ abstract class TXVodPlayEvent {
static const EVENT_PIP_MODE_ALREADY_EXIT = 2; // 已经退出画中画模式
static const EVENT_PIP_MODE_REQUEST_START = 3; // 开始请求进入画中画模式
static const EVENT_PIP_MODE_UI_STATE_CHANGED = 4; // pip UI状态发生变动,only support android > 31
static const EVENT_IOS_PIP_MODE_RESTORE_UI = 5; // 重置UI only support iOS
static const EVENT_IOS_PIP_MODE_WILL_EXIT = 6; // 将要退出画中画 only support iOS
static const NO_ERROR = 0;
static const ERROR_PIP_LOWER_VERSION = -101; // pip 错误,android版本过低
static const ERROR_PIP_DENIED_PERMISSION = -102; // pip 错误,画中画权限关闭/设备不支持画中画
static const ERROR_PIP_ACTIVITY_DESTROYED = -103; // pip 错误,当前界面已销毁
static const ERROR_PIP_LOWER_VERSION = -101; // pip 错误,android版本过低
static const ERROR_PIP_DENIED_PERMISSION = -102; // pip 错误,画中画权限关闭/设备不支持画中画
static const ERROR_PIP_ACTIVITY_DESTROYED = -103; // pip 错误,当前界面已销毁
static const ERROR_IOS_PIP_DEVICE_NOT_SUPPORT = -104; // pip 错误,设备或系统版本不支持(iPad iOS9+ 才支持PIP)
static const ERROR_IOS_PIP_PLAYER_NOT_SUPPORT = -105; // pip 错误,播放器不支持 only support iOS
static const ERROR_IOS_PIP_VIDEO_NOT_SUPPORT = -106; // pip 错误,视频不支持 only support iOS
static const ERROR_IOS_PIP_IS_NOT_POSSIBLE = -107; // pip 错误,PIP控制器不可用 only support iOS
static const ERROR_IOS_PIP_FROM_SYSTEM = -108; // pip 错误,PIP控制器报错 only support iOS
static const ERROR_IOS_PIP_PLAYER_NOT_EXIST = -109; // pip 错误,播放器对象不存在 only support iOS
static const ERROR_IOS_PIP_IS_RUNNING = -110; // pip 错误,PIP功能已经运行 only support iOS
static const ERROR_IOS_PIP_NOT_RUNNING = -111; // pip 错误,PIP功能没有启动 only support iOS
/// 视频下载相关事件
static const int EVENT_PREDOWNLOAD_ON_COMPLETE = 200; // 视频预下载完成
static const int EVENT_PREDOWNLOAD_ON_ERROR = 201; // 视频预下载出错
static const EVENT_PREDOWNLOAD_ON_COMPLETE = 200; // 视频预下载完成
static const EVENT_PREDOWNLOAD_ON_ERROR = 201; // 视频预下载出错
static const EVENT_DOWNLOAD_START = 301; // 视频下载开始
static const EVENT_DOWNLOAD_PROGRESS = 302; // 视频下载进度
static const EVENT_DOWNLOAD_STOP = 303; // 视频下载停止
static const EVENT_DOWNLOAD_FINISH = 304; // 视频下载完成
static const EVENT_DOWNLOAD_ERROR = 305; // 视频下载错误
}
abstract class TXVodNetEvent {
......@@ -159,13 +177,24 @@ enum TXPlayerEvent {
}
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
static const LOG_LEVEL_VERBOSE = 0; // 输出所有级别的log
static const LOG_LEVEL_DEBUG = 1; // 输出 DEBUG,INFO,WARNING,ERROR 和 FATAL 级别的log
static const LOG_LEVEL_INFO = 2; // 输出 INFO,WARNNING,ERROR 和 FATAL 级别的log
static const LOG_LEVEL_WARN = 3; // 输出WARNNING,ERROR 和 FATAL 级别的log
static const LOG_LEVEL_ERROR = 4; // 输出ERROR 和 FATAL 级别的log
static const LOG_LEVEL_FATAL = 5; // 只输出FATAL 级别的log
static const LOG_LEVEL_NULL = 6; // 不输出任何sdk log
}
class DownloadQuality {
static const QUALITY_OD = 0;
static const QUALITY_FLU = 1;
static const QUALITY_SD = 2;
static const QUALITY_HD = 3;
static const QUALITY_FHD = 4;
static const QUALITY_2K = 5;
static const QUALITY_4K = 6;
static const QUALITY_UNK = 1000;
}
class TXPlayInfoParams {
......@@ -184,7 +213,82 @@ class TXPlayInfoParams {
}
}
/// fileId存储
class TXVodDownloadDataSource {
/// 下载文件对应的appId,fileId下载必填
int? appId;
/// 下载文件Id,fileId下载必填
String? fileId;
/// 加密签名,加密视频必填
String? pSign;
/// 清晰度ID,fileId下载必传,通过[CommonUtils.getDownloadQualityBySize]进行转换
int? quality;
/// 加密token
String? token;
/// 账户名称,用于url下载设置账户名称
String? userName;
Map<String, dynamic> toJson() {
Map<String, dynamic> json = {};
json["appId"] = appId;
json["fileId"] = fileId;
json["pSign"] = pSign;
json["quality"] = quality;
json["token"] = token;
json["userName"] = userName;
return json;
}
}
/// 视频下载信息
class TXVodDownloadMedialnfo {
/// 缓存地址
String? playPath;
/// 下载进度
double? progress;
/// 下载状态
int? downloadState;
/// 账户名称,用于url下载设置账户名称
String? userName;
/// 总时长
int? duration;
/// 已下载的可播放时长
int? playableDuration;
/// 文件总大小,单位:byte
int? size;
/// 已下载大小,单位:byte
int? downloadSize;
/// 需要下载的视频url,url下载必填
/// <h1>
/// url下载不支持不支持嵌套m3u8和mp4下载
/// </h1>
String? url;
/// fileId 存储
TXVodDownloadDataSource? dataSource;
Map<String, dynamic> toJson() {
Map<String, dynamic> json = {};
json["url"] = url;
json["downloadState"] = downloadState;
json["progress"] = progress;
json["playPath"] = playPath;
json["userName"] = userName;
json["duration"] = duration;
json["playableDuration"] = playableDuration;
json["size"] = size;
json["downloadSize"] = downloadSize;
if(null != dataSource) {
json.addAll(dataSource!.toJson());
}
return json;
}
}
//视频预下载事件回调Listener
typedef FTXPredownlodOnCompleteListener = void Function(int taskId, String url);
typedef FTXPredownlodOnErrorListener = void Function(int taskId, String url, int code, String msg);
// 视频下载时间回调Listener
typedef FTXDownlodOnStateChangeListener = void Function(int event, TXVodDownloadMedialnfo info);
typedef FTXDownlodOnErrorListener = void Function(int errorCode, String errorMsg,TXVodDownloadMedialnfo info);
......@@ -4,34 +4,38 @@ part of SuperPlayer;
/// include features:
/// 1. Video predownlaod
/// 2. Video download
class TXVodDownlaodController {
class TXVodDownloadController {
static const String TAG = 'TXVodDownlaodController';
static TXVodDownlaodController? _instance;
static TXVodDownlaodController get instance => _sharedInstance();
static TXVodDownloadController? _instance;
static TXVodDownloadController get instance => _sharedInstance();
StreamSubscription? _downloadEventSubscription;
final StreamController<Map<dynamic, dynamic>> _downloadEventStreamController = StreamController.broadcast();
Stream<Map<dynamic, dynamic>> get onDownlaodEventBroadcast => _downloadEventStreamController.stream;
Stream<Map<dynamic, dynamic>> get onDownloadEventBroadcast => _downloadEventStreamController.stream;
late MethodChannel _methodChannel;
FTXPredownlodOnCompleteListener? _onPreDownloadOnCompleteListener;
FTXPredownlodOnErrorListener? _onPreDownloadOnErrorListener;
static TXVodDownlaodController _sharedInstance() {
FTXDownlodOnStateChangeListener? _downlodOnStateChangeListener;
FTXDownlodOnErrorListener? _downlodOnErrorListener;
static TXVodDownloadController _sharedInstance() {
if (_instance == null) {
_instance = TXVodDownlaodController._createInsatnce();
_instance = TXVodDownloadController._createInsatnce();
}
return _instance!;
}
TXVodDownlaodController._createInsatnce() {
TXVodDownloadController._createInsatnce() {
EventChannel eventChannel = EventChannel("cloud.tencent.com/txvodplayer/download/event");
_downloadEventSubscription = eventChannel.receiveBroadcastStream('event')
.listen(_eventHandler, onError: _errorHandler);
_downloadEventSubscription =
eventChannel.receiveBroadcastStream('event').listen(_eventHandler, onError: _errorHandler);
_methodChannel = MethodChannel("cloud.tencent.com/txvodplayer/download/api");
}
/// 启动预下载。
/// 【重要】启动预下载前,请先设置好播放引擎的缓存目录[SuperPlayerPlugin.setGlobalCacheFolderPath]和缓存大小[SuperPlayerPlugin.setGlobalMaxCacheSize],这个设置是全局配置需和播放器保持一致,否则会造成播放缓存失效。
/// playUrl: 要预下载的url
......@@ -40,21 +44,19 @@ class TXVodDownlaodController {
/// onCompleteListener:预下载成功回调
/// onErrorListener:预下载失败回调
/// 返回值:任务ID,可用这个任务ID停止预下载 [stopPreload]
Future<int> startPreLoad(final String playUrl,
final int preloadSizeMB,
final int preferredResolution,
{ FTXPredownlodOnCompleteListener? onCompleteListener,
FTXPredownlodOnErrorListener? onErrorListener,
}) async {
Future<int> startPreLoad(
final String playUrl,
final int preloadSizeMB,
final int preferredResolution, {
FTXPredownlodOnCompleteListener? onCompleteListener,
FTXPredownlodOnErrorListener? onErrorListener,
}) async {
_onPreDownloadOnCompleteListener = onCompleteListener;
_onPreDownloadOnErrorListener = onErrorListener;
var map = {"playUrl": playUrl,
"preloadSizeMB": preloadSizeMB,
"preferredResolution": preferredResolution };
var map = {"playUrl": playUrl, "preloadSizeMB": preloadSizeMB, "preferredResolution": preferredResolution};
return await _methodChannel.invokeMethod("startPreLoad", map);
}
/// 停止预下载。
/// taskId: 任务id,[startPreLoad]返回值
Future<void> stopPreLoad(final int taskId) async {
......@@ -62,6 +64,72 @@ class TXVodDownlaodController {
await _methodChannel.invokeMethod("stopPreLoad", map);
}
/// 开始下载
/// videoDownloadModel: 下载构造体 [TXVodDownloadMedialnfo]
/// userName: 下载用户,用来区分不同用户的下载,可不传,不传则使用默认值
Future<void> startDonwload(TXVodDownloadMedialnfo medialnfo) async {
await _methodChannel.invokeMethod("startDownload", medialnfo.toJson());
}
/// 停止下载
/// videoDownloadModel: 下载构造体 [TXVodDownloadMedialnfo]
Future<void> stopDownload(TXVodDownloadMedialnfo medialnfo) async {
await _methodChannel.invokeMethod("stopDownload", medialnfo.toJson());
}
/// 设置下载请求头
Future<void> setDownloadHeaders(Map<String, String> headers) async {
await _methodChannel.invokeMethod("setDownloadHeaders", {"headers": headers});
}
/// 获取所有视频下载列表
/// return [TXVodDownloadMedialnfo]
Future<List<TXVodDownloadMedialnfo>> getDownloadList() async {
List<TXVodDownloadMedialnfo> outputList = [];
List<dynamic> donwloadOrgList = await _methodChannel.invokeMethod("getDownloadList");
for (dynamic data in donwloadOrgList) {
outputList.add(_getDownloadInfoFromMap(data));
}
return outputList;
}
/// 获得指定视频的下载信息
/// return [TXVodDownloadMedialnfo]
Future<TXVodDownloadMedialnfo> getDownloadInfo(TXVodDownloadMedialnfo medialnfo) async {
Map<dynamic, dynamic> data = await _methodChannel.invokeMethod("getDownloadInfo", medialnfo.toJson());
return _getDownloadInfoFromMap(data);
}
/// 设置下载事件监听,该监听为全局下载监听配置,重复调用,listener会先后覆盖
void setDownloadObserver(
FTXDownlodOnStateChangeListener downlodOnStateChangeListener, FTXDownlodOnErrorListener downlodOnErrorListener) {
_downlodOnStateChangeListener = downlodOnStateChangeListener;
_downlodOnErrorListener = downlodOnErrorListener;
}
TXVodDownloadMedialnfo _getDownloadInfoFromMap(Map<dynamic, dynamic> map) {
TXVodDownloadMedialnfo medialnfo = TXVodDownloadMedialnfo();
medialnfo.playPath = map["playPath"];
medialnfo.progress = map["progress"];
medialnfo.downloadState = map["downloadState"];
medialnfo.userName = map["userName"];
medialnfo.duration = map["duration"];
medialnfo.playableDuration = map["playableDuration"];
medialnfo.size = map["size"];
medialnfo.downloadSize = map["downloadSize"];
medialnfo.url = map["url"];
if(map.keys.contains("appId")) {
TXVodDownloadDataSource dataSource = TXVodDownloadDataSource();
dataSource.appId = map["appId"];
dataSource.fileId = map["fileId"];
dataSource.pSign = map["pSign"];
dataSource.token = map["token"];
dataSource.userName = map["userName"];
dataSource.quality = map["quality"];
medialnfo.dataSource = dataSource;
}
return medialnfo;
}
_eventHandler(event) {
if (null == event) {
......@@ -69,7 +137,8 @@ class TXVodDownlaodController {
}
LogUtils.d(TAG, '_eventHandler, event= ${event}');
final Map<dynamic, dynamic> map = event;
switch (map["event"]) {
int eventCode = map["event"];
switch (eventCode) {
case TXVodPlayEvent.EVENT_PREDOWNLOAD_ON_COMPLETE:
int taskId = map['taskId'] as int;
String url = map['url'] as String;
......@@ -88,13 +157,23 @@ class TXVodDownlaodController {
_onPreDownloadOnErrorListener!(taskId, url, code, msg);
}
break;
case TXVodPlayEvent.EVENT_DOWNLOAD_START:
case TXVodPlayEvent.EVENT_DOWNLOAD_PROGRESS:
case TXVodPlayEvent.EVENT_DOWNLOAD_STOP:
case TXVodPlayEvent.EVENT_DOWNLOAD_FINISH:
_downlodOnStateChangeListener?.call(eventCode, _getDownloadInfoFromMap(map));
break;
case TXVodPlayEvent.EVENT_DOWNLOAD_ERROR:
TXVodDownloadMedialnfo info = _getDownloadInfoFromMap(map);
int errorCode = map["errorCode"];
String errorMsg = map["errorMsg"];
_downlodOnErrorListener?.call(errorCode, errorMsg, info);
break;
default:
break;
}
_downloadEventStreamController.add(event);
}
_errorHandler(error) {
}
}
\ No newline at end of file
_errorHandler(error) {}
}
......@@ -3,6 +3,7 @@ library SuperPlayer;
import 'dart:async';
import 'dart:core';
import 'dart:math';
import 'package:flutter/cupertino.dart';
import 'package:flutter/foundation.dart';
......@@ -17,4 +18,5 @@ part 'Core/txplayer_define.dart';
part 'Core/txplayer_widget.dart';
part 'Core/txvodplayer_config.dart';
part 'Core/txvodplayer_controller.dart';
part 'Core/txvoddownload_controller.dart';
\ No newline at end of file
part 'Core/txvoddownload_controller.dart';
part 'Core/tools/common_utils.dart';
\ No newline at end of file
name: super_player
description: player plugin.
version: 1.0.2
version: 1.0.3
author:
homepage:
......
......@@ -27,7 +27,7 @@ void main() {
await SuperPlayerPlugin.setGlobalMaxCacheSize(200);
String _url =
"http://1400329073.vod2.myqcloud.com/d62d88a7vodtranscq1400329073/59c68fe75285890800381567412/adp.10.m3u8";
int taskId = await TXVodDownlaodController.instance.startPreLoad(_url, 20, 720*1080,
int taskId = await TXVodDownloadController.instance.startPreLoad(_url, 20, 720*1080,
onCompleteListener:(int taskId,String url) {
print('taskID=${taskId} ,url=${url}');
}, onErrorListener: (int taskId, String url, int code, String msg) {
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论