Skip to content
项目
群组
代码片段
帮助
当前项目
正在载入...
登录 / 注册
切换导航面板
T
tx_player_fork
项目
项目
详情
活动
周期分析
仓库
仓库
文件
提交
分支
标签
贡献者
图表
比较
统计图
议题
0
议题
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
CI / CD
CI / CD
流水线
作业
日程
统计图
Wiki
Wiki
代码片段
代码片段
成员
成员
折叠边栏
关闭边栏
活动
图像
聊天
创建新问题
作业
提交
问题看板
Open sidebar
蒋俊
tx_player_fork
Commits
8ea0a67a
提交
8ea0a67a
authored
10月 28, 2024
作者:
kongdywang
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
1. Added Picture-in-Picture capability for iOS live streaming.
2. Fixed the Picture-in-Picture service leak issue on Android.
上级
b4fe752b
全部展开
隐藏空白字符变更
内嵌
并排
正在显示
31 个修改的文件
包含
931 行增加
和
118 行删除
+931
-118
FTXLivePlayer.java
.../src/main/java/com/tencent/vod/flutter/FTXLivePlayer.java
+2
-1
SuperPlayerPlugin.java
.../main/java/com/tencent/vod/flutter/SuperPlayerPlugin.java
+4
-0
FlutterPipImplActivity.java
...va/com/tencent/vod/flutter/ui/FlutterPipImplActivity.java
+1
-1
FTXEvent.h
Flutter/ios/Classes/FTXEvent.h
+6
-0
FTXLivePlayer.h
Flutter/ios/Classes/FTXLivePlayer.h
+3
-0
FTXLivePlayer.m
Flutter/ios/Classes/FTXLivePlayer.m
+0
-0
FTXVodPlayer.h
Flutter/ios/Classes/FTXVodPlayer.h
+1
-18
FTXVodPlayer.m
Flutter/ios/Classes/FTXVodPlayer.m
+9
-66
SuperPlayerPlugin.m
Flutter/ios/Classes/SuperPlayerPlugin.m
+4
-0
FTXPlayerConstants.h
Flutter/ios/Classes/common/FTXPlayerConstants.h
+64
-0
FTXLivePipDelegate.h
Flutter/ios/Classes/helper/FTXLivePipDelegate.h
+16
-0
FTXVodPlayerDelegate.h
Flutter/ios/Classes/helper/FTXVodPlayerDelegate.h
+23
-0
TXPipAuth.h
Flutter/ios/Classes/helper/TXPipAuth.h
+19
-0
TXPipAuth.m
Flutter/ios/Classes/helper/TXPipAuth.m
+54
-0
FTXBackPlayer.h
Flutter/ios/Classes/live/pip/FTXBackPlayer.h
+37
-0
FTXBackPlayer.m
Flutter/ios/Classes/live/pip/FTXBackPlayer.m
+246
-0
FTXPipCaller.h
Flutter/ios/Classes/live/pip/FTXPipCaller.h
+31
-0
FTXPipController.h
Flutter/ios/Classes/live/pip/FTXPipController.h
+30
-0
FTXPipController.m
Flutter/ios/Classes/live/pip/FTXPipController.m
+130
-0
FTXPipFactory.h
Flutter/ios/Classes/live/pip/FTXPipFactory.h
+19
-0
FTXPipFactory.m
Flutter/ios/Classes/live/pip/FTXPipFactory.m
+22
-0
FTXPipGlobalImpl.h
Flutter/ios/Classes/live/pip/FTXPipGlobalImpl.h
+16
-0
FTXPipGlobalImpl.m
Flutter/ios/Classes/live/pip/FTXPipGlobalImpl.m
+0
-0
FTXPipPlayerDelegate.h
Flutter/ios/Classes/live/pip/FTXPipPlayerDelegate.h
+14
-0
FTXPipRenderView.h
Flutter/ios/Classes/live/pip/FTXPipRenderView.h
+18
-0
FTXPipRenderView.m
Flutter/ios/Classes/live/pip/FTXPipRenderView.m
+51
-0
FTXImgTools.h
Flutter/ios/Classes/tools/FTXImgTools.h
+19
-0
FTXImgTools.m
Flutter/ios/Classes/tools/FTXImgTools.m
+74
-0
txliveplayer_controller.dart
Flutter/lib/Core/txliveplayer_controller.dart
+7
-14
txplayer_define.dart
Flutter/lib/Core/txplayer_define.dart
+6
-0
superplayer_controller.dart
...Widget/superplayer_widget/lib/superplayer_controller.dart
+5
-18
没有找到文件。
Flutter/android/src/main/java/com/tencent/vod/flutter/FTXLivePlayer.java
浏览文件 @
8ea0a67a
...
...
@@ -6,6 +6,7 @@ import android.graphics.Bitmap;
import
android.graphics.SurfaceTexture
;
import
android.os.Bundle
;
import
android.view.Surface
;
import
android.view.TextureView
;
import
androidx.annotation.NonNull
;
...
...
@@ -65,7 +66,7 @@ public class FTXLivePlayer extends FTXBasePlayer implements TXFlutterLivePlayerA
private
final
FTXV2LiveObserver
mObserver
;
private
int
mLastPlayEvent
=
-
1
;
private
boolean
mIsPaused
=
false
;
private
FtxMessages
.
TXLivePlayerFlutterAPI
mLiveFlutterApi
;
private
final
FtxMessages
.
TXLivePlayerFlutterAPI
mLiveFlutterApi
;
private
final
FTXPIPManager
.
PipCallback
pipCallback
=
new
FTXPIPManager
.
PipCallback
()
{
@Override
...
...
Flutter/android/src/main/java/com/tencent/vod/flutter/SuperPlayerPlugin.java
浏览文件 @
8ea0a67a
...
...
@@ -204,6 +204,7 @@ public class SuperPlayerPlugin implements FlutterPlugin, ActivityAware,
mPlayers
.
append
(
playerId
,
player
);
PlayerMsg
playerMsg
=
new
PlayerMsg
();
playerMsg
.
setPlayerId
((
long
)
playerId
);
LiteavLog
.
i
(
TAG
,
"createVodPlayer :"
+
playerId
);
return
playerMsg
;
}
...
...
@@ -215,6 +216,7 @@ public class SuperPlayerPlugin implements FlutterPlugin, ActivityAware,
mPlayers
.
append
(
playerId
,
player
);
PlayerMsg
playerMsg
=
new
PlayerMsg
();
playerMsg
.
setPlayerId
((
long
)
playerId
);
LiteavLog
.
i
(
TAG
,
"createLivePlayer :"
+
playerId
);
return
playerMsg
;
}
...
...
@@ -229,8 +231,10 @@ public class SuperPlayerPlugin implements FlutterPlugin, ActivityAware,
public
void
releasePlayer
(
@NonNull
PlayerMsg
playerId
)
{
if
(
null
!=
playerId
.
getPlayerId
())
{
int
intPlayerId
=
playerId
.
getPlayerId
().
intValue
();
LiteavLog
.
i
(
TAG
,
"releasePlayer :"
+
intPlayerId
);
FTXBasePlayer
player
=
mPlayers
.
get
(
intPlayerId
);
if
(
player
!=
null
)
{
LiteavLog
.
i
(
TAG
,
"releasePlayer start destroy player :"
+
intPlayerId
);
player
.
destroy
();
mPlayers
.
remove
(
intPlayerId
);
}
...
...
Flutter/android/src/main/java/com/tencent/vod/flutter/ui/FlutterPipImplActivity.java
浏览文件 @
8ea0a67a
...
...
@@ -498,7 +498,7 @@ public class FlutterPipImplActivity extends Activity implements TextureView.Surf
private
void
bindAndroid12BugServiceIfNeed
()
{
if
(
Build
.
VERSION
.
SDK_INT
>=
VERSION_CODES
.
Q
)
{
Intent
serviceIntent
=
new
Intent
(
this
,
TXAndroid12BridgeService
.
class
);
Intent
serviceIntent
=
new
Intent
(
getApplicationContext
()
,
TXAndroid12BridgeService
.
class
);
startService
(
serviceIntent
);
bindService
(
serviceIntent
,
this
,
Context
.
BIND_AUTO_CREATE
);
}
...
...
Flutter/ios/Classes/FTXEvent.h
浏览文件 @
8ea0a67a
...
...
@@ -45,6 +45,12 @@
/// PIP function is not started.
/// PIP功能没有启动
#define ERROR_IOS_PIP_NOT_RUNNING -111
/// PIP start time out
/// PIP 启动超时
#define ERROR_IOS_PIP_START_TIME_OUT -112
/// Insufficient permissions, currently only appears in Picture-in-Picture live streaming
/// 权限不足,目前只出现在直播画中画
#define ERROR_PIP_AUTH_DENIED -201
...
...
Flutter/ios/Classes/FTXLivePlayer.h
浏览文件 @
8ea0a67a
...
...
@@ -4,6 +4,7 @@
#import <Foundation/Foundation.h>
#import "FTXBasePlayer.h"
#import "FTXVodPlayerDelegate.h"
@protocol
FlutterPluginRegistrar
;
...
...
@@ -11,6 +12,8 @@ NS_ASSUME_NONNULL_BEGIN
@interface
FTXLivePlayer
:
FTXBasePlayer
@property
(
nonatomic
,
weak
)
id
<
FTXVodPlayerDelegate
>
delegate
;
-
(
instancetype
)
initWithRegistrar
:(
id
<
FlutterPluginRegistrar
>
)
registrar
;
-
(
void
)
notifyAppTerminate
:(
UIApplication
*
)
application
;
...
...
Flutter/ios/Classes/FTXLivePlayer.m
浏览文件 @
8ea0a67a
差异被折叠。
点击展开。
Flutter/ios/Classes/FTXVodPlayer.h
浏览文件 @
8ea0a67a
...
...
@@ -4,29 +4,12 @@
#import <Foundation/Foundation.h>
#import "FTXBasePlayer.h"
#import "FTXVodPlayerDelegate.h"
@protocol
FlutterPluginRegistrar
;
NS_ASSUME_NONNULL_BEGIN
@protocol
FTXVodPlayerDelegate
<
NSObject
>
-
(
void
)
onPlayerPipRequestStart
;
-
(
void
)
onPlayerPipStateDidStart
;
-
(
void
)
onPlayerPipStateWillStop
;
-
(
void
)
onPlayerPipStateDidStop
;
-
(
void
)
onPlayerPipStateRestoreUI
:(
double
)
playTime
;;
-
(
void
)
onPlayerPipStateError
:(
NSInteger
)
errorId
;
-
(
void
)
releasePlayerInner
:(
NSNumber
*
)
playerId
;
@end
@interface
FTXVodPlayer
:
FTXBasePlayer
@property
(
nonatomic
,
weak
)
id
<
FTXVodPlayerDelegate
>
delegate
;
...
...
Flutter/ios/Classes/FTXVodPlayer.m
浏览文件 @
8ea0a67a
...
...
@@ -12,6 +12,7 @@
#import "TXCommonUtil.h"
#import "FTXLog.h"
#import <stdatomic.h>
#import "FTXImgTools.h"
static
const
int
uninitialized
=
-
1
;
static
const
int
CODE_ON_RECEIVE_FIRST_FRAME
=
2003
;
...
...
@@ -547,7 +548,8 @@ static const int CODE_ON_RECEIVE_FIRST_FRAME = 2003;
}
UIImage
*
image
=
[
UIImage
imageWithContentsOfFile
:
imagePath
];
return
[
self
CVPixelBufferRefFromUiImage
:
image
];
// must create new obj when evey called
return
[
FTXImgTools
CVPixelBufferRefFromUiImage
:
image
];
}
-
(
void
)
setPlayerConfig
:
(
FTXVodPlayConfigPlayerMsg
*
)
args
...
...
@@ -792,6 +794,12 @@ static const int CODE_ON_RECEIVE_FIRST_FRAME = 2003;
case
TX_VOD_PLAYER_PIP_ERROR_TYPE_PIP_NOT_RUNNING
:
type
=
ERROR_IOS_PIP_NOT_RUNNING
;
break
;
case
TX_VOD_PLAYER_PIP_ERROR_TYPE_PIP_START_TIMEOUT
:
type
=
ERROR_IOS_PIP_START_TIME_OUT
;
break
;
default
:
type
=
errorType
;
break
;
}
self
.
hasEnteredPipMode
=
NO
;
FTXLOGE
(
@"[onPlayer], pictureInPictureErrorDidOccur errorType= %ld"
,
type
);
...
...
@@ -807,71 +815,6 @@ static const int CODE_ON_RECEIVE_FIRST_FRAME = 2003;
-
(
void
)
onPlayer
:
(
TXVodPlayer
*
)
player
airPlayStateDidChange
:
(
TX_VOD_PLAYER_AIRPLAY_STATE
)
airPlayState
withParam
:
(
NSDictionary
*
)
param
{
}
#pragma mark - Convert UIImage to CVPixelBufferRef
-
(
CVPixelBufferRef
)
CVPixelBufferRefFromUiImage
:
(
UIImage
*
)
img
{
CGSize
size
=
img
.
size
;
CGImageRef
image
=
[
img
CGImage
];
BOOL
hasAlpha
=
CGImageRefContainsAlpha
(
image
);
CFDictionaryRef
empty
=
CFDictionaryCreate
(
kCFAllocatorDefault
,
NULL
,
NULL
,
0
,
&
kCFTypeDictionaryKeyCallBacks
,
&
kCFTypeDictionaryValueCallBacks
);
NSDictionary
*
options
=
[
NSDictionary
dictionaryWithObjectsAndKeys
:
[
NSNumber
numberWithBool
:
YES
],
kCVPixelBufferCGImageCompatibilityKey
,
[
NSNumber
numberWithBool
:
YES
],
kCVPixelBufferCGBitmapContextCompatibilityKey
,
empty
,
kCVPixelBufferIOSurfacePropertiesKey
,
nil
];
CVPixelBufferRef
pxbuffer
=
NULL
;
CVReturn
status
=
CVPixelBufferCreate
(
kCFAllocatorDefault
,
size
.
width
,
size
.
height
,
inputPixelFormat
(),
(
__bridge
CFDictionaryRef
)
options
,
&
pxbuffer
);
NSParameterAssert
(
status
==
kCVReturnSuccess
&&
pxbuffer
!=
NULL
);
CVPixelBufferLockBaseAddress
(
pxbuffer
,
0
);
void
*
pxdata
=
CVPixelBufferGetBaseAddress
(
pxbuffer
);
NSParameterAssert
(
pxdata
!=
NULL
);
CGColorSpaceRef
rgbColorSpace
=
CGColorSpaceCreateDeviceRGB
();
uint32_t
bitmapInfo
=
bitmapInfoWithPixelFormatType
(
inputPixelFormat
(),
(
bool
)
hasAlpha
);
CGContextRef
context
=
CGBitmapContextCreate
(
pxdata
,
size
.
width
,
size
.
height
,
8
,
CVPixelBufferGetBytesPerRow
(
pxbuffer
),
rgbColorSpace
,
bitmapInfo
);
NSParameterAssert
(
context
);
CGContextDrawImage
(
context
,
CGRectMake
(
0
,
0
,
CGImageGetWidth
(
image
),
CGImageGetHeight
(
image
)),
image
);
CVPixelBufferUnlockBaseAddress
(
pxbuffer
,
0
);
CGColorSpaceRelease
(
rgbColorSpace
);
CGContextRelease
(
context
);
return
pxbuffer
;
}
static
OSType
inputPixelFormat
(){
return
kCVPixelFormatType_32BGRA
;
}
static
uint32_t
bitmapInfoWithPixelFormatType
(
OSType
inputPixelFormat
,
bool
hasAlpha
){
if
(
inputPixelFormat
==
kCVPixelFormatType_32BGRA
)
{
uint32_t
bitmapInfo
=
kCGImageAlphaPremultipliedFirst
|
kCGBitmapByteOrder32Host
;
if
(
!
hasAlpha
)
{
bitmapInfo
=
kCGImageAlphaNoneSkipFirst
|
kCGBitmapByteOrder32Host
;
}
return
bitmapInfo
;
}
else
if
(
inputPixelFormat
==
kCVPixelFormatType_32ARGB
)
{
uint32_t
bitmapInfo
=
kCGImageAlphaPremultipliedFirst
|
kCGBitmapByteOrder32Big
;
return
bitmapInfo
;
}
else
{
return
0
;
}
}
// Check alpha value
BOOL
CGImageRefContainsAlpha
(
CGImageRef
imageRef
)
{
if
(
!
imageRef
)
{
return
NO
;
}
CGImageAlphaInfo
alphaInfo
=
CGImageGetAlphaInfo
(
imageRef
);
BOOL
hasAlpha
=
!
(
alphaInfo
==
kCGImageAlphaNone
||
alphaInfo
==
kCGImageAlphaNoneSkipFirst
||
alphaInfo
==
kCGImageAlphaNoneSkipLast
);
return
hasAlpha
;
}
#pragma mark - TXFlutterVodPlayerApi
-
(
nullable
BoolMsg
*
)
enableHardwareDecodeEnable
:
(
nonnull
BoolPlayerMsg
*
)
enable
error
:
(
FlutterError
*
_Nullable
__autoreleasing
*
_Nonnull
)
error
{
...
...
Flutter/ios/Classes/SuperPlayerPlugin.m
浏览文件 @
8ea0a67a
...
...
@@ -116,6 +116,7 @@ SuperPlayerPlugin* instance;
FTXLOGV
(
@"called releasePlayerInner,%@ is start release"
,
playerId
);
FTXBasePlayer
*
player
=
[
_players
objectForKey
:
playerId
];
if
(
player
!=
nil
)
{
FTXLOGI
(
@"releasePlayer start destroy player :%@"
,
playerId
);
[
player
destory
];
[
_players
removeObjectForKey
:
playerId
];
}
...
...
@@ -234,8 +235,10 @@ SuperPlayerPlugin* instance;
-
(
nullable
PlayerMsg
*
)
createLivePlayerWithError
:
(
FlutterError
*
_Nullable
__autoreleasing
*
_Nonnull
)
error
{
FTXLivePlayer
*
player
=
[[
FTXLivePlayer
alloc
]
initWithRegistrar
:
self
.
registrar
];
player
.
delegate
=
self
;
NSNumber
*
playerId
=
player
.
playerId
;
_players
[
playerId
]
=
player
;
FTXLOGI
(
@"createLivePlayer :%@"
,
playerId
);
return
[
TXCommonUtil
playerMsgWith
:
playerId
];
}
...
...
@@ -244,6 +247,7 @@ SuperPlayerPlugin* instance;
player
.
delegate
=
self
;
NSNumber
*
playerId
=
player
.
playerId
;
_players
[
playerId
]
=
player
;
FTXLOGI
(
@"createVodPlayer :%@"
,
playerId
);
return
[
TXCommonUtil
playerMsgWith
:
playerId
];
}
...
...
Flutter/ios/Classes/common/FTXPlayerConstants.h
0 → 100644
浏览文件 @
8ea0a67a
// Copyright (c) 2024 Tencent. All rights reserved.
#ifndef SUPERPLAYER_FLUTTER_IOS_CLASSES_COMMON_FTXPLAYERCONSTANTS_H_
#define SUPERPLAYER_FLUTTER_IOS_CLASSES_COMMON_FTXPLAYERCONSTANTS_H_
#import "FTXLiteAVSDKHeader.h"
// 点播高级套餐 Feature
#define TUI_FEATURE_PLAYER_PREMIUM (0b10000000)
// 点播企业套餐 Feature
#define TUI_FEATURE_PLAYER_ENTERPRISE (0b100000000)
// pip status
typedef
NS_ENUM
(
NSInteger
,
FTX_LIVE_PIP_ERROR
)
{
/// 无错误
FTX_VOD_PLAYER_PIP_ERROR_TYPE_NONE
=
TX_VOD_PLAYER_PIP_ERROR_TYPE_NONE
,
/// 设备或系统版本不支持(iPad iOS9+ 才支持PIP)
FTX_VOD_PLAYER_PIP_ERROR_TYPE_DEVICE_NOT_SUPPORT
=
TX_VOD_PLAYER_PIP_ERROR_TYPE_DEVICE_NOT_SUPPORT
,
/// 播放器不支持
FTX_VOD_PLAYER_PIP_ERROR_TYPE_PLAYER_NOT_SUPPORT
=
TX_VOD_PLAYER_PIP_ERROR_TYPE_PLAYER_NOT_SUPPORT
,
/// 视频不支持
FTX_VOD_PLAYER_PIP_ERROR_TYPE_VIDEO_NOT_SUPPORT
=
TX_VOD_PLAYER_PIP_ERROR_TYPE_VIDEO_NOT_SUPPORT
,
/// PIP控制器不可用
FTX_VOD_PLAYER_PIP_ERROR_TYPE_PIP_IS_NOT_POSSIBLE
=
TX_VOD_PLAYER_PIP_ERROR_TYPE_PIP_IS_NOT_POSSIBLE
,
/// PIP控制器报错
FTX_VOD_PLAYER_PIP_ERROR_TYPE_ERROR_FROM_SYSTEM
=
TX_VOD_PLAYER_PIP_ERROR_TYPE_ERROR_FROM_SYSTEM
,
/// 播放器对象不存在
FTX_VOD_PLAYER_PIP_ERROR_TYPE_PLAYER_NOT_EXIST
=
TX_VOD_PLAYER_PIP_ERROR_TYPE_PLAYER_NOT_EXIST
,
/// PIP功能已经运行
FTX_VOD_PLAYER_PIP_ERROR_TYPE_PIP_IS_RUNNING
=
TX_VOD_PLAYER_PIP_ERROR_TYPE_PIP_IS_RUNNING
,
/// PIP功能没有启动
FTX_VOD_PLAYER_PIP_ERROR_TYPE_PIP_NOT_RUNNING
=
TX_VOD_PLAYER_PIP_ERROR_TYPE_PIP_NOT_RUNNING
,
/// PIP启动超时
FTX_VOD_PLAYER_PIP_ERROR_TYPE_PIP_START_TIMEOUT
=
TX_VOD_PLAYER_PIP_ERROR_TYPE_PIP_START_TIMEOUT
,
/// pip 没有 sdk 权限
FTX_VOD_PLAYER_PIP_ERROR_TYPE_PIP_AUTH_DENIED
=
101
,
// 缺乏画中画 bundle 资源
FTX_VOD_PLAYER_PIP_ERROR_TYPE_PIP_MISS_RESOURCE
=
102
,
};
/**
后台画中画播放器的播放状态
*/
typedef
NS_ENUM
(
NSInteger
,
FTXAVPlayerState
)
{
FTXAVPlayerStateIdle
=
0
,
// 初始状态
FTXAVPlayerStatePrepared
,
// 播放准备完毕
FTXAVPlayerStatePlaying
,
// 播放中
FTXAVPlayerStatePaused
,
// 播放暂停
FTXAVPlayerStateStopped
,
// 播放停止
FTXAVPlayerStateComplete
,
// 播放完毕
FTXAVPlayerStateError
,
// 播放失败
};
#endif // SUPERPLAYER_FLUTTER_IOS_CLASSES_COMMON_FTXPLAYERCONSTANTS_H_
Flutter/ios/Classes/helper/FTXLivePipDelegate.h
0 → 100644
浏览文件 @
8ea0a67a
// Copyright (c) 2024 Tencent. All rights reserved.
#ifndef SUPERPLAYER_FLUTTER_IOS_CLASSES_HELPER_FTXLIVEPIPDELEGATE_H_
#define SUPERPLAYER_FLUTTER_IOS_CLASSES_HELPER_FTXLIVEPIPDELEGATE_H_
#import "FTXLiteAVSDKHeader.h"
#import "FTXPlayerConstants.h"
@protocol
FTXLivePipDelegate
<
NSObject
>
-
(
void
)
pictureInPictureStateDidChange
:
(
TX_VOD_PLAYER_PIP_STATE
)
status
;
-
(
void
)
pictureInPictureErrorDidOccur
:(
FTX_LIVE_PIP_ERROR
)
errorStatus
;
@end
#endif // SUPERPLAYER_FLUTTER_IOS_CLASSES_HELPER_FTXLIVEPIPDELEGATE_H_
Flutter/ios/Classes/helper/FTXVodPlayerDelegate.h
0 → 100644
浏览文件 @
8ea0a67a
// Copyright (c) 2024 Tencent. All rights reserved.
#ifndef SUPERPLAYER_FLUTTER_IOS_CLASSES_HELPER_FTXVODPLAYERDELEGATE_H_
#define SUPERPLAYER_FLUTTER_IOS_CLASSES_HELPER_FTXVODPLAYERDELEGATE_H_
@protocol
FTXVodPlayerDelegate
<
NSObject
>
-
(
void
)
onPlayerPipRequestStart
;
-
(
void
)
onPlayerPipStateDidStart
;
-
(
void
)
onPlayerPipStateWillStop
;
-
(
void
)
onPlayerPipStateDidStop
;
-
(
void
)
onPlayerPipStateRestoreUI
:(
double
)
playTime
;;
-
(
void
)
onPlayerPipStateError
:(
NSInteger
)
errorId
;
-
(
void
)
releasePlayerInner
:(
NSNumber
*
)
playerId
;
@end
#endif // SUPERPLAYER_FLUTTER_IOS_CLASSES_HELPER_FTXVODPLAYERDELEGATE_H_
Flutter/ios/Classes/helper/TXPipAuth.h
0 → 100644
浏览文件 @
8ea0a67a
// Copyright (c) 2024 Tencent. All rights reserved.
#ifndef SUPERPLAYER_FLUTTER_IOS_CLASSES_HELPER_TXPIPAUTH_H_
#define SUPERPLAYER_FLUTTER_IOS_CLASSES_HELPER_TXPIPAUTH_H_
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface
TXPipAuth
:
NSObject
+
(
instancetype
)
shareInstance
;
+
(
BOOL
)
cpa
;
@end
NS_ASSUME_NONNULL_END
#endif // SUPERPLAYER_FLUTTER_IOS_CLASSES_HELPER_TXPIPAUTH_H_
Flutter/ios/Classes/helper/TXPipAuth.m
0 → 100644
浏览文件 @
8ea0a67a
// Copyright (c) 2024 Tencent. All rights reserved.
#import "TXPipAuth.h"
#import "FTXPlayerConstants.h"
#define TUI_RGSKEY_PARAM1 @"KEY_PARAM1"
#define TUI_RETKEY_PARAM1 @"KEY_PARAM1"
#define TUI_ID_CHECK_FEATURE_AUTH (2) ///< 校验某个 feature 是否授权
@implementation
TXPipAuth
+
(
instancetype
)
shareInstance
{
static
TXPipAuth
*
g_playerAuth
=
nil
;
static
dispatch_once_t
onceToken
;
dispatch_once
(
&
onceToken
,
^
{
g_playerAuth
=
[[
self
alloc
]
init
];
});
return
g_playerAuth
;
}
+
(
BOOL
)
cpa
{
return
[
TXPipAuth
cfa
:
TUI_FEATURE_PLAYER_PREMIUM
];
}
+
(
BOOL
)
cfa
:
(
int
)
featureId
{
// 输入参数
NSMutableDictionary
*
inputParams
=
[
NSMutableDictionary
dictionary
];
NSString
*
featureIdStr
=
[
NSString
stringWithFormat
:
@"%@"
,
@
(
featureId
)];
[
inputParams
setObject
:
featureIdStr
forKey
:
TUI_RGSKEY_PARAM1
];
// 函数调用
__block
BOOL
result
=
NO
;
Class
HostEngineManagerClass
=
NSClassFromString
(
@"TXCHostEngineManager"
);
SEL
sharedManagerSEL
=
NSSelectorFromString
(
@"sharedManager"
);
IMP
sharedManagerIMP
=
[
HostEngineManagerClass
methodForSelector
:
sharedManagerSEL
];
NSObject
*
(
*
sharedManagerFunc
)(
id
,
SEL
)
=
(
void
*
)
sharedManagerIMP
;
NSObject
*
sharedManagerObj
=
sharedManagerFunc
(
HostEngineManagerClass
,
sharedManagerSEL
);
void
(
^
SyncRequestToHostFuncBlock
)(
NSDictionary
*
)
=
^
(
NSDictionary
*
outParams
)
{
NSObject
*
featureAuthObj
=
[
outParams
objectForKey
:
TUI_RETKEY_PARAM1
];
if
([
featureAuthObj
isKindOfClass
:[
NSNumber
class
]])
{
result
=
[(
NSNumber
*
)
featureAuthObj
boolValue
];
}
};
SEL
sendSyncRequestToHostSEL
=
NSSelectorFromString
(
@"sendSyncRequestToHostWithFunctionId:inputParams:completionHandler:"
);
IMP
sendSyncRequestToHostIMP
=
[
sharedManagerObj
methodForSelector
:
sendSyncRequestToHostSEL
];
void
(
*
sendSyncRequestToHostFunc
)(
id
,
SEL
,
NSInteger
,
NSDictionary
*
,
void
(
^
)(
NSDictionary
<
NSString
*
,
NSObject
*>
*
))
=
(
void
*
)
sendSyncRequestToHostIMP
;
sendSyncRequestToHostFunc
(
sharedManagerObj
,
sendSyncRequestToHostSEL
,
TUI_ID_CHECK_FEATURE_AUTH
,
inputParams
,
SyncRequestToHostFuncBlock
);
return
result
;
}
@end
Flutter/ios/Classes/live/pip/FTXBackPlayer.h
0 → 100644
浏览文件 @
8ea0a67a
// Copyright (c) 2024 Tencent. All rights reserved.
#ifndef SUPERPLAYER_FLUTTER_IOS_CLASSES_LIVE_PIP_FTXBACKPLAYER_H_
#define SUPERPLAYER_FLUTTER_IOS_CLASSES_LIVE_PIP_FTXBACKPLAYER_H_
#import <Foundation/Foundation.h>
#import <AVKit/AVKit.h>
#import "FTXPipPlayerDelegate.h"
NS_ASSUME_NONNULL_BEGIN
@interface
FTXBackPlayer
:
NSObject
@property
(
nonatomic
,
strong
)
id
<
FTXPipPlayerDelegate
>
playerDelegate
;
-
(
void
)
prepareVideo
:(
AVPlayerItem
*
)
item
;
-
(
void
)
replaceCurrentItemWithPlayerItem
:(
AVPlayerItem
*
)
item
;
-
(
void
)
play
;
-
(
void
)
setContainerView
:(
UIView
*
)
container
;
-
(
void
)
setLoopback
:(
BOOL
)
isLoop
;
-
(
void
)
seekTo
:(
int64_t
)
positionMs
;
-
(
void
)
stop
;
-
(
void
)
pause
;
-
(
AVPlayerLayer
*
)
getPlayerLayer
;
@end
NS_ASSUME_NONNULL_END
#endif // SUPERPLAYER_FLUTTER_IOS_CLASSES_LIVE_PIP_FTXBACKPLAYER_H_
Flutter/ios/Classes/live/pip/FTXBackPlayer.m
0 → 100644
浏览文件 @
8ea0a67a
// Copyright (c) 2024 Tencent. All rights reserved.
#import "FTXBackPlayer.h"
#import "FTXPlayerConstants.h"
#import "FTXLog.h"
/// 微妙转毫秒
#define USEC_TO_MSEC 1000ull
static
NSString
*
kPipTag
=
@"FTXBackPlayer"
;
static
void
*
gTVKAVPlayerKVOContextTimeControlStatus
=
&
gTVKAVPlayerKVOContextTimeControlStatus
;
static
void
*
gTVKAVPlayerKVOContextAirplay
=
&
gTVKAVPlayerKVOContextAirplay
;
static
void
*
gTVKAVPlayerKVOContextError
=
&
gTVKAVPlayerKVOContextError
;
static
void
*
gTVKAVPlayerItemKVOContextState
=
&
gTVKAVPlayerItemKVOContextState
;
@interface
FTXBackPlayer
()
@property
(
nonatomic
,
strong
)
AVPlayer
*
avPlayer
;
@property
(
nonatomic
,
strong
)
AVPlayerLayer
*
playerLayer
;
@property
(
nonatomic
,
strong
)
AVPlayerItem
*
playerItem
;
@property
(
nonatomic
,
assign
)
BOOL
isLoopBack
;
@property
(
nonatomic
,
strong
)
AVAsset
*
asset
;
@property
(
nonatomic
,
assign
)
FTXAVPlayerState
playerState
;
/// 系统播放资源
@end
@implementation
FTXBackPlayer
-
(
instancetype
)
init
{
self
=
[
super
init
];
if
(
self
)
{
self
.
playerState
=
FTXAVPlayerStateIdle
;
}
return
self
;
}
-
(
void
)
prepareVideo
:
(
AVPlayerItem
*
)
item
{
self
.
asset
=
item
.
asset
;
self
.
playerItem
=
item
;
self
.
avPlayer
=
[[
AVPlayer
alloc
]
initWithPlayerItem
:
self
.
playerItem
];
self
.
avPlayer
.
volume
=
0
;
self
.
avPlayer
.
rate
=
1
;
self
.
avPlayer
.
muted
=
YES
;
if
(
self
.
isLoopBack
)
{
[
self
.
avPlayer
setActionAtItemEnd
:
AVPlayerActionAtItemEndNone
];
}
// 不允许airplay
self
.
avPlayer
.
allowsExternalPlayback
=
NO
;
self
.
playerLayer
=
[[
AVPlayerLayer
alloc
]
init
];
self
.
playerLayer
.
videoGravity
=
AVLayerVideoGravityResizeAspectFill
;
self
.
playerLayer
.
hidden
=
YES
;
[
self
.
playerLayer
setPlayer
:
self
.
avPlayer
];
[[
NSNotificationCenter
defaultCenter
]
removeObserver
:
self
name
:
AVPlayerItemDidPlayToEndTimeNotification
object
:
self
.
playerItem
];
[[
NSNotificationCenter
defaultCenter
]
addObserver
:
self
selector
:
@selector
(
playbackFinished
:
)
name
:
AVPlayerItemDidPlayToEndTimeNotification
object
:
self
.
playerItem
];
[
self
addKVOWithPlayer
:
self
.
avPlayer
];
self
.
playerState
=
FTXAVPlayerStatePrepared
;
}
-
(
void
)
replaceCurrentItemWithPlayerItem
:
(
AVPlayerItem
*
)
playerItem
{
FTXLOGI
(
@"[%@]replaceCurrentItemWithPlayerItem"
,
kPipTag
);
if
(
nil
==
self
.
avPlayer
)
{
FTXLOGW
(
@"[%@]replaceCurrentItemWithPlayerItem met null player"
,
kPipTag
);
return
;
}
self
.
asset
=
playerItem
.
asset
;
[[
NSNotificationCenter
defaultCenter
]
removeObserver
:
self
name
:
AVPlayerItemDidPlayToEndTimeNotification
object
:
self
.
playerItem
];
self
.
playerItem
=
playerItem
;
[[
NSNotificationCenter
defaultCenter
]
addObserver
:
self
selector
:
@selector
(
playbackFinished
:
)
name
:
AVPlayerItemDidPlayToEndTimeNotification
object
:
self
.
playerItem
];
}
-
(
void
)
seekTo
:
(
int64_t
)
positionMs
{
FTXLOGI
(
@"[%@]seekto %lld ms"
,
kPipTag
,
positionMs
);
if
(
positionMs
<
0
)
{
return
;
}
if
(
nil
==
self
.
avPlayer
)
{
FTXLOGW
(
@"[%@]seekto met null player"
,
kPipTag
);
return
;
}
dispatch_async
(
dispatch_get_main_queue
(),
^
{
[
self
.
avPlayer
seekToTime
:
CMTimeMake
(
positionMs
*
1000
,
USEC_PER_SEC
)
toleranceBefore
:
kCMTimeZero
toleranceAfter
:
kCMTimeZero
];
});
}
-
(
void
)
play
{
FTXLOGI
(
@"[%@]play"
,
kPipTag
);
if
(
nil
==
self
.
avPlayer
)
{
FTXLOGW
(
@"[%@]play met null player"
,
kPipTag
);
return
;
}
[
self
.
avPlayer
play
];
self
.
playerState
=
FTXAVPlayerStatePlaying
;
}
-
(
void
)
pause
{
FTXLOGI
(
@"[%@]pause"
,
kPipTag
);
if
(
nil
==
self
.
avPlayer
)
{
FTXLOGW
(
@"[%@]pause met null player"
,
kPipTag
);
return
;
}
dispatch_async
(
dispatch_get_main_queue
(),
^
{
[
self
.
avPlayer
pause
];
self
.
playerState
=
FTXAVPlayerStatePaused
;
});
}
-
(
void
)
stop
{
FTXLOGI
(
@"[%@]stop"
,
kPipTag
);
if
(
nil
==
self
.
avPlayer
)
{
FTXLOGW
(
@"[%@]stop met null player"
,
kPipTag
);
return
;
}
dispatch_async
(
dispatch_get_main_queue
(),
^
{
[
self
.
avPlayer
pause
];
self
.
playerState
=
FTXAVPlayerStateStopped
;
});
[
self
reset
];
}
-
(
void
)
setContainerView
:
(
UIView
*
)
container
{
FTXLOGI
(
@"[%@]setContainerView"
,
kPipTag
);
if
(
nil
==
self
.
playerLayer
)
{
FTXLOGW
(
@"[%@]setContainerView met null playerLayer"
,
kPipTag
);
return
;
}
[
container
.
layer
addSublayer
:
self
.
playerLayer
];
self
.
playerLayer
.
frame
=
container
.
bounds
;
}
-
(
void
)
setPlayerState
:
(
FTXAVPlayerState
)
playerState
{
FTXLOGI
(
@"[%@]setPlayerState,playerState:%ld"
,
kPipTag
,
(
long
)
playerState
);
if
(
self
.
playerDelegate
!=
nil
)
{
[
self
.
playerDelegate
playerStateDidChange
:
playerState
];
}
self
->
_playerState
=
playerState
;
}
-
(
void
)
setLoopback
:
(
BOOL
)
isLoop
{
FTXLOGI
(
@"[%@]setLoopback,isLoop:%i"
,
kPipTag
,
isLoop
);
self
.
isLoopBack
=
isLoop
;
}
-
(
AVPlayerLayer
*
)
getPlayerLayer
{
return
self
.
playerLayer
;
}
-
(
void
)
reset
{
[
self
.
playerLayer
removeFromSuperlayer
];
[
self
.
playerLayer
setPlayer
:
nil
];
[
self
removeKVOWithPlayer
:
self
.
avPlayer
];
self
.
isLoopBack
=
NO
;
self
.
playerState
=
FTXAVPlayerStateIdle
;
self
.
avPlayer
=
nil
;
self
.
playerLayer
=
nil
;
}
-
(
void
)
observeValueForKeyPath
:
(
NSString
*
)
keyPath
ofObject
:
(
id
)
object
change
:
(
NSDictionary
*
)
change
context
:
(
void
*
)
context
{
if
(
object
==
self
.
avPlayer
)
{
if
(
context
==
gTVKAVPlayerKVOContextTimeControlStatus
)
{
// 画中画播放时,才管系统播放器的播放暂停状态。 非画中画情况下,都是主播放器影响系统播放器,不需要系统播放器反过来影响主播放器
[
self
handleAVPlayerTimeControlStatusChanged
];
}
}
}
-
(
int64_t
)
currentPositionMs
{
return
CMTimeGetSeconds
(
self
.
avPlayer
.
currentTime
)
*
USEC_TO_MSEC
;
}
-
(
int64_t
)
durationMs
{
return
(
int64_t
)(
CMTimeGetSeconds
(
self
.
asset
.
duration
)
*
1000
);
}
-
(
void
)
playbackFinished
:
(
NSNotification
*
)
notification
{
if
(
self
.
isLoopBack
)
{
[
self
.
avPlayer
seekToTime
:
CMTimeMake
(
0
,
USEC_PER_SEC
)
toleranceBefore
:
kCMTimeZero
toleranceAfter
:
kCMTimeZero
];
[
self
.
avPlayer
play
];
}
}
-
(
void
)
addKVOWithPlayer
:
(
AVPlayer
*
)
player
{
if
(
player
!=
nil
)
{
[
player
addObserver
:
self
forKeyPath
:
@"error"
options
:
NSKeyValueObservingOptionNew
context
:
gTVKAVPlayerKVOContextError
];
// kvo监听系统播放器的暂停播放状态(之前监听播放速度rate来判断状态,可能不准,监听timeControlStatus是最准的)
if
(
@available
(
iOS
10
.
0
,
macOS
10
.
12
,
*
))
{
[
player
addObserver
:
self
forKeyPath
:
@"timeControlStatus"
options
:
NSKeyValueObservingOptionNew
context
:
gTVKAVPlayerKVOContextTimeControlStatus
];
}
else
{
[
player
addObserver
:
self
forKeyPath
:
@"rate"
options
:
NSKeyValueObservingOptionNew
context
:
gTVKAVPlayerKVOContextTimeControlStatus
];
}
[
player
addObserver
:
self
forKeyPath
:
@"airPlayVideoActive"
options
:
NSKeyValueObservingOptionNew
context
:
gTVKAVPlayerKVOContextAirplay
];
[
player
addObserver
:
self
forKeyPath
:
@"status"
options
:
NSKeyValueObservingOptionInitial
|
NSKeyValueObservingOptionNew
context
:
gTVKAVPlayerItemKVOContextState
];
}
}
-
(
void
)
removeKVOWithPlayer
:
(
AVPlayer
*
)
player
{
if
(
player
!=
nil
)
{
[
player
removeObserver
:
self
forKeyPath
:
@"error"
];
// kvo监听系统播放器的暂停播放状态(之前监听播放速度rate来判断状态,可能不准,监听timeControlStatus是最准的)
if
(
@available
(
iOS
10
.
0
,
macOS
10
.
12
,
*
))
{
[
player
removeObserver
:
self
forKeyPath
:
@"timeControlStatus"
];
}
else
{
[
player
removeObserver
:
self
forKeyPath
:
@"rate"
];
}
[
player
removeObserver
:
self
forKeyPath
:
@"airPlayVideoActive"
];
[
player
removeObserver
:
self
forKeyPath
:
@"status"
];
}
}
-
(
void
)
handleAVPlayerTimeControlStatusChanged
{
BOOL
isPlayingState
=
NO
;
if
(
@available
(
iOS
10
.
0
,
macOS
10
.
12
,
*
))
{
isPlayingState
=
self
.
avPlayer
.
timeControlStatus
==
AVPlayerTimeControlStatusPlaying
||
self
.
avPlayer
.
timeControlStatus
==
AVPlayerTimeControlStatusWaitingToPlayAtSpecifiedRate
;
}
else
{
isPlayingState
=
(
self
.
avPlayer
.
rate
!=
0
);
}
if
(
isPlayingState
&&
self
.
playerState
!=
FTXAVPlayerStatePlaying
)
{
FTXLOGI
(
@"[%@]playerStateDidChange:playing"
,
kPipTag
);
self
.
playerState
=
FTXAVPlayerStatePlaying
;
}
else
if
(
!
isPlayingState
&&
self
.
playerState
==
FTXAVPlayerStatePlaying
)
{
FTXLOGI
(
@"[%@]playerStateDidChange:paused"
,
kPipTag
);
// 换源之后,播放完成前一刻,监听系统播放器TimeControlStatus属性变为暂停状态,这不是用户调用的暂停,不需要回抛暂停
if
(
self
.
currentPositionMs
==
self
.
durationMs
)
{
FTXLOGI
(
@"[%@]playerStateDidChange:complete, duration:%lld == currentPosition:%lld"
,
kPipTag
,
self
.
durationMs
,
self
.
currentPositionMs
);
return
;
}
self
.
playerState
=
FTXAVPlayerStatePaused
;
}
}
@end
Flutter/ios/Classes/live/pip/FTXPipCaller.h
0 → 100644
浏览文件 @
8ea0a67a
// Copyright (c) 2024 Tencent. All rights reserved.
#ifndef SUPERPLAYER_FLUTTER_IOS_CLASSES_LIVE_PIP_FTXPIPCALLER_H_
#define SUPERPLAYER_FLUTTER_IOS_CLASSES_LIVE_PIP_FTXPIPCALLER_H_
#import "FTXLiteAVSDKHeader.h"
#import "FTXLivePipDelegate.h"
#import "FTXPipPlayerDelegate.h"
#import "FTXPipRenderView.h"
@protocol
FTXPipCaller
<
NSObject
>
@property
(
nonatomic
,
strong
)
id
<
FTXLivePipDelegate
>
pipDelegate
;
@property
(
nonatomic
,
strong
)
id
<
FTXPipPlayerDelegate
>
playerDelegate
;
-
(
int
)
handleStartPip
:(
CGSize
)
size
;
-
(
void
)
exitPip
;
-
(
FTXPipRenderView
*
)
getVideoView
;
-
(
TX_VOD_PLAYER_PIP_STATE
)
getStatus
;
-
(
void
)
pausePipVideo
;
-
(
void
)
resumePipVideo
;
-
(
void
)
displayPixelBuffer
:(
CVPixelBufferRef
)
pixelBuffer
;
@end
#endif // SUPERPLAYER_FLUTTER_IOS_CLASSES_LIVE_PIP_FTXPIPCALLER_H_
Flutter/ios/Classes/live/pip/FTXPipController.h
0 → 100644
浏览文件 @
8ea0a67a
// Copyright (c) 2024 Tencent. All rights reserved.
#ifndef SUPERPLAYER_FLUTTER_IOS_CLASSES_LIVE_PIP_FTXPIPCONTROLLER_H_
#define SUPERPLAYER_FLUTTER_IOS_CLASSES_LIVE_PIP_FTXPIPCONTROLLER_H_
#import <UIKit/UIKit.h>
#import <AVFoundation/AVFoundation.h>
#import "FTXLiteAVSDKHeader.h"
#import "FTXLivePipDelegate.h"
#import "FTXPipPlayerDelegate.h"
@interface
FTXPipController
:
NSObject
@property
(
nonatomic
,
strong
)
id
<
FTXLivePipDelegate
>
pipDelegate
;
@property
(
nonatomic
,
strong
)
id
<
FTXPipPlayerDelegate
>
playerDelegate
;
+
(
instancetype
)
shareInstance
;
-
(
int
)
startOpenPip
:(
V2TXLivePlayer
*
)
livePlayer
withSize
:(
CGSize
)
size
;
-
(
void
)
pausePipVideo
;
-
(
void
)
resumePipVideo
;
-
(
void
)
exitPip
;
-
(
void
)
displayPixelBuffer
:(
CVPixelBufferRef
)
pixelBuffer
;
@end
#endif // SUPERPLAYER_FLUTTER_IOS_CLASSES_LIVE_PIP_FTXPIPCONTROLLER_H_
Flutter/ios/Classes/live/pip/FTXPipController.m
0 → 100644
浏览文件 @
8ea0a67a
// Copyright (c) 2024 Tencent. All rights reserved.
#import <Foundation/Foundation.h>
#import "FTXPipController.h"
#import "FTXEvent.h"
#import "FTXPipCaller.h"
#import "FTXPipFactory.h"
#import "TXPipAuth.h"
#import "FTXLog.h"
#import <CoreVideo/CoreVideo.h>
@interface
FTXPipController
()
@property
(
nonatomic
,
strong
)
id
<
FTXPipCaller
>
pipImpl
;
@property
(
nonatomic
,
strong
)
FTXPipFactory
*
pipFactory
;
@property
(
atomic
,
strong
)
NSObject
*
controlLock
;
@end
static
NSString
*
kPipTag
=
@"FTXPipController"
;
static
FTXPipController
*
_shareInstance
=
nil
;
@implementation
FTXPipController
+
(
instancetype
)
shareInstance
{
static
dispatch_once_t
predicate
;
dispatch_once
(
&
predicate
,
^
{
_shareInstance
=
[[
FTXPipController
alloc
]
init
];
});
return
_shareInstance
;
}
-
(
instancetype
)
init
{
self
=
[
super
init
];
if
(
self
)
{
self
.
pipFactory
=
[[
FTXPipFactory
alloc
]
init
];
self
.
controlLock
=
[[
NSObject
alloc
]
init
];
}
return
self
;
}
-
(
int
)
startOpenPip
:
(
V2TXLivePlayer
*
)
livePlayer
withSize
:
(
CGSize
)
size
{
if
(
!
[
TXPipAuth
cpa
])
{
FTXLOGE
(
@"%@ pip auth is deined when enter"
,
kPipTag
);
if
(
nil
!=
self
.
pipDelegate
)
{
[
self
.
pipDelegate
pictureInPictureErrorDidOccur
:
FTX_VOD_PLAYER_PIP_ERROR_TYPE_PIP_AUTH_DENIED
];
}
return
ERROR_PIP_AUTH_DENIED
;
}
if
(
!
[
TXVodPlayer
isSupportPictureInPicture
])
{
FTXLOGE
(
@"%@ pip is not support"
,
kPipTag
);
if
(
nil
!=
self
.
pipDelegate
)
{
[
self
.
pipDelegate
pictureInPictureErrorDidOccur
:
FTX_VOD_PLAYER_PIP_ERROR_TYPE_DEVICE_NOT_SUPPORT
];
}
return
ERROR_IOS_PIP_DEVICE_NOT_SUPPORT
;
}
if
(
nil
!=
self
.
pipImpl
)
{
TX_VOD_PLAYER_PIP_STATE
status
=
[
self
.
pipImpl
getStatus
];
if
(
status
==
TX_VOD_PLAYER_PIP_STATE_WILL_START
||
status
==
TX_VOD_PLAYER_PIP_STATE_DID_START
)
{
FTXLOGE
(
@"%@ pip is running when enter, status %ld"
,
kPipTag
,
(
long
)
status
);
if
(
nil
!=
self
.
pipDelegate
)
{
[
self
.
pipDelegate
pictureInPictureErrorDidOccur
:
FTX_VOD_PLAYER_PIP_ERROR_TYPE_PIP_IS_RUNNING
];
}
return
ERROR_IOS_PIP_IS_RUNNING
;
}
}
self
.
pipImpl
=
[
self
.
pipFactory
createPipCaller
];
if
(
self
.
pipDelegate
!=
nil
)
{
self
.
pipImpl
.
pipDelegate
=
self
.
pipDelegate
;
}
if
(
self
.
playerDelegate
!=
nil
)
{
self
.
pipImpl
.
playerDelegate
=
self
.
playerDelegate
;
}
int
retCode
=
[
self
.
pipImpl
handleStartPip
:
size
];
return
retCode
;
}
-
(
void
)
setPipDelegate
:
(
id
<
FTXLivePipDelegate
>
)
pipDelegate
{
if
(
nil
!=
self
.
pipImpl
)
{
self
.
pipImpl
.
pipDelegate
=
pipDelegate
;
}
self
->
_pipDelegate
=
pipDelegate
;
}
-
(
void
)
setPlayerDelegate
:
(
id
<
FTXPipPlayerDelegate
>
)
playerDelegate
{
if
(
nil
!=
self
.
pipImpl
)
{
self
.
pipImpl
.
playerDelegate
=
playerDelegate
;
}
self
->
_playerDelegate
=
playerDelegate
;
}
-
(
void
)
exitPip
{
@synchronized
(
self
.
controlLock
)
{
if
(
nil
!=
self
.
pipImpl
)
{
[
self
.
pipImpl
exitPip
];
self
.
pipImpl
=
nil
;
self
.
pipDelegate
=
nil
;
self
.
playerDelegate
=
nil
;
}
}
}
-
(
void
)
pausePipVideo
{
if
(
nil
!=
self
.
pipImpl
)
{
[
self
.
pipImpl
pausePipVideo
];
}
}
-
(
void
)
resumePipVideo
{
if
(
nil
!=
self
.
pipImpl
)
{
[
self
.
pipImpl
resumePipVideo
];
}
}
-
(
void
)
displayPixelBuffer
:
(
CVPixelBufferRef
)
pixelBuffer
{
@synchronized
(
self
.
controlLock
)
{
if
(
!
pixelBuffer
||
pixelBuffer
==
NULL
)
{
NSLog
(
@"Invalid CVPixelBufferRef"
);
return
;
}
if
(
nil
!=
self
.
pipImpl
)
{
[
self
.
pipImpl
displayPixelBuffer
:
pixelBuffer
];
}
}
}
@end
Flutter/ios/Classes/live/pip/FTXPipFactory.h
0 → 100644
浏览文件 @
8ea0a67a
// Copyright (c) 2024 Tencent. All rights reserved.
#ifndef SUPERPLAYER_FLUTTER_IOS_CLASSES_LIVE_PIP_FTXPIPFACTORY_H_
#define SUPERPLAYER_FLUTTER_IOS_CLASSES_LIVE_PIP_FTXPIPFACTORY_H_
#import <Foundation/Foundation.h>
#import "FTXPipCaller.h"
NS_ASSUME_NONNULL_BEGIN
@interface
FTXPipFactory
:
NSObject
-
(
id
<
FTXPipCaller
>
)
createPipCaller
;
@end
NS_ASSUME_NONNULL_END
#endif // SUPERPLAYER_FLUTTER_IOS_CLASSES_LIVE_PIP_FTXPIPFACTORY_H_
Flutter/ios/Classes/live/pip/FTXPipFactory.m
0 → 100644
浏览文件 @
8ea0a67a
// Copyright (c) 2024 Tencent. All rights reserved.
#import "FTXPipFactory.h"
#import "FTXPipGlobalImpl.h"
@implementation
FTXPipFactory
-
(
instancetype
)
init
{
self
=
[
super
init
];
if
(
self
)
{
}
return
self
;
}
-
(
nonnull
id
<
FTXPipCaller
>
)
createPipCaller
{
id
<
FTXPipCaller
>
pipCalled
=
[[
FTXPipGlobalImpl
alloc
]
init
];
return
pipCalled
;
}
@end
Flutter/ios/Classes/live/pip/FTXPipGlobalImpl.h
0 → 100644
浏览文件 @
8ea0a67a
// Copyright (c) 2024 Tencent. All rights reserved.
#ifndef SUPERPLAYER_FLUTTER_IOS_CLASSES_LIVE_PIP_FTXPIPGLOBALIMPL_H_
#define SUPERPLAYER_FLUTTER_IOS_CLASSES_LIVE_PIP_FTXPIPGLOBALIMPL_H_
#import <Foundation/Foundation.h>
#import "FTXPipCaller.h"
NS_ASSUME_NONNULL_BEGIN
@interface
FTXPipGlobalImpl
:
NSObject
<
FTXPipCaller
>
@end
NS_ASSUME_NONNULL_END
#endif // SUPERPLAYER_FLUTTER_IOS_CLASSES_LIVE_PIP_FTXPIPGLOBALIMPL_H_
Flutter/ios/Classes/live/pip/FTXPipGlobalImpl.m
0 → 100644
浏览文件 @
8ea0a67a
差异被折叠。
点击展开。
Flutter/ios/Classes/live/pip/FTXPipPlayerDelegate.h
0 → 100644
浏览文件 @
8ea0a67a
// Copyright (c) 2024 Tencent. All rights reserved.
#ifndef SUPERPLAYER_FLUTTER_IOS_CLASSES_LIVE_PIP_FTXPIPPLAYERDELEGATE_H_
#define SUPERPLAYER_FLUTTER_IOS_CLASSES_LIVE_PIP_FTXPIPPLAYERDELEGATE_H_
#import "FTXPlayerConstants.h"
@protocol
FTXPipPlayerDelegate
<
NSObject
>
-
(
void
)
playerStateDidChange
:
(
FTXAVPlayerState
)
playerState
;
@end
#endif // SUPERPLAYER_FLUTTER_IOS_CLASSES_LIVE_PIP_FTXPIPPLAYERDELEGATE_H_
Flutter/ios/Classes/live/pip/FTXPipRenderView.h
0 → 100644
浏览文件 @
8ea0a67a
// Copyright (c) 2024 Tencent. All rights reserved.
#ifndef SUPERPLAYER_FLUTTER_IOS_CLASSES_LIVE_PIP_FTXPIPRENDERVIEW_H_
#define SUPERPLAYER_FLUTTER_IOS_CLASSES_LIVE_PIP_FTXPIPRENDERVIEW_H_
#import <Foundation/Foundation.h>
#import "FTXBackPlayer.h"
NS_ASSUME_NONNULL_BEGIN
@interface
FTXPipRenderView
:
UIView
-
(
void
)
displayPixelBuffer
:(
CVPixelBufferRef
)
pixelBuffer
;
@end
NS_ASSUME_NONNULL_END
#endif // SUPERPLAYER_FLUTTER_IOS_CLASSES_LIVE_PIP_FTXPIPRENDERVIEW_H_
Flutter/ios/Classes/live/pip/FTXPipRenderView.m
0 → 100644
浏览文件 @
8ea0a67a
// Copyright (c) 2024 Tencent. All rights reserved.
#import "FTXPipRenderView.h"
#import "FTXImgTools.h"
@interface
FTXPipRenderView
()
@property
(
nonatomic
,
strong
)
CALayer
*
videoLayer
;
@end
@implementation
FTXPipRenderView
-
(
instancetype
)
initWithFrame
:(
CGRect
)
frame
{
self
=
[
super
initWithFrame
:
frame
];
if
(
self
)
{
self
.
videoLayer
=
[
CALayer
layer
];
self
.
videoLayer
.
frame
=
self
.
bounds
;
[
self
.
layer
addSublayer
:
self
.
videoLayer
];
}
return
self
;
}
-
(
void
)
displayPixelBuffer
:(
CVPixelBufferRef
)
pixelBuffer
{
CIImage
*
ciImage
=
[
FTXImgTools
ciImageFromPixelBuffer
:
pixelBuffer
];
// 创建一个CIContext
CIContext
*
context
=
[
CIContext
contextWithOptions
:
nil
];
// 将CIImage渲染到CGImage
CGImageRef
cgImage
=
[
context
createCGImage
:
ciImage
fromRect
:
ciImage
.
extent
];
// 更新CALayer的内容
self
.
videoLayer
.
contents
=
(
__bridge
id
_Nullable
)(
cgImage
);
// 释放CGImage
CGImageRelease
(
cgImage
);
}
-
(
void
)
layoutSubviews
{
[
super
layoutSubviews
];
CGSize
oldSize
=
self
.
videoLayer
.
frame
.
size
;
CGRect
newRect
=
self
.
frame
;
CGSize
newSize
=
newRect
.
size
;
if
(
oldSize
.
width
!=
newSize
.
width
||
oldSize
.
height
!=
newSize
.
height
)
{
self
.
videoLayer
.
frame
=
newRect
;
}
}
@end
Flutter/ios/Classes/tools/FTXImgTools.h
0 → 100644
浏览文件 @
8ea0a67a
// Copyright (c) 2024 Tencent. All rights reserved.
#ifndef SUPERPLAYER_FLUTTER_IOS_CLASSES_TOOLS_FTXIMGTOOLS_H_
#define SUPERPLAYER_FLUTTER_IOS_CLASSES_TOOLS_FTXIMGTOOLS_H_
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface
FTXImgTools
:
NSObject
+
(
CVPixelBufferRef
)
CVPixelBufferRefFromUiImage
:(
UIImage
*
)
img
;
+
(
CIImage
*
)
ciImageFromPixelBuffer
:(
CVPixelBufferRef
)
pixelBuffer
;
@end
NS_ASSUME_NONNULL_END
#endif // SUPERPLAYER_FLUTTER_IOS_CLASSES_TOOLS_FTXIMGTOOLS_H_
Flutter/ios/Classes/tools/FTXImgTools.m
0 → 100644
浏览文件 @
8ea0a67a
// Copyright (c) 2024 Tencent. All rights reserved.
#import "FTXImgTools.h"
@implementation
FTXImgTools
+
(
CVPixelBufferRef
)
CVPixelBufferRefFromUiImage
:(
UIImage
*
)
img
{
CGSize
size
=
img
.
size
;
CGImageRef
image
=
[
img
CGImage
];
BOOL
hasAlpha
=
CGImageRefContainsAlpha
(
image
);
CFDictionaryRef
empty
=
CFDictionaryCreate
(
kCFAllocatorDefault
,
NULL
,
NULL
,
0
,
&
kCFTypeDictionaryKeyCallBacks
,
&
kCFTypeDictionaryValueCallBacks
);
NSDictionary
*
options
=
[
NSDictionary
dictionaryWithObjectsAndKeys
:
[
NSNumber
numberWithBool
:
YES
],
kCVPixelBufferCGImageCompatibilityKey
,
[
NSNumber
numberWithBool
:
YES
],
kCVPixelBufferCGBitmapContextCompatibilityKey
,
empty
,
kCVPixelBufferIOSurfacePropertiesKey
,
nil
];
CVPixelBufferRef
pxbuffer
=
NULL
;
CVReturn
status
=
CVPixelBufferCreate
(
kCFAllocatorDefault
,
size
.
width
,
size
.
height
,
inputPixelFormat
(),
(
__bridge
CFDictionaryRef
)
options
,
&
pxbuffer
);
NSParameterAssert
(
status
==
kCVReturnSuccess
&&
pxbuffer
!=
NULL
);
CVPixelBufferLockBaseAddress
(
pxbuffer
,
0
);
void
*
pxdata
=
CVPixelBufferGetBaseAddress
(
pxbuffer
);
NSParameterAssert
(
pxdata
!=
NULL
);
CGColorSpaceRef
rgbColorSpace
=
CGColorSpaceCreateDeviceRGB
();
uint32_t
bitmapInfo
=
bitmapInfoWithPixelFormatType
(
inputPixelFormat
(),
(
bool
)
hasAlpha
);
CGContextRef
context
=
CGBitmapContextCreate
(
pxdata
,
size
.
width
,
size
.
height
,
8
,
CVPixelBufferGetBytesPerRow
(
pxbuffer
),
rgbColorSpace
,
bitmapInfo
);
NSParameterAssert
(
context
);
CGContextDrawImage
(
context
,
CGRectMake
(
0
,
0
,
CGImageGetWidth
(
image
),
CGImageGetHeight
(
image
)),
image
);
CVPixelBufferUnlockBaseAddress
(
pxbuffer
,
0
);
CGColorSpaceRelease
(
rgbColorSpace
);
CGContextRelease
(
context
);
return
pxbuffer
;
}
+
(
CIImage
*
)
ciImageFromPixelBuffer
:(
CVPixelBufferRef
)
pixelBuffer
{
CIImage
*
ciImage
=
[
CIImage
imageWithCVPixelBuffer
:
pixelBuffer
];
return
ciImage
;
}
static
OSType
inputPixelFormat
(
void
){
return
kCVPixelFormatType_32BGRA
;
}
static
uint32_t
bitmapInfoWithPixelFormatType
(
OSType
inputPixelFormat
,
bool
hasAlpha
){
if
(
inputPixelFormat
==
kCVPixelFormatType_32BGRA
)
{
uint32_t
bitmapInfo
=
kCGImageAlphaPremultipliedFirst
|
kCGBitmapByteOrder32Host
;
if
(
!
hasAlpha
)
{
bitmapInfo
=
kCGImageAlphaNoneSkipFirst
|
kCGBitmapByteOrder32Host
;
}
return
bitmapInfo
;
}
else
if
(
inputPixelFormat
==
kCVPixelFormatType_32ARGB
)
{
uint32_t
bitmapInfo
=
kCGImageAlphaPremultipliedFirst
|
kCGBitmapByteOrder32Big
;
return
bitmapInfo
;
}
else
{
return
0
;
}
}
// Check alpha value
BOOL
CGImageRefContainsAlpha
(
CGImageRef
imageRef
)
{
if
(
!
imageRef
)
{
return
NO
;
}
CGImageAlphaInfo
alphaInfo
=
CGImageGetAlphaInfo
(
imageRef
);
BOOL
hasAlpha
=
!
(
alphaInfo
==
kCGImageAlphaNone
||
alphaInfo
==
kCGImageAlphaNoneSkipFirst
||
alphaInfo
==
kCGImageAlphaNoneSkipLast
);
return
hasAlpha
;
}
@end
Flutter/lib/Core/txliveplayer_controller.dart
浏览文件 @
8ea0a67a
...
...
@@ -281,20 +281,13 @@ class TXLivePlayerController extends ChangeNotifier implements ValueListenable<T
{
String
?
backIconForAndroid
,
String
?
playIconForAndroid
,
String
?
pauseIconForAndroid
,
String
?
forwardIconForAndroid
})
async
{
if
(
_isNeedDisposed
)
return
-
1
;
await
_initPlayer
.
future
;
if
(
defaultTargetPlatform
==
TargetPlatform
.
android
)
{
IntMsg
intMsg
=
await
_livePlayerApi
.
enterPictureInPictureMode
(
PipParamsPlayerMsg
()
..
backIconForAndroid
=
backIconForAndroid
..
playIconForAndroid
=
playIconForAndroid
..
pauseIconForAndroid
=
pauseIconForAndroid
..
forwardIconForAndroid
=
forwardIconForAndroid
..
playerId
=
_playerId
);
return
intMsg
.
value
??
-
1
;
}
else
if
(
defaultTargetPlatform
==
TargetPlatform
.
iOS
)
{
// The background picture-in-picture feature for ios steaming live is temporarily disabled.
return
-
1
;
}
else
{
return
-
1
;
}
IntMsg
intMsg
=
await
_livePlayerApi
.
enterPictureInPictureMode
(
PipParamsPlayerMsg
()
..
backIconForAndroid
=
backIconForAndroid
..
playIconForAndroid
=
playIconForAndroid
..
pauseIconForAndroid
=
pauseIconForAndroid
..
forwardIconForAndroid
=
forwardIconForAndroid
..
playerId
=
_playerId
);
return
intMsg
.
value
??
-
1
;
}
/// Exit picture-in-picture mode if the player is in picture-in-picture mode.
...
...
Flutter/lib/Core/txplayer_define.dart
浏览文件 @
8ea0a67a
...
...
@@ -374,6 +374,12 @@ abstract class TXVodPlayEvent {
// PIP error, PIP function is not started (only support iOS).
// pip 错误,PIP功能没有启动 only support iOS
static
const
ERROR_IOS_PIP_NOT_RUNNING
=
-
111
;
// PIP start time out
// PIP 启动超时
static
const
ERROR_IOS_PIP_START_TIME_OUT
=
-
112
;
// Insufficient permissions, currently only appears in Picture-in-Picture live streaming
// 权限不足,目前只出现在直播画中画
static
const
ERROR_PIP_AUTH_DENIED
=
-
201
;
// PIP error, currently unable to enter PIP mode, such as being in full screen mode.
// pip 错误,当前不能进入pip模式,例如正处于全屏模式下
static
const
ERROR_PIP_CAN_NOT_ENTER
=
-
120
;
...
...
FlutterWidget/superplayer_widget/lib/superplayer_controller.dart
浏览文件 @
8ea0a67a
...
...
@@ -822,24 +822,11 @@ class SuperPlayerController {
Future
<
int
>
enterPictureInPictureMode
(
{
String
?
backIcon
,
String
?
playIcon
,
String
?
pauseIcon
,
String
?
forwardIcon
})
async
{
if
(
_playerUIStatus
==
SuperPlayerUIStatus
.
WINDOW_MODE
)
{
if
(
playerType
==
SuperPlayerType
.
VOD
)
{
return
TXPipController
.
instance
.
enterPip
(
_vodPlayerController
,
_context
,
backIconForAndroid:
backIcon
,
playIconForAndroid:
playIcon
,
pauseIconForAndroid:
pauseIcon
,
forwardIconForAndroid:
forwardIcon
);
}
else
{
if
(
defaultTargetPlatform
==
TargetPlatform
.
android
)
{
return
TXPipController
.
instance
.
enterPip
(
_livePlayerController
,
_context
,
backIconForAndroid:
backIcon
,
playIconForAndroid:
playIcon
,
pauseIconForAndroid:
pauseIcon
,
forwardIconForAndroid:
forwardIcon
);
}
else
if
(
defaultTargetPlatform
==
TargetPlatform
.
iOS
)
{
TXPipController
.
instance
.
exitAndReleaseCurrentPip
();
return
_livePlayerController
.
enterPictureInPictureMode
();
}
}
return
TXPipController
.
instance
.
enterPip
(
getCurrentController
(),
_context
,
backIconForAndroid:
backIcon
,
playIconForAndroid:
playIcon
,
pauseIconForAndroid:
pauseIcon
,
forwardIconForAndroid:
forwardIcon
);
}
return
TXVodPlayEvent
.
ERROR_PIP_CAN_NOT_ENTER
;
}
...
...
编写
预览
Markdown
格式
0%
重试
或
添加新文件
添加附件
取消
您添加了
0
人
到此讨论。请谨慎行事。
请先完成此评论的编辑!
取消
请
注册
或者
登录
后发表评论