提交 3cdf0ab2 authored 作者: kongdywang's avatar kongdywang

fix bug

上级 a5db668c
......@@ -172,6 +172,7 @@ class _TestState extends State<Test> {
Future<void> initPlayer() async {
await _controller.initialize();
await _controller.setConfig(FTXVodPlayConfig());
await _controller.startPlay(_url);
}
......@@ -320,6 +321,117 @@ class _DemoSuperplayerState extends State<DemoSuperplayer> {
}
```
## 视频下载能力的使用
### 预下载
视频预下载能力依赖于`TXVodDownloadController`,使用其可对视频进行预下载和监听
**接口**
- 预下载视频
```dart
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) {
print('taskID=${taskId} ,url=${url}, code=${code} , msg=${msg}');
} );
```
- 停止预下载
```dart
TXVodDownloadController.instance.stopPreLoad(taskId);
```
taskId从启动预下载的接口获得
### 视频下载
视频下载能力依赖于`TXVodDownloadController`,使用其可对视频进行下载和监听
**接口**
- 下载视频
```dart
TXVodDownloadMedialnfo downloadMedialnfo = TXVodDownloadMedialnfo();
TXVodDownloadDataSource dataSource = TXVodDownloadDataSource();
dataSource.appId = appId;
dataSource.fileId = fileId;
dataSource.pSign = pSign;
downloadMedialnfo.dataSource = dataSource;
TXVodDownloadController.instance.startDonwload(downloadMedialnfo);
```
也可以使用url下载。
```dart
TXVodDownloadMedialnfo downloadMedialnfo = TXVodDownloadMedialnfo();
downloadMedialnfo.url = videoUrl;
TXVodDownloadController.instance.startDonwload(downloadMedialnfo);
```
视频url下载不支持嵌套m3u8和mp4下载。
下载也可以指定username,用来区分不同用户的下载,不传递的话,默认为default
```dart
downloadMedialnfo.userName = username;
```
- 停止下载
```dart
TXVodDownloadController.instance.stopDownload(downloadMedialnfo);
```
- 设置下载请求头
针对部分视频下载的时候,需要设置额外的请求头
```dart
TXVodDownloadController.instance.setDownloadHeaders(headers);
```
- 获得视频的下载信息
该接口可以获得下载中或者已经下载视频的下载信息,可以获得视频的当前缓存地址
```dart
TXVodDownloadController.instance.getDownloadInfo(downloadMedialnfo);
```
- 获得所有视频的下载信息
```dart
TXVodDownloadController.instance.getDownloadList();
```
- 设置视频下载监听
该接口设置的视频下载监听为全局监听,所有视频的下载进度都会在该方法中回调,重复调用的话会前后覆盖
```dart
TXVodDownloadController.instance.setDownloadObserver((event, info) {
// donwload state $event ,donwload info $info
}, (errorCode, errorMsg, info) {
// donwload error code $errorCode,error msg $errorMsg
});
```
- 视频下载事件
| 参数名 | 值 | 描述 |
| ------ | ------ | ------------------ |
| NO_ERROR | 301 | 视频下载开始 |
| EVENT_DOWNLOAD_PROGRESS | 302 | 视频下载中,进度回调 |
| EVENT_DOWNLOAD_STOP | 303 | 视频下载停止 |
| EVENT_DOWNLOAD_FINISH | 304 | 视频下载完成 |
| EVENT_DOWNLOAD_ERROR | 305 | 视频下载错误 |
## 深度定制开发指引
腾讯云播放器SDK Flutter插件对原生播放器能力进行了封装, 如果您要进行深度定制开发,建议采用如下方法:
......
// 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 事件
......
......@@ -2,6 +2,7 @@
package com.tencent.vod.flutter;
import android.app.Activity;
import android.app.AppOpsManager;
import android.app.PendingIntent;
import android.app.PictureInPictureParams;
import android.app.RemoteAction;
......@@ -110,7 +111,10 @@ public class FTXPIPManager {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
params.mPipParams = new PictureInPictureParams.Builder();
updatePipActions(isPlaying, params);
mActivity.enterPictureInPictureMode(params.mPipParams.build());
boolean enterResult = mActivity.enterPictureInPictureMode(params.mPipParams.build());
if(!enterResult) {
pipResult = FTXEvent.ERROR_PIP_DENIED_PERMISSION;
}
} else {
mActivity.enterPictureInPictureMode();
}
......@@ -129,6 +133,9 @@ public class FTXPIPManager {
if (!isSuccess) {
pipResult = FTXEvent.ERROR_PIP_DENIED_PERMISSION;
Log.e(TAG, "enterPip failed,because PIP feature is disabled");
} else if(!hasPipPermission()) {
pipResult = FTXEvent.ERROR_PIP_DENIED_PERMISSION;
Log.e(TAG, "enterPip failed,because PIP has no permission");
}
} else {
pipResult = FTXEvent.ERROR_PIP_LOWER_VERSION;
......@@ -142,6 +149,17 @@ public class FTXPIPManager {
return pipResult;
}
private boolean hasPipPermission() {
AppOpsManager appOpsManager = (AppOpsManager) mActivity.getSystemService(Context.APP_OPS_SERVICE);
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
int permissionResult = appOpsManager.checkOpNoThrow(AppOpsManager.OPSTR_PICTURE_IN_PICTURE,
android.os.Process.myUid(),mActivity.getPackageName());
return permissionResult == AppOpsManager.MODE_ALLOWED;
} else {
return false;
}
}
public boolean isInPipMode() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
return mActivity.isInPictureInPictureMode();
......
<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"
......
......@@ -122,10 +122,11 @@ public class FTXFlutterPipActivity extends FlutterActivity {
@Override
public boolean enterPictureInPictureMode(@NonNull PictureInPictureParams params) {
if (null != mEventSink) {
boolean enterResult = super.enterPictureInPictureMode(params);
if (enterResult && null != mEventSink) {
mEventSink.success(getParams(EVENT_PIP_MODE_REQUEST_START, null));
}
return super.enterPictureInPictureMode(params);
return enterResult;
}
private Map<String, Object> getParams(int event, Bundle bundle) {
......
......@@ -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(); // 总播放时长,转换后的单位 秒
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();
}
......
......@@ -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 <TXLiteAVSDK_Professional/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 <TXLiteAVSDK_Professional/TXVodPreloadManager.h>
#import <TXLiteAVSDK_Professional/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,9 +3,30 @@
#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功能没有启动
// 画中画事件code
#define EVENT_PIP_MODE_ALREADY_ENTER 1
......@@ -32,4 +53,16 @@
#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 */
......@@ -66,11 +66,20 @@ BOOL volatile isStop = false;
_netStatusChannel = [FlutterEventChannel eventChannelWithName:[@"cloud.tencent.com/txvodplayer/net/" stringByAppendingString:[self.playerId stringValue]] binaryMessenger:[registrar messenger]];
[_netStatusChannel setStreamHandler:self];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onApplicationTerminateClick) name:UIApplicationWillTerminateNotification object:nil];
}
return self;
}
- (void)onApplicationTerminateClick {
[_txVodPlayer removeVideoWidget];
_txVodPlayer = nil;
_txVodPlayer.videoProcessDelegate = nil;
_textureId = -1;
}
- (void)destory
{
[self stopPlay];
......
// 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; // 音量变化
......@@ -113,8 +113,14 @@ abstract class TXVodPlayEvent {
/// 视频下载相关事件
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 {
......@@ -171,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 {
......@@ -196,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,
Future<int> startPreLoad(
final String playUrl,
final int preloadSizeMB,
final int preferredResolution,
{ FTXPredownlodOnCompleteListener? onCompleteListener,
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) {
}
_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';
......@@ -18,3 +19,4 @@ part 'Core/txplayer_widget.dart';
part 'Core/txvodplayer_config.dart';
part 'Core/txvodplayer_controller.dart';
part 'Core/txvoddownload_controller.dart';
part 'Core/tools/common_utils.dart';
\ No newline at end of file
......@@ -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 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论