提交 fb96993f authored 作者: tomgui's avatar tomgui 提交者: kongdywang

fix memory leak & fix pip play error

上级 8aa805c1
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" <manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
package="com.tencent.vod.flutterr"> package="com.tencent.vod.flutter">
<!-- android:documentLaunchMode="intoExisting"-->
<!-- android:excludeFromRecents="true"-->
<application> <application>
<activity <activity
android:name="com.tencent.vod.flutter.ui.FlutterPipImplActivity" android:name="com.tencent.vod.flutter.ui.FlutterPipImplActivity"
android:theme="@style/Theme" android:theme="@style/Theme"
android:supportsPictureInPicture="true" android:supportsPictureInPicture="true"
android:screenOrientation="portrait" android:screenOrientation="portrait"
android:documentLaunchMode="intoExisting"
android:excludeFromRecents="true" android:excludeFromRecents="true"
android:exported="true" android:exported="true"
android:resizeableActivity="true" android:resizeableActivity="true"
......
...@@ -163,6 +163,7 @@ public class FTXDownloadManager implements MethodChannel.MethodCallHandler, ITXV ...@@ -163,6 +163,7 @@ public class FTXDownloadManager implements MethodChannel.MethodCallHandler, ITXV
public void destroy() { public void destroy() {
mMethodChannel.setMethodCallHandler(null); mMethodChannel.setMethodCallHandler(null);
mEventChannel.setStreamHandler(null); mEventChannel.setStreamHandler(null);
TXVodDownloadManager.getInstance().setListener(null);
} }
private Map<String,Object> buildMapFromDownloadMediaInfo(TXVodDownloadMediaInfo mediaInfo) { private Map<String,Object> buildMapFromDownloadMediaInfo(TXVodDownloadMediaInfo mediaInfo) {
......
...@@ -32,7 +32,7 @@ import com.tencent.vod.flutter.FTXEvent; ...@@ -32,7 +32,7 @@ import com.tencent.vod.flutter.FTXEvent;
import com.tencent.vod.flutter.FTXPIPManager.PipParams; import com.tencent.vod.flutter.FTXPIPManager.PipParams;
import com.tencent.vod.flutter.model.PipResult; import com.tencent.vod.flutter.model.PipResult;
import com.tencent.vod.flutter.model.VideoModel; import com.tencent.vod.flutter.model.VideoModel;
import com.tencent.vod.flutterr.R; import com.tencent.vod.flutter.R;
import io.flutter.embedding.android.FlutterActivity; import io.flutter.embedding.android.FlutterActivity;
public class FlutterPipImplActivity extends FlutterActivity implements Callback, ITXVodPlayListener, public class FlutterPipImplActivity extends FlutterActivity implements Callback, ITXVodPlayListener,
...@@ -319,6 +319,9 @@ public class FlutterPipImplActivity extends FlutterActivity implements Callback, ...@@ -319,6 +319,9 @@ public class FlutterPipImplActivity extends FlutterActivity implements Callback,
@Override @Override
protected void onStop() { protected void onStop() {
super.onStop(); super.onStop();
unRegisterPipBroadcast();
mVodPlayer.stopPlay(true);
mLivePlayer.stopPlay(true);
mIsNeedToStop = true; mIsNeedToStop = true;
} }
...@@ -331,9 +334,6 @@ public class FlutterPipImplActivity extends FlutterActivity implements Callback, ...@@ -331,9 +334,6 @@ public class FlutterPipImplActivity extends FlutterActivity implements Callback,
@Override @Override
protected void onDestroy() { protected void onDestroy() {
super.onDestroy(); super.onDestroy();
unRegisterPipBroadcast();
mVodPlayer.stopPlay(true);
mLivePlayer.stopPlay(true);
} }
private void attachSurface(Surface surface) { private void attachSurface(Surface surface) {
......
// Copyright (c) 2022 Tencent. All rights reserved.
package com.example.super_player_example;
import android.app.PictureInPictureParams;
import android.app.PictureInPictureUiState;
import android.content.res.Configuration;
import android.os.Build;
import android.os.Bundle;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import io.flutter.embedding.android.FlutterActivity;
import io.flutter.plugin.common.EventChannel;
/**
* 画中画模式activity父类,使用画中画模式,需要将自己项目中的activity修改为继承该类
*/
public class FTXFlutterPipActivity extends FlutterActivity {
private static final String PIP_CHANNEL_NAME = "cloud.tencent.com/playerPlugin/pipEvent";
private static final int EVENT_PIP_MODE_ALREADY_ENTER = 1;
private static final int EVENT_PIP_MODE_ALREADY_EXIT = 2;
private static final int EVENT_PIP_MODE_REQUEST_START = 3;
private static final int EVENT_PIP_MODE_UI_STATE_CHANGED = 4;
private EventChannel mPipEventChannel;
private EventChannel.EventSink mEventSink;
/**
* 这里使用needToExitPip作为标志位,在出现onPictureInPictureModeChanged回调画中画状态和isInPictureInPictureMode不一致的时候。
* 标志位true,然后在onConfigurationChanged监听到界面宽高发生变化的时候,进行画中画模式退出的事件通知。
* for MIUI 12.5.1
*/
private boolean needToExitPip = false;
private int configWidth = 0;
private int configHeight = 0;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
initPipEventChannel();
}
private void initPipEventChannel() {
if (null == mPipEventChannel && null != getFlutterEngine()) {
mPipEventChannel = new EventChannel(getFlutterEngine().getDartExecutor().getBinaryMessenger(),
PIP_CHANNEL_NAME);
mPipEventChannel.setStreamHandler(new EventChannel.StreamHandler() {
@Override
public void onListen(Object arguments, EventChannel.EventSink events) {
mEventSink = events;
}
@Override
public void onCancel(Object arguments) {
}
});
}
}
@Override
public void onConfigurationChanged(@NonNull Configuration newConfig) {
super.onConfigurationChanged(newConfig);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
boolean isInPictureInPictureMode = isInPictureInPictureMode();
if (isInPictureInPictureMode) {
configWidth = newConfig.screenWidthDp;
configHeight = newConfig.screenHeightDp;
} else if (needToExitPip && configWidth != newConfig.screenWidthDp && configHeight != newConfig.screenHeightDp) {
if (null != mEventSink) {
mEventSink.success(getParams(EVENT_PIP_MODE_ALREADY_EXIT, null));
}
needToExitPip = false;
}
}
}
/**
* 为了兼容MIUI 12.5,PIP模式下,打开其他app然后上滑退出,再点击画中画窗口,onPictureInPictureModeChanged会异常回调关闭的情况
*
* @param ignore 校对画中画状态
*/
@Override
public void onPictureInPictureModeChanged(boolean ignore) {
boolean isInPictureInPictureMode = ignore;
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.N) {
isInPictureInPictureMode = isInPictureInPictureMode();
}
if (isInPictureInPictureMode != ignore) {
needToExitPip = true;
} else {
if (null != mEventSink) {
if (isInPictureInPictureMode) {
mEventSink.success(getParams(EVENT_PIP_MODE_ALREADY_ENTER, null));
} else {
mEventSink.success(getParams(EVENT_PIP_MODE_ALREADY_EXIT, null));
}
}
}
super.onPictureInPictureModeChanged(isInPictureInPictureMode);
}
@Override
public void onPictureInPictureUiStateChanged(@NonNull PictureInPictureUiState pipState) {
super.onPictureInPictureUiStateChanged(pipState);
if (null != mEventSink) {
mEventSink.success(getParams(EVENT_PIP_MODE_UI_STATE_CHANGED, null));
}
}
/**
* enterPictureInPictureMode生效后的回调通知,only for android > 31
*/
@Override
public boolean onPictureInPictureRequested() {
return super.onPictureInPictureRequested();
}
@Override
public boolean enterPictureInPictureMode(@NonNull PictureInPictureParams params) {
boolean enterResult = super.enterPictureInPictureMode(params);
if (enterResult && null != mEventSink) {
mEventSink.success(getParams(EVENT_PIP_MODE_REQUEST_START, null));
}
return enterResult;
}
private Map<String, Object> getParams(int event, Bundle bundle) {
Map<String, Object> param = new HashMap<>();
if (event != 0) {
param.put("event", event);
}
if (bundle != null && !bundle.isEmpty()) {
Set<String> keySet = bundle.keySet();
for (String key : keySet) {
Object val = bundle.get(key);
param.put(key, val);
}
}
return param;
}
}
...@@ -5,7 +5,6 @@ class ShortVideoPageWidget extends StatefulWidget { ...@@ -5,7 +5,6 @@ class ShortVideoPageWidget extends StatefulWidget {
String videoUrl; String videoUrl;
String coverUrl; String coverUrl;
int position; int position;
late TXVodPlayerController _controller;
ShortVideoPageWidget( ShortVideoPageWidget(
{required this.position, {required this.position,
...@@ -30,6 +29,15 @@ class _TXVodPlayerPageState extends State<ShortVideoPageWidget> { ...@@ -30,6 +29,15 @@ class _TXVodPlayerPageState extends State<ShortVideoPageWidget> {
late StreamSubscription _streamSubscriptionApplicationPause; late StreamSubscription _streamSubscriptionApplicationPause;
StreamSubscription? _playEventSubscription;
TXVodPlayerController _controller;
_TXVodPlayerPageState() : _controller = TXVodPlayerController(){
_txPlayerVideo = new TXPlayerVideo(controller: _controller);
}
@override @override
void initState() { void initState() {
super.initState(); super.initState();
...@@ -37,11 +45,9 @@ class _TXVodPlayerPageState extends State<ShortVideoPageWidget> { ...@@ -37,11 +45,9 @@ class _TXVodPlayerPageState extends State<ShortVideoPageWidget> {
} }
_init() async { _init() async {
widget._controller = new TXVodPlayerController(); await _controller?.initialize();
_txPlayerVideo = new TXPlayerVideo(controller: widget._controller); _controller.setConfig(FTXVodPlayConfig());
await widget._controller.initialize(); LogUtils.i(TAG, " [init] ${widget.position.toString()} ${this.hashCode.toString()} ${_controller.hashCode.toString()}");
widget._controller.setConfig(FTXVodPlayConfig());
LogUtils.i(TAG, " [init] ${widget.position.toString()} ${this.hashCode.toString()} ${widget._controller.hashCode.toString()}");
_setPlayerListener(); _setPlayerListener();
_setEventBusListener(); _setEventBusListener();
if (widget.position == 0) { if (widget.position == 0) {
...@@ -64,9 +70,10 @@ class _TXVodPlayerPageState extends State<ShortVideoPageWidget> { ...@@ -64,9 +70,10 @@ class _TXVodPlayerPageState extends State<ShortVideoPageWidget> {
_streamSubscriptionStopAndPlay.cancel(); _streamSubscriptionStopAndPlay.cancel();
_streamSubscriptionApplicationResume.cancel(); _streamSubscriptionApplicationResume.cancel();
_streamSubscriptionApplicationPause.cancel(); _streamSubscriptionApplicationPause.cancel();
_playEventSubscription?.cancel();
await _stop(); await _stop();
widget._controller.dispose(); _controller.dispose();
LogUtils.i(TAG, " [dispose] ${widget.position.toString()} ${this.hashCode.toString()} ${widget._controller.hashCode.toString()}"); LogUtils.i(TAG, " [dispose] ${widget.position.toString()} ${this.hashCode.toString()} ${_controller.hashCode.toString()}");
} }
Widget _getTXVodPlayerMainPage() { Widget _getTXVodPlayerMainPage() {
...@@ -95,15 +102,15 @@ class _TXVodPlayerPageState extends State<ShortVideoPageWidget> { ...@@ -95,15 +102,15 @@ class _TXVodPlayerPageState extends State<ShortVideoPageWidget> {
} }
_onTapPageView() { _onTapPageView() {
widget._controller.isPlaying().then((value) { _controller.isPlaying().then((value) {
value == true ? _pause() :_resume(); value ? _pause() :_resume();
}); });
LogUtils.i(TAG, "tap ${_isVideoPlaying.toString()}"); LogUtils.i(TAG, "tap ${_isVideoPlaying.toString()}");
} }
Widget _getSeekBarView() { Widget _getSeekBarView() {
return Positioned( return Positioned(
child: VideoSliderView(widget._controller, _progressSliderKey), child: VideoSliderView(_controller, _progressSliderKey),
bottom: 20, bottom: 20,
right: 0, right: 0,
left: 0, left: 0,
...@@ -137,24 +144,24 @@ class _TXVodPlayerPageState extends State<ShortVideoPageWidget> { ...@@ -137,24 +144,24 @@ class _TXVodPlayerPageState extends State<ShortVideoPageWidget> {
))); )));
} }
_pause() { _pause() async{
LogUtils.i(TAG, "[_pause]"); LogUtils.i(TAG, "[_pause]");
widget._controller.pause(); await _controller.pause();
setState(() { setState(() {
_isVideoPlaying = false; _isVideoPlaying = false;
}); });
} }
_resume() { _resume() async{
LogUtils.i(TAG, "[_resume]"); LogUtils.i(TAG, "[_resume]");
widget._controller.resume(); await _controller.resume();
setState(() { setState(() {
_isVideoPlaying = true; _isVideoPlaying = true;
}); });
} }
_stopLastAndPlayCurrent(StopAndResumeEvent event) { _stopLastAndPlayCurrent(StopAndResumeEvent event) {
LogUtils.i(TAG, " [received at not current outside] ${widget.position.toString()} ${this.hashCode.toString()} ${widget.hashCode.toString()} ${widget._controller.hashCode.toString()}"); LogUtils.i(TAG, " [received at not current outside] ${widget.position.toString()} ${this.hashCode.toString()} ${widget.hashCode.toString()} ${_controller.hashCode.toString()}");
event.index != widget.position ? _stop() :_startPlay(); event.index != widget.position ? _stop() :_startPlay();
} }
...@@ -163,7 +170,7 @@ class _TXVodPlayerPageState extends State<ShortVideoPageWidget> { ...@@ -163,7 +170,7 @@ class _TXVodPlayerPageState extends State<ShortVideoPageWidget> {
LogUtils.i(TAG, " [stop] ${widget.position.toString()} ${widget.hashCode.toString()}"); LogUtils.i(TAG, " [stop] ${widget.position.toString()} ${widget.hashCode.toString()}");
_isVideoPrepared = false; _isVideoPrepared = false;
_isVideoPlaying = true; _isVideoPlaying = true;
widget._controller.stop(); _controller.stop();
} }
...@@ -173,8 +180,8 @@ class _TXVodPlayerPageState extends State<ShortVideoPageWidget> { ...@@ -173,8 +180,8 @@ class _TXVodPlayerPageState extends State<ShortVideoPageWidget> {
setState(() { setState(() {
_isVideoPlaying = true; _isVideoPlaying = true;
}); });
await widget._controller.setLoop(true); await _controller.setLoop(true);
widget._controller.startVodPlay(widget.videoUrl); _controller.startVodPlay(widget.videoUrl);
} }
_hideCover() { _hideCover() {
...@@ -200,7 +207,7 @@ class _TXVodPlayerPageState extends State<ShortVideoPageWidget> { ...@@ -200,7 +207,7 @@ class _TXVodPlayerPageState extends State<ShortVideoPageWidget> {
} }
_setPlayerListener() { _setPlayerListener() {
widget._controller.onPlayerEventBroadcast.listen((event) async { _playEventSubscription = _controller.onPlayerEventBroadcast.listen((event) async {
if (event["event"] == TXVodPlayEvent.PLAY_EVT_PLAY_PROGRESS) { if (event["event"] == TXVodPlayEvent.PLAY_EVT_PLAY_PROGRESS) {
if (!mounted) return; if (!mounted) return;
double currentProgress = event["EVT_PLAY_PROGRESS"].toDouble(); double currentProgress = event["EVT_PLAY_PROGRESS"].toDouble();
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论