提交 c3e34940 authored 作者: kongdywang's avatar kongdywang

fix ios miss first frame error & fix player bug

上级 9feccf1d
......@@ -68,7 +68,7 @@ public class FTXLivePlayer extends FTXBasePlayer implements MethodChannel.Method
* 直播播放器
*/
public FTXLivePlayer(FlutterPlugin.FlutterPluginBinding flutterPluginBinding, Activity activity,
FTXPIPManager pipManager) {
FTXPIPManager pipManager) {
super();
mFlutterPluginBinding = flutterPluginBinding;
mActivity = activity;
......@@ -184,7 +184,7 @@ public class FTXLivePlayer extends FTXBasePlayer implements MethodChannel.Method
result.success(null);
} else if (call.method.equals("startLivePlay")) {
String url = call.argument("url");
int type = call.argument("playType");
Integer type = call.argument("playType");
int r = startLivePlay(url, type);
result.success(r);
} else if (call.method.equals("stop")) {
......@@ -248,7 +248,7 @@ public class FTXLivePlayer extends FTXBasePlayer implements MethodChannel.Method
mPipManager.toAndroidPath(playResumeAssetPath),
mPipManager.toAndroidPath(playPauseAssetPath),
mPipManager.toAndroidPath(playForwardAssetPath),
getPlayerId(),false, false, true);
getPlayerId(), false, false, true);
mPipParams.setIsPlaying(isPlaying());
int pipResult = mPipManager.enterPip(mPipParams, mVideoModel);
// 启动成功之后,暂停当前界面视频
......@@ -277,8 +277,11 @@ public class FTXLivePlayer extends FTXBasePlayer implements MethodChannel.Method
return mSurfaceTextureEntry == null ? -1 : mSurfaceTextureEntry.id();
}
int startLivePlay(String url, int type) {
int startLivePlay(String url, Integer type) {
Log.d(TAG, "startLivePlay:");
if (null == type) {
type = TXLivePlayer.PLAY_TYPE_LIVE_FLV;
}
mVideoModel.setVideoUrl(url);
mVideoModel.setLiveType(type);
if (mLivePlayer != null) {
......
......@@ -47,17 +47,16 @@ class _DemoShortVideoPlayerState extends State<DemoShortVideoPlayer> with Widget
children: widgetList,
),
SafeArea(
child: Positioned(
left: 0,
top: 0,
child: InkWell(
onTap: _onBackTap,
child: const Image(
width: 40,
height: 40,
image: AssetImage("images/superplayer_btn_back_play.png", package: StringResource.PKG_NAME),
),
))),
child: Container(
child: InkWell(
onTap: _onBackTap,
child: const Image(
width: 40,
height: 40,
image: AssetImage("images/superplayer_btn_back_play.png", package: StringResource.PKG_NAME),
),
),
)),
],
);
}
......
......@@ -6,6 +6,7 @@ import 'package:flutter_easyloading/flutter_easyloading.dart';
import 'package:super_player/super_player.dart';
import 'package:super_player_example/ui/demo_inputdialog.dart';
import 'package:superplayer_widget/demo_superplayer_lib.dart';
import 'dart:ui';
/// flutter superplayer demo
class DemoSuperPlayer extends StatefulWidget {
......@@ -22,6 +23,7 @@ class _DemoSuperPlayerState extends State<DemoSuperPlayer> with TXPipPlayerResto
static const ARGUMENT_TYPE_POS = "arg_type_pos";
static const ARGUMENT_VIDEO_DATA = "arg_video_data";
static const sPlayerViewDisplayRatio = 720.0 / 1280.0; //当前界面播放器view展示的宽高比,用主流的16:9
List<SuperPlayerModel> videoModels = [];
bool _isFullScreen = false;
......@@ -34,6 +36,7 @@ class _DemoSuperPlayerState extends State<DemoSuperPlayer> with TXPipPlayerResto
double? initStartTime;
TextStyle _textStyleSelected = new TextStyle(fontSize: 16, color: Colors.white);
TextStyle _textStyleUnSelected = new TextStyle(fontSize: 16, color: Colors.grey);
double playerHeight = 220;
@override
void initState() {
......@@ -59,8 +62,9 @@ class _DemoSuperPlayerState extends State<DemoSuperPlayer> with TXPipPlayerResto
if (null != widget.initParams) {
tabSelectPos = widget.initParams![ARGUMENT_TYPE_POS];
initVideoModel = widget.initParams![ARGUMENT_VIDEO_DATA];
initStartTime = widget.initParams![TXPipController.ARGUMENT_PIP_START_TIME];
initStartTime = widget.initParams![TXPipController.ARGUMENT_PIP_START_TIME];
}
_adjustSuperPlayerViewHeight();
if (tabSelectPos == 0) {
_getLiveListData();
} else if (tabSelectPos == 1) {
......@@ -68,6 +72,11 @@ class _DemoSuperPlayerState extends State<DemoSuperPlayer> with TXPipPlayerResto
}
}
/// 竖屏下,播放器始终保持 16 :9的宽高比,优先保证宽度填充
void _adjustSuperPlayerViewHeight() {
playerHeight = (window.physicalSize.width / window.devicePixelRatio) * sPlayerViewDisplayRatio;
}
@override
Widget build(BuildContext context) {
return WillPopScope(
......@@ -136,7 +145,7 @@ class _DemoSuperPlayerState extends State<DemoSuperPlayer> with TXPipPlayerResto
Widget _getPlayArea() {
return Container(
decoration: BoxDecoration(color: Colors.black),
height: 220,
height: playerHeight,
child: SuperPlayerView(_controller),
);
}
......@@ -203,6 +212,7 @@ class _DemoSuperPlayerState extends State<DemoSuperPlayer> with TXPipPlayerResto
if (url.isNotEmpty) {
model.videoURL = url;
playCurrentModel(model, 0);
_addVideoToCurrentList(model);
} else if (appId != 0 && fileId.isNotEmpty) {
model.videoId = new SuperPlayerVideoId();
model.videoId!.fileId = fileId;
......@@ -210,9 +220,7 @@ class _DemoSuperPlayerState extends State<DemoSuperPlayer> with TXPipPlayerResto
model.videoId!.psign = pSign;
}
loader.getVideoData(model, (resultModel) {
setState(() {
videoModels.add(resultModel);
});
_addVideoToCurrentList(resultModel);
playCurrentModel(resultModel, 0);
});
} else {
......@@ -225,6 +233,12 @@ class _DemoSuperPlayerState extends State<DemoSuperPlayer> with TXPipPlayerResto
});
}
void _addVideoToCurrentList(SuperPlayerModel model) {
setState(() {
videoModels.add(model);
});
}
void playCurrentModel(SuperPlayerModel model, double startTime) {
currentVideoModel = model;
_controller.setStartTime(startTime);
......
......@@ -20,8 +20,7 @@ class _DemoTXLivelayerState extends State<DemoTXLivePlayer> with WidgetsBindingO
double _progress = 0.0;
int _volume = 100;
bool _isMute = false;
String _url =
"http://liteavapp.qcloud.com/live/liteavdemoplayerstreamid_demo1080p.flv";
String _url = "http://liteavapp.qcloud.com/live/liteavdemoplayerstreamid_demo1080p.flv";
bool _isStop = true;
bool _isPlaying = false;
double _maxLiveProgressTime = 0;
......@@ -42,12 +41,12 @@ class _DemoTXLivelayerState extends State<DemoTXLivePlayer> with WidgetsBindingO
_progress = event["EVT_PLAY_PROGRESS"].toDouble();
_maxLiveProgressTime = _progress >= _maxLiveProgressTime ? _progress : _maxLiveProgressTime;
progressSliderKey.currentState?.updateProgress(1, _maxLiveProgressTime);
} else if (event["event"] == TXVodPlayEvent.PLAY_EVT_PLAY_BEGIN ||
event["event"] == TXVodPlayEvent.PLAY_EVT_RCV_FIRST_I_FRAME) {
} else if (event["event"] == TXVodPlayEvent.PLAY_EVT_RCV_FIRST_I_FRAME) {
//首帧出现
_isStop = false;
_isPlaying = true;
EasyLoading.dismiss();
_resizeVideo(event);
} else if (event["event"] == TXVodPlayEvent.PLAY_EVT_STREAM_SWITCH_SUCC) {
//切换流成功
EasyLoading.dismiss();
......@@ -60,24 +59,18 @@ class _DemoTXLivelayerState extends State<DemoTXLivePlayer> with WidgetsBindingO
EasyLoading.dismiss();
EasyLoading.showError("切流失败");
switchUrl();
}else if(event["event"] == TXVodPlayEvent.PLAY_EVT_CHANGE_RESOLUTION) {
} else if (event["event"] == TXVodPlayEvent.PLAY_EVT_CHANGE_RESOLUTION) {
LogUtils.w("PLAY_EVT_CHANGE_RESOLUTION", event);
_resizeVideo(event);
}
});
playNetEventSubscription = _controller.onPlayerNetStatusBroadcast.listen((event) {
double w = (event[TXVodNetEvent.NET_STATUS_VIDEO_WIDTH]).toDouble();
double h = (event[TXVodNetEvent.NET_STATUS_VIDEO_HEIGHT]).toDouble();
if (w > 0 && h > 0) {
setState(() {
_aspectRatio = 1.0 * w / h;
});
}
playNetEventSubscription = _controller.onPlayerNetStatusBroadcast.listen((event) {
// 订阅状态变化
});
playerStateEventSubscription = _controller.onPlayerState.listen((event) {
//订阅状态变化
// 订阅状态变化
debugPrint("播放状态 ${event!.name}");
});
......@@ -88,6 +81,16 @@ class _DemoTXLivelayerState extends State<DemoTXLivePlayer> with WidgetsBindingO
await _controller.startLivePlay(_url, playType: TXPlayType.LIVE_FLV);
}
void _resizeVideo(Map<dynamic, dynamic> event) {
int? videoWidth = event[TXVodPlayEvent.EVT_VIDEO_WIDTH];
int? videoHeight = event[TXVodPlayEvent.EVT_VIDEO_HEIGHT];
if ((videoWidth != null && videoWidth != 0) && (videoHeight != null && videoHeight != 0)) {
setState(() {
_aspectRatio = 1.0 * videoWidth / videoHeight;
});
}
}
@override
void initState() {
super.initState();
......@@ -104,7 +107,7 @@ class _DemoTXLivelayerState extends State<DemoTXLivePlayer> with WidgetsBindingO
case AppLifecycleState.inactive:
break;
case AppLifecycleState.resumed:
if(_isPlaying) {
if (_isPlaying) {
_controller.resume();
}
break;
......@@ -304,7 +307,7 @@ class _DemoTXLivelayerState extends State<DemoTXLivePlayer> with WidgetsBindingO
showDialog(
context: context,
builder: (context) {
return DemoInputDialog("", 0, "", (String url, int appId, String fileId,String pSign) {
return DemoInputDialog("", 0, "", (String url, int appId, String fileId, String pSign) {
_url = url;
_controller.stop();
if (url.isNotEmpty) {
......
......@@ -44,16 +44,13 @@ class _DemoTXVodPlayerState extends State<DemoTXVodPlayer>
});
LogUtils.logOpen = true;
SuperPlayerPlugin.setGlobalMaxCacheSize(200);
SuperPlayerPlugin.setGlobalCacheFolderPath("postfixPath");
playEventSubscription = _controller.onPlayerEventBroadcast.listen((event) async {
//订阅状态变化
if (event["event"] == TXVodPlayEvent.PLAY_EVT_PLAY_BEGIN ||
event["event"] == TXVodPlayEvent.PLAY_EVT_RCV_FIRST_I_FRAME) {
if (event["event"] == TXVodPlayEvent.PLAY_EVT_RCV_FIRST_I_FRAME) {
EasyLoading.dismiss();
_supportedBitrates = (await _controller.getSupportedBitrates())!;
_isPlaying = true;
_resizeVideo(event);
} else if (event["event"] == TXVodPlayEvent.PLAY_EVT_PLAY_PROGRESS) {
_currentProgress = event[TXVodPlayEvent.EVT_PLAY_PROGRESS].toDouble();
double videoDuration = event[TXVodPlayEvent.EVT_PLAY_DURATION].toDouble(); // 总播放时长,转换后的单位 秒
......@@ -64,6 +61,8 @@ class _DemoTXVodPlayerState extends State<DemoTXVodPlayer>
}
} else if (event["event"] == TXVodPlayEvent.PLAY_EVT_GET_PLAYINFO_SUCC) {
String? playUrl = event[TXVodPlayEvent.EVT_PLAY_URL]?.toString();
} else if(event["event"] == TXVodPlayEvent.PLAY_EVT_CHANGE_RESOLUTION) {
_resizeVideo(event);
}
});
......@@ -89,6 +88,16 @@ class _DemoTXVodPlayerState extends State<DemoTXVodPlayer>
await _controller.startVodPlay(_url);
}
void _resizeVideo(Map<dynamic, dynamic> event) {
int? videoWidth = event[TXVodPlayEvent.EVT_VIDEO_WIDTH];
int? videoHeight = event[TXVodPlayEvent.EVT_VIDEO_HEIGHT];
if ((videoWidth != null && videoWidth != 0) && (videoHeight != null && videoHeight != 0)) {
setState(() {
_aspectRatio = 1.0 * videoWidth / videoHeight;
});
}
}
@override
void initState() {
super.initState();
......@@ -202,26 +211,6 @@ class _DemoTXVodPlayerState extends State<DemoTXVodPlayer>
),
),
),
new GestureDetector(
onTap: () => {_controller.seek(_currentProgress + 10)},
child: Container(
alignment: Alignment.center,
child: Text(
"前进",
style: TextStyle(fontSize: 18, color: Colors.blue),
),
),
),
new GestureDetector(
onTap: () => {_controller.seek(_currentProgress - 10)},
child: Container(
alignment: Alignment.center,
child: Text(
"后退",
style: TextStyle(fontSize: 18, color: Colors.blue),
),
),
),
new GestureDetector(
onTap: () => {onClickSetRate()},
child: Container(
......
......@@ -64,6 +64,9 @@ class _MyAppState extends State<MyApp> {
TXPipController.instance.setNavigatorHandle((params) {
navigatorKey.currentState?.push(MaterialPageRoute(builder: (_) => DemoSuperPlayer(initParams: params)));
});
SuperPlayerPlugin.setGlobalMaxCacheSize(200);
SuperPlayerPlugin.setGlobalCacheFolderPath("postfixPath");
}
Future<void> _getflutterSdkVersion() async {
......
......@@ -41,11 +41,13 @@ static const int CODE_ON_RECEIVE_FIRST_FRAME = 2003;
id<FlutterTextureRegistry> _textureRegistry;
float currentPlayTime;
BOOL volatile isVideoFirstFrameReceived;
NSNumber *videoWidth;
NSNumber *videoHeight;
// 主线程队列,用于保证视频播放部分事件按顺序进行
dispatch_queue_t playerMainqueue;
}
BOOL volatile isStop = false;
- (instancetype)initWithRegistrar:(id<FlutterPluginRegistrar>)registrar
{
if (self = [self init]) {
......@@ -53,6 +55,10 @@ BOOL volatile isStop = false;
_lastBuffer = nil;
_latestPixelBuffer = nil;
_textureId = -1;
isVideoFirstFrameReceived = false;
videoWidth = 0;
videoHeight = 0;
playerMainqueue = dispatch_get_main_queue();
self.hasEnteredPipMode = NO;
self.restoreUI = NO;
_eventSink = [FTXPlayerEventSinkQueue new];
......@@ -190,7 +196,6 @@ BOOL volatile isStop = false;
- (BOOL)stopPlay
{
if (_txVodPlayer != nil) {
isStop = true;
return [_txVodPlayer stopPlay];
}
return NO;
......@@ -517,11 +522,13 @@ BOOL volatile isStop = false;
(void **)&_latestPixelBuffer)) {
pixelBuffer = _latestPixelBuffer;
}
if(isStop && nil != pixelBuffer) {
isStop = false;
[_eventSink success:[FTXVodPlayer getParamsWithEvent:CODE_ON_RECEIVE_FIRST_FRAME withParams:@{}]];
}
return isStop ? nil : pixelBuffer;
dispatch_async(playerMainqueue, ^{
if(!self->isVideoFirstFrameReceived && nil != pixelBuffer) {
[self->_eventSink success:[FTXVodPlayer getParamsWithEvent:CODE_ON_RECEIVE_FIRST_FRAME withParams:@{@"EVT_WIDTH":@(self->videoWidth.intValue), @"EVT_HEIGHT":@(self->videoHeight.intValue)}]];
self->isVideoFirstFrameReceived = true;
}
});
return pixelBuffer;
}
#pragma mark - TXVodPlayListener
......@@ -531,11 +538,24 @@ BOOL volatile isStop = false;
// 交给flutter共享纹理处理首帧事件返回时机
if (EvtID == CODE_ON_RECEIVE_FIRST_FRAME) {
currentPlayTime = 0;
dispatch_async(playerMainqueue, ^{
self->videoWidth = param[@"EVT_WIDTH"];
self->videoHeight = param[@"EVT_HEIGHT"];
});
return;
} else if(EvtID == PLAY_EVT_CHANGE_RESOLUTION) {
dispatch_async(playerMainqueue, ^{
self->videoWidth = param[@"EVT_WIDTH"];
self->videoHeight = param[@"EVT_HEIGHT"];
});
} else if(EvtID == PLAY_EVT_PLAY_PROGRESS) {
currentPlayTime = [param[EVT_PLAY_PROGRESS] floatValue];
} else if(EvtID == PLAY_EVT_PLAY_BEGIN) {
currentPlayTime = 0;
} else if(EvtID == PLAY_EVT_START_VIDEO_DECODER) {
dispatch_async(playerMainqueue, ^{
self->isVideoFirstFrameReceived = false;
});
}
[_eventSink success:[FTXVodPlayer getParamsWithEvent:EvtID withParams:param]];
......
......@@ -83,6 +83,8 @@ abstract class TXVodPlayEvent {
static const EVT_DESCRIPTION = "EVT_MSG"; // 事件说明
static const EVT_PARAM1 = "EVT_PARAM1"; // 事件参数1
static const EVT_PARAM2 = "EVT_PARAM2"; // 事件参数2
static const EVT_VIDEO_WIDTH = "EVT_WIDTH"; // 分辨率之width
static const EVT_VIDEO_HEIGHT = "EVT_HEIGHT"; // 分辨率之height
static const EVT_GET_MSG = "EVT_GET_MSG"; // 消息内容,收到PLAY_EVT_GET_MESSAGE事件时,通过该字段获取消息内容
static const EVT_PLAY_COVER_URL = "EVT_PLAY_COVER_URL"; // 视频封面
static const EVT_PLAY_URL = "EVT_PLAY_URL"; // 视频地址
......
......@@ -57,6 +57,7 @@ class SuperPlayerController {
double _seekPos = 0; // 记录切换硬解时的播放时间
/// 该值会改变新播放视频的播放开始时间点
double startPos = 0;
/// 播放器内核解析出来的视频宽高
double videoWidth = 0;
double videoHeight = 0;
double currentPlayRate = 1.0;
......@@ -148,6 +149,7 @@ class SuperPlayerController {
_updatePlayerState(SuperPlayerState.PLAYING);
break;
case TXVodPlayEvent.PLAY_EVT_RCV_FIRST_I_FRAME: // PLAY_EVT_RCV_FIRST_I_FRAME
_configVideoSize(event);
if (_needToPause) {
return;
}
......@@ -175,23 +177,12 @@ class SuperPlayerController {
_observer?.onPlayProgress(currentDuration, videoDuration, await getPlayableDuration());
}
break;
case TXVodPlayEvent.PLAY_EVT_CHANGE_RESOLUTION:
_configVideoSize(event);
break;
}
});
_vodNetEventListener = _vodPlayerController.onPlayerNetStatusBroadcast.listen((event) {
dynamic wd = (event["VIDEO_WIDTH"]);
dynamic hd = (event["VIDEO_HEIGHT"]);
if (null != wd && null != hd) {
double w = wd.toDouble();
double h = hd.toDouble();
if (w > 0 && h > 0) {
if (w != videoWidth) {
videoWidth = w;
}
if (h != videoHeight) {
videoHeight = h;
}
}
}
_playerNetStatusStreamController.add(event);
});
}
......@@ -225,6 +216,7 @@ class SuperPlayerController {
_updatePlayerState(SuperPlayerState.LOADING);
break;
case TXVodPlayEvent.PLAY_EVT_RCV_FIRST_I_FRAME:
_configVideoSize(event);
_updatePlayerState(SuperPlayerState.PLAYING);
_observer?.onRcvFirstIframe();
break;
......@@ -239,27 +231,27 @@ class SuperPlayerController {
_maxLiveProgressTime = progress > _maxLiveProgressTime ? progress : _maxLiveProgressTime;
_observer?.onPlayProgress(progress / 1000, _maxLiveProgressTime / 1000, await getPlayableDuration());
break;
case TXVodPlayEvent.PLAY_EVT_CHANGE_RESOLUTION:
_configVideoSize(event);
break;
}
});
_liveNetEventListener = _livePlayerController.onPlayerNetStatusBroadcast.listen((event) {
dynamic wd = (event["VIDEO_WIDTH"]);
dynamic hd = (event["VIDEO_HEIGHT"]);
if (null != wd && null != hd) {
double w = wd.toDouble();
double h = hd.toDouble();
if (w > 0 && h > 0) {
if (w != videoWidth) {
videoWidth = w;
}
if (h != videoHeight) {
videoHeight = h;
}
}
}
_playerNetStatusStreamController.add(event);
});
}
void _configVideoSize(Map<dynamic, dynamic> event) {
int? eventVideoWidth = event[TXVodPlayEvent.EVT_VIDEO_WIDTH];
int? eventVideoHeight = event[TXVodPlayEvent.EVT_VIDEO_HEIGHT];
if (eventVideoWidth != null && eventVideoWidth != 0) {
videoWidth = eventVideoWidth.toDouble();
}
if (eventVideoHeight != null && eventVideoHeight != 0) {
videoHeight = eventVideoHeight.toDouble();
}
}
/// 播放视频.
/// 10.7版本开始,playWithModel变更为playWithModelNeedLicence,需要通过 {@link SuperPlayerPlugin#setGlobalLicense} 设置 Licence 后方可成功播放,
/// 否则将播放失败(黑屏),全局仅设置一次即可。直播 Licence、短视频 Licence 和视频播放 Licence 均可使用,若您暂未获取上述 Licence ,
......@@ -643,7 +635,7 @@ class SuperPlayerController {
_observer?.onDispose();
playerStreamController.close();
// 如果处于画中画模式,播放器暂时不释放
if(!TXPipController.instance.isPlayerInPip(getCurrentController())) {
if (!TXPipController.instance.isPlayerInPip(getCurrentController())) {
resetPlayer();
_vodPlayerController.dispose();
_livePlayerController.dispose();
......
......@@ -86,6 +86,7 @@ class SuperPlayerViewState extends State<SuperPlayerView> with WidgetsBindingObs
(value) => _playController.enableHardwareDecode(value),
(playRate) => _playController.setPlayRate(playRate),
() => _playController.playerType == SuperPlayerType.VOD);
_playController.onPlayerNetStatusBroadcast.listen((event) {
dynamic wd = (event["VIDEO_WIDTH"]);
dynamic hd = (event["VIDEO_HEIGHT"]);
......@@ -161,6 +162,8 @@ class SuperPlayerViewState extends State<SuperPlayerView> with WidgetsBindingObs
}, () {
// onRcvFirstIframe
_coverViewKey.currentState?.hideCover();
// 收到首帧事件后,先用播放器内核解析出来的分辨率对播放器大小进行调整
_calculateSize(_playController.videoWidth, _playController.videoHeight);
}, () {
// onPlayLoading
setState(() {
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论