提交 22901bfd authored 作者: kongdywang's avatar kongdywang

pip optimize

上级 ba8d10e6
rootProject.ext { rootProject.ext {
compileSdkVersion = 28 compileSdkVersion = 31
buildToolsVersion = "28.0.3" buildToolsVersion = "28.0.3"
supportSdkVersion = "26.0.1" supportSdkVersion = "26.0.1"
minSdkVersion = 19 minSdkVersion = 19
......
<?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"
package="com.tencent.vod.flutterr"> package="com.tencent.vod.flutterr">
<application>
<activity
android:name="com.tencent.vod.flutter.ui.FlutterPipImplActivity"
android:theme="@style/Theme"
android:supportsPictureInPicture="true"
android:screenOrientation="portrait"
android:exported="true"
android:resizeableActivity="true"
android:launchMode="singleTop"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
android:hardwareAccelerated="true"
android:windowSoftInputMode="stateHidden"
tools:ignore="UnusedAttribute" >
<intent-filter>
<action android:name="com.tencent.flutter.startPip"/>
<action android:name="com.tencent.flutter.exitPip"/>
<action android:name="com.tencent.flutter.updatePip"/>
</intent-filter>
</activity>
</application>
</manifest> </manifest>
\ No newline at end of file
...@@ -39,6 +39,7 @@ public class FTXEvent { ...@@ -39,6 +39,7 @@ public class FTXEvent {
/** /**
* pip 事件 * pip 事件
*/ */
public static final String PIP_CHANNEL_NAME = "cloud.tencent.com/playerPlugin/componentEvent";
// pip广播action // pip广播action
public final static String ACTION_PIP_PLAY_CONTROL = "vodPlayControl"; public final static String ACTION_PIP_PLAY_CONTROL = "vodPlayControl";
// pip 操作 // pip 操作
...@@ -57,6 +58,40 @@ public class FTXEvent { ...@@ -57,6 +58,40 @@ public class FTXEvent {
public static final int ERROR_PIP_DENIED_PERMISSION = -102; public static final int ERROR_PIP_DENIED_PERMISSION = -102;
// pip 错误,当前界面已销毁 // pip 错误,当前界面已销毁
public static final int ERROR_PIP_ACTIVITY_DESTROYED = -103; public static final int ERROR_PIP_ACTIVITY_DESTROYED = -103;
// 来自画中画容器的事件,广播键值
public static final String EVENT_PIP_ACTION = "com.tencent.flutter.pipevent";
// 来自画中画容器的事件,事件键值
public static final String EVENT_PIP_MODE_NAME = "pipEventName";
// 画中画当前播放时间
public static final String EVENT_PIP_PLAY_TIME = "playTime";
// 来自画中画容器的事件,已经进入画中画
public static final int EVENT_PIP_MODE_ALREADY_ENTER = 1;
// 来自画中画容器的事件,已经退出画中画
public static final int EVENT_PIP_MODE_ALREADY_EXIT = 2;
// 来自画中画容器的事件,开始进入画中画
public static final int EVENT_PIP_MODE_REQUEST_START = 3;
// 来自画中画容器的事件,画中画UI发生变动,> android 31
public static final int EVENT_PIP_MODE_UI_STATE_CHANGED = 4;
// 画中画界面恢复,即点击放大按钮
public static final int EVENT_PIP_MODE_RESTORE_UI = 5;
// 启动画中画
public static final String PIP_ACTION_START = "com.tencent.flutter.startPip";
// 退出画中画
public static final String PIP_ACTION_EXIT = "com.tencent.flutter.exitPip";
// 更新画中画
public static final String PIP_ACTION_UPDATE = "com.tencent.flutter.updatePip";
// 画中画参数
public static final String EXTRA_NAME_PARAMS = "pipParams";
// 视频源
public static final String EXTRA_NAME_VIDEO = "videoModel";
// 画中画结束参数
public static final String EXTRA_NAME_RESULT = "pipResult";
// 点播播放器
public static final int PLAYER_VOD = 1;
// 直播播放器
public static final int PLAYER_LIVE = 2;
// 屏幕旋转事件 // 屏幕旋转事件
...@@ -71,4 +106,5 @@ public class FTXEvent { ...@@ -71,4 +106,5 @@ public class FTXEvent {
public static final int ORIENTATION_PORTRAIT_DOWN = 413; public static final int ORIENTATION_PORTRAIT_DOWN = 413;
// 横屏,底部在左 // 横屏,底部在左
public static final int ORIENTATION_LANDSCAPE_LEFT = 414; public static final int ORIENTATION_LANDSCAPE_LEFT = 414;
} }
...@@ -6,22 +6,20 @@ import android.graphics.SurfaceTexture; ...@@ -6,22 +6,20 @@ import android.graphics.SurfaceTexture;
import android.os.Bundle; import android.os.Bundle;
import android.util.Log; import android.util.Log;
import android.view.Surface; import android.view.Surface;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import com.tencent.rtmp.ITXLivePlayListener; import com.tencent.rtmp.ITXLivePlayListener;
import com.tencent.rtmp.TXLiveBase; import com.tencent.rtmp.TXLiveBase;
import com.tencent.rtmp.TXLiveConstants; import com.tencent.rtmp.TXLiveConstants;
import com.tencent.rtmp.TXLivePlayConfig; import com.tencent.rtmp.TXLivePlayConfig;
import com.tencent.rtmp.TXLivePlayer; import com.tencent.rtmp.TXLivePlayer;
import com.tencent.vod.flutter.model.PipResult;
import java.util.Map; import com.tencent.vod.flutter.model.VideoModel;
import io.flutter.embedding.engine.plugins.FlutterPlugin; import io.flutter.embedding.engine.plugins.FlutterPlugin;
import io.flutter.plugin.common.EventChannel; import io.flutter.plugin.common.EventChannel;
import io.flutter.plugin.common.MethodCall; import io.flutter.plugin.common.MethodCall;
import io.flutter.plugin.common.MethodChannel; import io.flutter.plugin.common.MethodChannel;
import io.flutter.view.TextureRegistry; import io.flutter.view.TextureRegistry;
import java.util.Map;
/** /**
* live player processor * live player processor
...@@ -50,27 +48,15 @@ public class FTXLivePlayer extends FTXBasePlayer implements MethodChannel.Method ...@@ -50,27 +48,15 @@ public class FTXLivePlayer extends FTXBasePlayer implements MethodChannel.Method
private final FTXPIPManager mPipManager; private final FTXPIPManager mPipManager;
private FTXPIPManager.PipParams mPipParams; private FTXPIPManager.PipParams mPipParams;
private VideoModel mVideoModel;
private final FTXPIPManager.PipCallback pipCallback = new FTXPIPManager.PipCallback() { private final FTXPIPManager.PipCallback pipCallback = new FTXPIPManager.PipCallback() {
@Override @Override
public void onPlayBack() { public void onPipResult(PipResult result) {
// pip not support playback // 启动pip的时候,当前player已经暂停,pip退出之后,如果退出的时候pip还处于播放状态,那么当前player也置为播放状态
} boolean isPipPlaying = result.isPlaying();
if(isPipPlaying) {
@Override
public void onResumeOrPlay() {
boolean isPlaying = isPlaying();
if (isPlaying) {
pause();
} else {
resume(); resume();
} }
// isPlaying取反,点击暂停/播放之后,播放状态会变化
mPipManager.updatePipActions(!isPlaying, mPipParams);
}
@Override
public void onPlayForward() {
// pip not support forward
} }
}; };
...@@ -80,6 +66,8 @@ public class FTXLivePlayer extends FTXBasePlayer implements MethodChannel.Method ...@@ -80,6 +66,8 @@ public class FTXLivePlayer extends FTXBasePlayer implements MethodChannel.Method
mFlutterPluginBinding = flutterPluginBinding; mFlutterPluginBinding = flutterPluginBinding;
mActivity = activity; mActivity = activity;
mPipManager = pipManager; mPipManager = pipManager;
mVideoModel = new VideoModel();
mVideoModel.setPlayerType(FTXEvent.PLAYER_LIVE);
mSurfaceTextureEntry = mFlutterPluginBinding.getTextureRegistry().createSurfaceTexture(); mSurfaceTextureEntry = mFlutterPluginBinding.getTextureRegistry().createSurfaceTexture();
mSurfaceTexture = mSurfaceTextureEntry.surfaceTexture(); mSurfaceTexture = mSurfaceTextureEntry.surfaceTexture();
...@@ -245,11 +233,22 @@ public class FTXLivePlayer extends FTXBasePlayer implements MethodChannel.Method ...@@ -245,11 +233,22 @@ public class FTXLivePlayer extends FTXBasePlayer implements MethodChannel.Method
String playPauseAssetPath = call.argument("pauseIcon"); String playPauseAssetPath = call.argument("pauseIcon");
String playForwardAssetPath = call.argument("forwardIcon"); String playForwardAssetPath = call.argument("forwardIcon");
mPipManager.addCallback(getPlayerId(), pipCallback); mPipManager.addCallback(getPlayerId(), pipCallback);
mPipParams = new FTXPIPManager.PipParams(playBackAssetPath, playResumeAssetPath, mPipParams = new FTXPIPManager.PipParams(
playPauseAssetPath, mPipManager.toAndroidPath(playBackAssetPath),
playForwardAssetPath, getPlayerId(), false, false, true); mPipManager.toAndroidPath(playResumeAssetPath),
int pipResult = mPipManager.enterPip(isPlaying(), mPipParams); mPipManager.toAndroidPath(playPauseAssetPath),
mPipManager.toAndroidPath(playForwardAssetPath),
getPlayerId(),false, false, true);
mPipParams.setIsPlaying(isPlaying());
int pipResult = mPipManager.enterPip(mPipParams, mVideoModel);
// 启动成功之后,暂停当前界面视频
if(pipResult == FTXEvent.NO_ERROR) {
pause();
}
result.success(pipResult); result.success(pipResult);
} else if(call.method.equals("exitPictureInPictureMode")) {
mPipManager.exitPip();
result.success(null);
} else if (call.method.equals("setConfig")) { } else if (call.method.equals("setConfig")) {
Map<Object, Object> config = call.argument("config"); Map<Object, Object> config = call.argument("config");
setPlayConfig(config); setPlayConfig(config);
...@@ -272,6 +271,8 @@ public class FTXLivePlayer extends FTXBasePlayer implements MethodChannel.Method ...@@ -272,6 +271,8 @@ public class FTXLivePlayer extends FTXBasePlayer implements MethodChannel.Method
int startLivePlay(String url, int type) { int startLivePlay(String url, int type) {
Log.d(TAG, "startLivePlay:"); Log.d(TAG, "startLivePlay:");
mVideoModel.setVideoUrl(url);
mVideoModel.setLiveType(type);
if (mLivePlayer != null) { if (mLivePlayer != null) {
mLivePlayer.setSurface(mSurface); mLivePlayer.setSurface(mSurface);
mLivePlayer.setPlayListener(this); mLivePlayer.setPlayListener(this);
......
...@@ -6,9 +6,7 @@ import android.graphics.SurfaceTexture; ...@@ -6,9 +6,7 @@ import android.graphics.SurfaceTexture;
import android.os.Bundle; import android.os.Bundle;
import android.text.TextUtils; import android.text.TextUtils;
import android.view.Surface; import android.view.Surface;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import com.tencent.rtmp.ITXVodPlayListener; import com.tencent.rtmp.ITXVodPlayListener;
import com.tencent.rtmp.TXBitrateItem; import com.tencent.rtmp.TXBitrateItem;
import com.tencent.rtmp.TXImageSprite; import com.tencent.rtmp.TXImageSprite;
...@@ -17,18 +15,18 @@ import com.tencent.rtmp.TXLivePlayer; ...@@ -17,18 +15,18 @@ import com.tencent.rtmp.TXLivePlayer;
import com.tencent.rtmp.TXPlayInfoParams; import com.tencent.rtmp.TXPlayInfoParams;
import com.tencent.rtmp.TXVodPlayConfig; import com.tencent.rtmp.TXVodPlayConfig;
import com.tencent.rtmp.TXVodPlayer; import com.tencent.rtmp.TXVodPlayer;
import com.tencent.vod.flutter.model.PipResult;
import java.io.ByteArrayOutputStream; import com.tencent.vod.flutter.model.VideoModel;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import io.flutter.embedding.engine.plugins.FlutterPlugin; import io.flutter.embedding.engine.plugins.FlutterPlugin;
import io.flutter.plugin.common.EventChannel; import io.flutter.plugin.common.EventChannel;
import io.flutter.plugin.common.MethodCall; import io.flutter.plugin.common.MethodCall;
import io.flutter.plugin.common.MethodChannel; import io.flutter.plugin.common.MethodChannel;
import io.flutter.view.TextureRegistry; import io.flutter.view.TextureRegistry;
import java.io.ByteArrayOutputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/** /**
* vodPlayer plugin processor * vodPlayer plugin processor
...@@ -49,6 +47,7 @@ public class FTXVodPlayer extends FTXBasePlayer implements MethodChannel.MethodC ...@@ -49,6 +47,7 @@ public class FTXVodPlayer extends FTXBasePlayer implements MethodChannel.MethodC
private TXVodPlayer mVodPlayer; private TXVodPlayer mVodPlayer;
private TXImageSprite mTxImageSprite; private TXImageSprite mTxImageSprite;
private VideoModel mVideoModel;
private static final int Uninitialized = -101; private static final int Uninitialized = -101;
private TextureRegistry.SurfaceTextureEntry mSurfaceTextureEntry; private TextureRegistry.SurfaceTextureEntry mSurfaceTextureEntry;
...@@ -59,39 +58,17 @@ public class FTXVodPlayer extends FTXBasePlayer implements MethodChannel.MethodC ...@@ -59,39 +58,17 @@ public class FTXVodPlayer extends FTXBasePlayer implements MethodChannel.MethodC
private FTXPIPManager.PipParams mPipParams; private FTXPIPManager.PipParams mPipParams;
private final FTXPIPManager.PipCallback pipCallback = new FTXPIPManager.PipCallback() { private final FTXPIPManager.PipCallback pipCallback = new FTXPIPManager.PipCallback() {
@Override @Override
public void onPlayBack() { public void onPipResult(PipResult result) {
boolean isPlaying = isPlaying(); float playTime = result.getPlayTime();
if (isPlaying) {
float backPlayTime = getCurrentPlaybackTime() - 10;
if (backPlayTime < 0) {
backPlayTime = 0;
}
seek(backPlayTime);
}
}
@Override
public void onResumeOrPlay() {
boolean isPlaying = isPlaying();
if (isPlaying) {
pause();
} else {
resume();
}
// isPlaying取反,点击暂停/播放之后,播放状态会变化
mPipManager.updatePipActions(!isPlaying, mPipParams);
}
@Override
public void onPlayForward() {
boolean isPlaying = isPlaying();
if (isPlaying) {
float forwardPlayTime = getCurrentPlaybackTime() + 10;
float duration = mVodPlayer.getDuration(); float duration = mVodPlayer.getDuration();
if (forwardPlayTime > duration) { if (playTime > duration) {
forwardPlayTime = duration; playTime = duration;
} }
seek(forwardPlayTime); seek(playTime);
// 启动pip的时候,当前player已经暂停,pip退出之后,如果退出的时候pip还处于播放状态,那么当前player也置为播放状态
boolean isPipPlaying = result.isPlaying();
if(isPipPlaying) {
resume();
} }
} }
}; };
...@@ -100,6 +77,8 @@ public class FTXVodPlayer extends FTXBasePlayer implements MethodChannel.MethodC ...@@ -100,6 +77,8 @@ public class FTXVodPlayer extends FTXBasePlayer implements MethodChannel.MethodC
super(); super();
mPipManager = pipManager; mPipManager = pipManager;
mFlutterPluginBinding = flutterPluginBinding; mFlutterPluginBinding = flutterPluginBinding;
mVideoModel = new VideoModel();
mVideoModel.setPlayerType(FTXEvent.PLAYER_VOD);
mMethodChannel = new MethodChannel(flutterPluginBinding.getBinaryMessenger(), "cloud.tencent" + mMethodChannel = new MethodChannel(flutterPluginBinding.getBinaryMessenger(), "cloud.tencent" +
".com/txvodplayer/" + super.getPlayerId()); ".com/txvodplayer/" + super.getPlayerId());
...@@ -196,10 +175,6 @@ public class FTXVodPlayer extends FTXBasePlayer implements MethodChannel.MethodC ...@@ -196,10 +175,6 @@ public class FTXVodPlayer extends FTXBasePlayer implements MethodChannel.MethodC
} }
} else if (event == TXLiveConstants.PLAY_WARNING_HW_ACCELERATION_FAIL) { } else if (event == TXLiveConstants.PLAY_WARNING_HW_ACCELERATION_FAIL) {
mHardwareDecodeFail = true; mHardwareDecodeFail = true;
} else if (event == TXLiveConstants.PLAY_EVT_PLAY_END) {
if (null != mPipManager && mPipManager.isInPipMode()) {
mPipManager.updatePipActions(false, mPipParams);
}
} }
mEventSink.success(CommonUtil.getParams(event, bundle)); mEventSink.success(CommonUtil.getParams(event, bundle));
} }
...@@ -355,11 +330,23 @@ public class FTXVodPlayer extends FTXBasePlayer implements MethodChannel.MethodC ...@@ -355,11 +330,23 @@ public class FTXVodPlayer extends FTXBasePlayer implements MethodChannel.MethodC
String playPauseAssetPath = call.argument("pauseIcon"); String playPauseAssetPath = call.argument("pauseIcon");
String playForwardAssetPath = call.argument("forwardIcon"); String playForwardAssetPath = call.argument("forwardIcon");
mPipManager.addCallback(getPlayerId(), pipCallback); mPipManager.addCallback(getPlayerId(), pipCallback);
mPipParams = new FTXPIPManager.PipParams(playBackAssetPath, playResumeAssetPath, mPipParams = new FTXPIPManager.PipParams(
playPauseAssetPath, mPipManager.toAndroidPath(playBackAssetPath),
playForwardAssetPath, getPlayerId()); mPipManager.toAndroidPath(playResumeAssetPath),
int pipResult = mPipManager.enterPip(isPlaying(), mPipParams); mPipManager.toAndroidPath(playPauseAssetPath),
mPipManager.toAndroidPath(playForwardAssetPath),
getPlayerId());
mPipParams.setIsPlaying(isPlaying());
mPipParams.setCurrentPlayTime(getCurrentPlaybackTime());
int pipResult = mPipManager.enterPip(mPipParams, mVideoModel);
// 启动成功之后,暂停当前界面视频
if(pipResult == FTXEvent.NO_ERROR) {
pause();
}
result.success(pipResult); result.success(pipResult);
} else if(call.method.equals("exitPictureInPictureMode")) {
mPipManager.exitPip();
result.success(null);
} else if(call.method.equals("initImageSprite")) { } else if(call.method.equals("initImageSprite")) {
String vvtUrl = call.argument("vvtUrl"); String vvtUrl = call.argument("vvtUrl");
List<String> imageUrls = call.argument("imageUrls"); List<String> imageUrls = call.argument("imageUrls");
...@@ -411,6 +398,10 @@ public class FTXVodPlayer extends FTXBasePlayer implements MethodChannel.MethodC ...@@ -411,6 +398,10 @@ public class FTXVodPlayer extends FTXBasePlayer implements MethodChannel.MethodC
} }
int startVodPlay(String url) { int startVodPlay(String url) {
mVideoModel.setVideoUrl(url);
mVideoModel.setAppId(0);
mVideoModel.setFileId("");
mVideoModel.setPSign("");
if (mVodPlayer != null) { if (mVodPlayer != null) {
return mVodPlayer.startVodPlay(url); return mVodPlayer.startVodPlay(url);
} }
...@@ -422,6 +413,10 @@ public class FTXVodPlayer extends FTXBasePlayer implements MethodChannel.MethodC ...@@ -422,6 +413,10 @@ public class FTXVodPlayer extends FTXBasePlayer implements MethodChannel.MethodC
int appId = call.argument("appId"); int appId = call.argument("appId");
String fileId = call.argument("fileId"); String fileId = call.argument("fileId");
String psign = call.argument("psign"); String psign = call.argument("psign");
mVideoModel.setVideoUrl("");
mVideoModel.setAppId(appId);
mVideoModel.setFileId(fileId);
mVideoModel.setPSign(psign);
TXPlayInfoParams playInfoParams = new TXPlayInfoParams(appId, fileId, psign); TXPlayInfoParams playInfoParams = new TXPlayInfoParams(appId, fileId, psign);
mVodPlayer.startVodPlay(playInfoParams); mVodPlayer.startVodPlay(playInfoParams);
} }
......
...@@ -246,7 +246,7 @@ public class SuperPlayerPlugin implements FlutterPlugin, MethodCallHandler, Acti ...@@ -246,7 +246,7 @@ public class SuperPlayerPlugin implements FlutterPlugin, MethodCallHandler, Acti
private void initPipManagerIfNeed() { private void initPipManagerIfNeed() {
if (null == mTxPipManager) { if (null == mTxPipManager) {
mTxPipManager = new FTXPIPManager(mTxAudioManager, mActivityPluginBinding.getActivity(), mTxPipManager = new FTXPIPManager(mTxAudioManager, mFlutterPluginBinding, mActivityPluginBinding,
mFlutterPluginBinding.getFlutterAssets()); mFlutterPluginBinding.getFlutterAssets());
} }
} }
...@@ -280,7 +280,7 @@ public class SuperPlayerPlugin implements FlutterPlugin, MethodCallHandler, Acti ...@@ -280,7 +280,7 @@ public class SuperPlayerPlugin implements FlutterPlugin, MethodCallHandler, Acti
@Override @Override
public void onDetachedFromActivity() { public void onDetachedFromActivity() {
if (null != mTxPipManager) { if (null != mTxPipManager) {
mTxPipManager.releaseReceiver(); mTxPipManager.releaseActivityListener();
} }
unregisterReceiver(); unregisterReceiver();
} }
......
package com.tencent.vod.flutter.model;
import android.os.Parcel;
import android.os.Parcelable;
public class PipResult implements Parcelable {
private Float mPlayTime;
private boolean mIsPlaying;
private int mPlayerId;
public PipResult(){}
protected PipResult(Parcel in) {
if (in.readByte() == 0) {
mPlayTime = null;
} else {
mPlayTime = in.readFloat();
}
mIsPlaying = in.readByte() != 0;
mPlayerId = in.readInt();
}
public static final Creator<PipResult> CREATOR = new Creator<PipResult>() {
@Override
public PipResult createFromParcel(Parcel in) {
return new PipResult(in);
}
@Override
public PipResult[] newArray(int size) {
return new PipResult[size];
}
};
public Float getPlayTime() {
return mPlayTime;
}
public void setPlayTime(Float mPlayTime) {
this.mPlayTime = mPlayTime;
}
public boolean isPlaying() {
return mIsPlaying;
}
public void setPlaying(boolean playing) {
mIsPlaying = playing;
}
public int getPlayerId() {
return mPlayerId;
}
public void setPlayerId(int mPlayerId) {
this.mPlayerId = mPlayerId;
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
if (mPlayTime == null) {
dest.writeByte((byte) 0);
} else {
dest.writeByte((byte) 1);
dest.writeFloat(mPlayTime);
}
dest.writeByte((byte) (mIsPlaying ? 1 : 0));
dest.writeInt(mPlayerId);
}
}
// Copyright (c) 2022 Tencent. All rights reserved.
package com.tencent.vod.flutter.model;
import android.os.Parcel;
import android.os.Parcelable;
import com.tencent.rtmp.TXLivePlayer;
import com.tencent.vod.flutter.FTXEvent;
/**
* 视频model
*/
public class VideoModel implements Parcelable {
private String videoUrl;
private int appId;
private String fileId;
private String pSign;
private int mPlayerType = FTXEvent.PLAYER_VOD;
private int mLiveType = TXLivePlayer.PLAY_TYPE_LIVE_FLV;
public VideoModel() {}
protected VideoModel(Parcel in) {
videoUrl = in.readString();
appId = in.readInt();
fileId = in.readString();
pSign = in.readString();
mPlayerType = in.readInt();
mLiveType = in.readInt();
}
public static final Creator<VideoModel> CREATOR = new Creator<VideoModel>() {
@Override
public VideoModel createFromParcel(Parcel in) {
return new VideoModel(in);
}
@Override
public VideoModel[] newArray(int size) {
return new VideoModel[size];
}
};
public String getVideoUrl() {
return videoUrl;
}
public void setVideoUrl(String videoUrl) {
this.videoUrl = videoUrl;
}
public int getAppId() {
return appId;
}
public void setAppId(int appId) {
this.appId = appId;
}
public String getFileId() {
return fileId;
}
public void setFileId(String fileId) {
this.fileId = fileId;
}
public String getPSign() {
return pSign;
}
public void setPSign(String pSign) {
this.pSign = pSign;
}
public int getPlayerType() {
return mPlayerType;
}
public void setPlayerType(int mPlayerType) {
this.mPlayerType = mPlayerType;
}
public int getLiveType() {
return mLiveType;
}
public void setLiveType(int mLiveType) {
this.mLiveType = mLiveType;
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(videoUrl);
dest.writeInt(appId);
dest.writeString(fileId);
dest.writeString(pSign);
dest.writeInt(mPlayerType);
dest.writeInt(mLiveType);
}
}
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/transparent"
tools:context="com.tencent.vod.flutter.ui.FlutterPipImplActivity">
<SurfaceView
android:id="@+id/sv_video_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="gone"/>
<ProgressBar
android:id="@+id/pb_video_progress"
android:layout_width="match_parent"
android:layout_height="3dp"
android:layout_alignParentBottom="true"
android:max="100"
android:visibility="gone"
style="@android:style/Widget.Holo.ProgressBar.Horizontal"/>
</RelativeLayout>
\ No newline at end of file
<resources></resources>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- 取消界面转场动画使用-->
<style name="Theme" parent="android:Theme.Translucent.NoTitleBar.Fullscreen">
<item name="android:windowAnimationStyle">@style/Animation</item>
<item name="android:background">@android:color/transparent</item>
<item name="android:windowIsTranslucent">true</item>
<item name="android:windowBackground">@android:color/transparent</item>
</style>
<style name="Animation">
<item name="android:activityOpenEnterAnimation">@null</item>
<item name="android:activityOpenExitAnimation">@null</item>
<item name="android:activityCloseEnterAnimation">@null</item>
<item name="android:activityCloseExitAnimation">@null</item>
<item name="android:taskOpenEnterAnimation">@null</item>
<item name="android:taskOpenExitAnimation">@null</item>
<item name="android:taskCloseEnterAnimation">@null</item>
<item name="android:taskCloseExitAnimation">@null</item>
<item name="android:taskToFrontEnterAnimation">@null</item>
<item name="android:taskToFrontExitAnimation">@null</item>
<item name="android:taskToBackEnterAnimation">@null</item>
<item name="android:taskToBackExitAnimation">@null</item>
</style>
</resources>
\ No newline at end of file
// Copyright (c) 2022 Tencent. All rights reserved. // Copyright (c) 2022 Tencent. All rights reserved.
package com.example.super_player_example; package com.example.super_player_example;
public class MainActivity extends FTXFlutterPipActivity { import io.flutter.embedding.android.FlutterActivity;
public class MainActivity extends FlutterActivity {
} }
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
archiveVersion = 1; archiveVersion = 1;
classes = { classes = {
}; };
objectVersion = 50; objectVersion = 51;
objects = { objects = {
/* Begin PBXBuildFile section */ /* Begin PBXBuildFile section */
...@@ -393,7 +393,7 @@ ...@@ -393,7 +393,7 @@
"$(inherited)", "$(inherited)",
"@executable_path/Frameworks", "@executable_path/Frameworks",
); );
PRODUCT_BUNDLE_IDENTIFIER = com.yunxiao.dev.liteavdemo; PRODUCT_BUNDLE_IDENTIFIER = com.yunxiao.dev.liteav.demo;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "Yunxiao Develop Profile"; PROVISIONING_PROFILE_SPECIFIER = "Yunxiao Develop Profile";
VERSIONING_SYSTEM = "apple-generic"; VERSIONING_SYSTEM = "apple-generic";
...@@ -522,7 +522,7 @@ ...@@ -522,7 +522,7 @@
"$(inherited)", "$(inherited)",
"@executable_path/Frameworks", "@executable_path/Frameworks",
); );
PRODUCT_BUNDLE_IDENTIFIER = com.yunxiao.dev.liteavdemo; PRODUCT_BUNDLE_IDENTIFIER = com.yunxiao.dev.liteav.demo;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "Yunxiao Develop Profile"; PROVISIONING_PROFILE_SPECIFIER = "Yunxiao Develop Profile";
VERSIONING_SYSTEM = "apple-generic"; VERSIONING_SYSTEM = "apple-generic";
...@@ -544,7 +544,7 @@ ...@@ -544,7 +544,7 @@
"$(inherited)", "$(inherited)",
"@executable_path/Frameworks", "@executable_path/Frameworks",
); );
PRODUCT_BUNDLE_IDENTIFIER = com.yunxiao.dev.liteavdemo; PRODUCT_BUNDLE_IDENTIFIER = com.yunxiao.dev.liteav.demo;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "Yunxiao Develop Profile"; PROVISIONING_PROFILE_SPECIFIER = "Yunxiao Develop Profile";
VERSIONING_SYSTEM = "apple-generic"; VERSIONING_SYSTEM = "apple-generic";
......
...@@ -2,6 +2,8 @@ ...@@ -2,6 +2,8 @@
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0"> <plist version="1.0">
<dict> <dict>
<key>CADisableMinimumFrameDurationOnPhone</key>
<true/>
<key>CFBundleDevelopmentRegion</key> <key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string> <string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleExecutable</key> <key>CFBundleExecutable</key>
...@@ -52,7 +54,5 @@ ...@@ -52,7 +54,5 @@
<false/> <false/>
<key>io.flutter.embedded_views_preview</key> <key>io.flutter.embedded_views_preview</key>
<true/> <true/>
<key>CADisableMinimumFrameDurationOnPhone</key>
<true/>
</dict> </dict>
</plist> </plist>
...@@ -8,26 +8,31 @@ import 'package:super_player_example/ui/demo_inputdialog.dart'; ...@@ -8,26 +8,31 @@ import 'package:super_player_example/ui/demo_inputdialog.dart';
import 'package:superplayer_widget/demo_superplayer_lib.dart'; import 'package:superplayer_widget/demo_superplayer_lib.dart';
/// flutter superplayer demo /// flutter superplayer demo
class DemoSuperplayer extends StatefulWidget { class DemoSuperPlayer extends StatefulWidget {
Map? initParams = {};
DemoSuperPlayer({this.initParams});
@override @override
State<StatefulWidget> createState() => _DemoSuperplayerState(); State<StatefulWidget> createState() => _DemoSuperPlayerState();
} }
class _DemoSuperplayerState extends State<DemoSuperplayer> { class _DemoSuperPlayerState extends State<DemoSuperPlayer> with TXPipPlayerRestorePage {
static const DEFAULT_PLACE_HOLDER = "http://xiaozhibo-10055601.file.myqcloud.com/coverImg.jpg"; static const DEFAULT_PLACE_HOLDER = "http://xiaozhibo-10055601.file.myqcloud.com/coverImg.jpg";
static const ARGUMENT_TYPE_POS = "arg_type_pos";
static const ARGUMENT_VIDEO_DATA = "arg_video_data";
List<SuperPlayerModel> videoModels = []; List<SuperPlayerModel> videoModels = [];
bool _isFullScreen = false; bool _isFullScreen = false;
late SuperPlayerController _controller; late SuperPlayerController _controller;
StreamSubscription? simpleEventSubscription; StreamSubscription? simpleEventSubscription;
int tabSelectPos = 0; int tabSelectPos = 0;
TextStyle _textStyleSelected = new TextStyle( SuperPlayerModel? currentVideoModel;
fontSize: 16, color: Colors.white SuperPlayerModel? initVideoModel;
); double? initStartTime;
TextStyle _textStyleUnSelected = new TextStyle( TextStyle _textStyleSelected = new TextStyle(fontSize: 16, color: Colors.white);
fontSize: 16, color: Colors.grey TextStyle _textStyleUnSelected = new TextStyle(fontSize: 16, color: Colors.grey);
);
@override @override
void initState() { void initState() {
...@@ -35,6 +40,7 @@ class _DemoSuperplayerState extends State<DemoSuperplayer> { ...@@ -35,6 +40,7 @@ class _DemoSuperplayerState extends State<DemoSuperplayer> {
// 监听设备旋转 // 监听设备旋转
SuperPlayerPlugin.startVideoOrientationService(); SuperPlayerPlugin.startVideoOrientationService();
_controller = SuperPlayerController(context); _controller = SuperPlayerController(context);
TXPipController.instance.setPipPlayerPage(DemoSuperPlayer, this);
FTXVodPlayConfig config = FTXVodPlayConfig(); FTXVodPlayConfig config = FTXVodPlayConfig();
// 如果不配置preferredResolution,则在播放多码率视频的时候优先播放720 * 1280分辨率的码率 // 如果不配置preferredResolution,则在播放多码率视频的时候优先播放720 * 1280分辨率的码率
config.preferredResolution = 720 * 1280; config.preferredResolution = 720 * 1280;
...@@ -49,7 +55,16 @@ class _DemoSuperplayerState extends State<DemoSuperplayer> { ...@@ -49,7 +55,16 @@ class _DemoSuperplayerState extends State<DemoSuperplayer> {
print(evtName); print(evtName);
} }
}); });
if (null != widget.initParams) {
tabSelectPos = widget.initParams![ARGUMENT_TYPE_POS];
initVideoModel = widget.initParams![ARGUMENT_VIDEO_DATA];
initStartTime = widget.initParams![TXPipController.ARGUMENT_PIP_START_TIME];
}
if (tabSelectPos == 0) {
_getLiveListData(); _getLiveListData();
} else if (tabSelectPos == 1) {
_getVodListData();
}
} }
@override @override
...@@ -157,7 +172,7 @@ class _DemoSuperplayerState extends State<DemoSuperplayer> { ...@@ -157,7 +172,7 @@ class _DemoSuperplayerState extends State<DemoSuperplayer> {
playModel.title, playModel.title,
style: TextStyle(color: Colors.white), style: TextStyle(color: Colors.white),
), ),
onTap: () => playCurrentModel(playModel), onTap: () => playCurrentModel(playModel, 0),
horizontalTitleGap: 10, horizontalTitleGap: 10,
), ),
Divider() Divider()
...@@ -177,7 +192,11 @@ class _DemoSuperplayerState extends State<DemoSuperplayer> { ...@@ -177,7 +192,11 @@ class _DemoSuperplayerState extends State<DemoSuperplayer> {
showDialog( showDialog(
context: context, context: context,
builder: (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) {
SuperPlayerModel model = new SuperPlayerModel(); SuperPlayerModel model = new SuperPlayerModel();
model.appId = appId; model.appId = appId;
if (url.isNotEmpty) { if (url.isNotEmpty) {
...@@ -193,15 +212,30 @@ class _DemoSuperplayerState extends State<DemoSuperplayer> { ...@@ -193,15 +212,30 @@ class _DemoSuperplayerState extends State<DemoSuperplayer> {
return; return;
} }
playCurrentModel(model); playCurrentModel(model, 0);
}, needPisgn: !isLive, showFileEdited: !isLive,); },
needPisgn: !isLive,
showFileEdited: !isLive,
);
}); });
} }
void playCurrentModel(SuperPlayerModel model) { void playCurrentModel(SuperPlayerModel model, double startTime) {
currentVideoModel = model;
_controller.setStartTime(startTime);
_controller.playWithModelNeedLicence(model); _controller.playWithModelNeedLicence(model);
} }
void playVideo(SuperPlayerModel model) {
if (null != initVideoModel) {
playCurrentModel(initVideoModel!, initStartTime ?? 0);
initVideoModel = null;
initStartTime = null;
} else {
playCurrentModel(model, 0);
}
}
void _getLiveListData() async { void _getLiveListData() async {
setState(() { setState(() {
tabSelectPos = 0; tabSelectPos = 0;
...@@ -212,7 +246,8 @@ class _DemoSuperplayerState extends State<DemoSuperplayer> { ...@@ -212,7 +246,8 @@ class _DemoSuperplayerState extends State<DemoSuperplayer> {
SuperPlayerModel model = SuperPlayerModel(); SuperPlayerModel model = SuperPlayerModel();
model.title = "测试视频"; model.title = "测试视频";
model.videoURL = "http://liteavapp.qcloud.com/live/liteavdemoplayerstreamid_demo1080p.flv"; model.videoURL = "http://liteavapp.qcloud.com/live/liteavdemoplayerstreamid_demo1080p.flv";
model.coverUrl = "http://1500005830.vod2.myqcloud.com/6c9a5118vodcq1500005830/66bc542f387702300661648850/0RyP1rZfkdQA.png"; model.coverUrl =
"http://1500005830.vod2.myqcloud.com/6c9a5118vodcq1500005830/66bc542f387702300661648850/0RyP1rZfkdQA.png";
model.playAction = playAction; model.playAction = playAction;
models.add(model); models.add(model);
...@@ -220,7 +255,7 @@ class _DemoSuperplayerState extends State<DemoSuperplayer> { ...@@ -220,7 +255,7 @@ class _DemoSuperplayerState extends State<DemoSuperplayer> {
videoModels.addAll(models); videoModels.addAll(models);
setState(() { setState(() {
if (videoModels.isNotEmpty) { if (videoModels.isNotEmpty) {
playCurrentModel(videoModels[0]); playVideo(videoModels[0]);
} else { } else {
EasyLoading.showError("video list request error"); EasyLoading.showError("video list request error");
} }
...@@ -290,7 +325,7 @@ class _DemoSuperplayerState extends State<DemoSuperplayer> { ...@@ -290,7 +325,7 @@ class _DemoSuperplayerState extends State<DemoSuperplayer> {
videoModels.addAll(models); videoModels.addAll(models);
setState(() { setState(() {
if (videoModels.isNotEmpty) { if (videoModels.isNotEmpty) {
playCurrentModel(videoModels[0]); playVideo(videoModels[0]);
} else { } else {
EasyLoading.showError("video list request error"); EasyLoading.showError("video list request error");
} }
...@@ -306,4 +341,10 @@ class _DemoSuperplayerState extends State<DemoSuperplayer> { ...@@ -306,4 +341,10 @@ class _DemoSuperplayerState extends State<DemoSuperplayer> {
SuperPlayerPlugin.restorePageBrightness(); SuperPlayerPlugin.restorePageBrightness();
super.dispose(); super.dispose();
} }
@override
void onNeedSavePipPageState(Map<String, dynamic> params) {
params[ARGUMENT_TYPE_POS] = tabSelectPos;
params[ARGUMENT_VIDEO_DATA] = currentVideoModel;
}
} }
...@@ -12,10 +12,10 @@ import 'ui/demo_video_slider_view.dart'; ...@@ -12,10 +12,10 @@ import 'ui/demo_video_slider_view.dart';
class DemoTXVodPlayer extends StatefulWidget { class DemoTXVodPlayer extends StatefulWidget {
@override @override
_DemoTXVodlayerState createState() => _DemoTXVodlayerState(); _DemoTXVodPlayerState createState() => _DemoTXVodPlayerState();
} }
class _DemoTXVodlayerState extends State<DemoTXVodPlayer> class _DemoTXVodPlayerState extends State<DemoTXVodPlayer>
with WidgetsBindingObserver { with WidgetsBindingObserver {
late TXVodPlayerController _controller; late TXVodPlayerController _controller;
double _aspectRatio = 16 / 9; double _aspectRatio = 16 / 9;
...@@ -24,8 +24,7 @@ class _DemoTXVodlayerState extends State<DemoTXVodPlayer> ...@@ -24,8 +24,7 @@ class _DemoTXVodlayerState extends State<DemoTXVodPlayer>
int _volume = 100; int _volume = 100;
List _supportedBitrates = []; List _supportedBitrates = [];
int _curBitrateIndex = 0; int _curBitrateIndex = 0;
String _url = String _url = "http://1400329073.vod2.myqcloud.com/d62d88a7vodtranscq1400329073/59c68fe75285890800381567412/adp.10.m3u8";
"http://1400329073.vod2.myqcloud.com/d62d88a7vodtranscq1400329073/59c68fe75285890800381567412/adp.10.m3u8";
int _appId = 0; int _appId = 0;
String _fileId = ""; String _fileId = "";
double _rate = 1.0; double _rate = 1.0;
......
...@@ -5,7 +5,9 @@ import 'package:flutter/material.dart'; ...@@ -5,7 +5,9 @@ import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:flutter_easyloading/flutter_easyloading.dart'; import 'package:flutter_easyloading/flutter_easyloading.dart';
import 'package:super_player/super_player.dart'; import 'package:super_player/super_player.dart';
import 'package:super_player_example/demo_superplayer.dart';
import 'package:super_player_example/shortvideo/demo_short_video_lib.dart'; import 'package:super_player_example/shortvideo/demo_short_video_lib.dart';
import 'package:superplayer_widget/demo_superplayer_lib.dart';
import 'ui/treePage.dart'; import 'ui/treePage.dart';
...@@ -22,6 +24,7 @@ class MyApp extends StatefulWidget { ...@@ -22,6 +24,7 @@ class MyApp extends StatefulWidget {
class _MyAppState extends State<MyApp> { class _MyAppState extends State<MyApp> {
String _platformVersion = 'Unknown'; String _platformVersion = 'Unknown';
String? _liteAVSdkVersion = 'Unknown'; String? _liteAVSdkVersion = 'Unknown';
GlobalKey<NavigatorState> navigatorKey = GlobalKey<NavigatorState>();
@override @override
void initState() { void initState() {
...@@ -58,6 +61,9 @@ class _MyAppState extends State<MyApp> { ...@@ -58,6 +61,9 @@ class _MyAppState extends State<MyApp> {
setState(() { setState(() {
_platformVersion = platformVersion!; _platformVersion = platformVersion!;
}); });
TXPipController.instance.setNavigatorHandle((params) {
navigatorKey.currentState?.push(MaterialPageRoute(builder: (_) => DemoSuperPlayer(initParams: params)));
});
} }
Future<void> _getflutterSdkVersion() async { Future<void> _getflutterSdkVersion() async {
...@@ -70,21 +76,23 @@ class _MyAppState extends State<MyApp> { ...@@ -70,21 +76,23 @@ class _MyAppState extends State<MyApp> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return MaterialApp( return MaterialApp(
navigatorKey: navigatorKey,
home: Container( home: Container(
decoration: BoxDecoration( decoration: BoxDecoration(
image: DecorationImage( image: DecorationImage(
image: AssetImage("images/ic_new_vod_bg.png"), image: AssetImage("images/ic_new_vod_bg.png"),
fit: BoxFit.cover, fit: BoxFit.cover,
) )),
),
child: Scaffold( child: Scaffold(
backgroundColor: Colors.transparent, backgroundColor: Colors.transparent,
appBar: AppBar( appBar: AppBar(
backgroundColor: Colors.transparent, backgroundColor: Colors.transparent,
title: const Text('腾讯云Flutter播放器', style: TextStyle(fontSize: 24, fontWeight: FontWeight.w400),), title: const Text(
'腾讯云Flutter播放器',
style: TextStyle(fontSize: 24, fontWeight: FontWeight.w400),
),
), ),
body: Builder( body: Builder(builder: (context) {
builder: (context) {
return Container( return Container(
color: Colors.transparent, color: Colors.transparent,
child: Stack( child: Stack(
...@@ -93,15 +101,13 @@ class _MyAppState extends State<MyApp> { ...@@ -93,15 +101,13 @@ class _MyAppState extends State<MyApp> {
Align( Align(
alignment: Alignment.bottomCenter, alignment: Alignment.bottomCenter,
child: Padding( child: Padding(
padding: new EdgeInsets.all(20.0) , padding: new EdgeInsets.all(20.0),
child: Text('LiteAVSDKVersion: ${_liteAVSdkVersion}'), child: Text('LiteAVSDKVersion: ${_liteAVSdkVersion}'),
) )),
),
], ],
), ),
); );
} }),
),
), ),
), ),
builder: EasyLoading.init(), builder: EasyLoading.init(),
......
...@@ -136,7 +136,7 @@ class _TreePageState extends State<TreePage> { ...@@ -136,7 +136,7 @@ class _TreePageState extends State<TreePage> {
}else if (i == 1) { }else if (i == 1) {
return DemoTXVodPlayer(); return DemoTXVodPlayer();
} else if (i==2){ } else if (i==2){
return DemoSuperplayer(); return DemoSuperPlayer();
} else { } else {
return DemoShortVideoPlayer(); return DemoShortVideoPlayer();
} }
......
...@@ -15,6 +15,8 @@ ...@@ -15,6 +15,8 @@
#define EVENT_PIP_MODE_RESTORE_UI 5 #define EVENT_PIP_MODE_RESTORE_UI 5
#define EVENT_PIP_MODE_WILL_EXIT 6 #define EVENT_PIP_MODE_WILL_EXIT 6
#define EVENT_PIP_PLAY_TIME @"playTime"
// 画中画错误事件code // 画中画错误事件code
#define NO_ERROR 0 ///< 无错误 #define NO_ERROR 0 ///< 无错误
#define ERROR_IOS_PIP_DEVICE_NOT_SUPPORT -104 ///< 设备或系统版本不支持(iPad iOS9+ 才支持PIP) #define ERROR_IOS_PIP_DEVICE_NOT_SUPPORT -104 ///< 设备或系统版本不支持(iPad iOS9+ 才支持PIP)
......
...@@ -17,7 +17,7 @@ NS_ASSUME_NONNULL_BEGIN ...@@ -17,7 +17,7 @@ NS_ASSUME_NONNULL_BEGIN
- (void)onPlayerPipStateDidStop; - (void)onPlayerPipStateDidStop;
- (void)onPlayerPipStateRestoreUI; - (void)onPlayerPipStateRestoreUI:(double)playTime;;
- (void)onPlayerPipStateError:(NSInteger)errorId; - (void)onPlayerPipStateError:(NSInteger)errorId;
......
...@@ -39,6 +39,8 @@ static const int CODE_ON_RECEIVE_FIRST_FRAME = 2003; ...@@ -39,6 +39,8 @@ static const int CODE_ON_RECEIVE_FIRST_FRAME = 2003;
id<FlutterPluginRegistrar> _registrar; id<FlutterPluginRegistrar> _registrar;
id<FlutterTextureRegistry> _textureRegistry; id<FlutterTextureRegistry> _textureRegistry;
float currentPlayTime;
} }
...@@ -417,7 +419,7 @@ BOOL volatile isStop = false; ...@@ -417,7 +419,7 @@ BOOL volatile isStop = false;
result(@(index)); result(@(index));
} }
else if([@"getPlayableDuration" isEqualToString:call.method]){ else if([@"getPlayableDuration" isEqualToString:call.method]){
float time = [self getCurrentPlaybackTime]; float time = [self getPlayableDuration];
result(@(time)); result(@(time));
} }
else if([@"getDuration" isEqualToString:call.method]){ else if([@"getDuration" isEqualToString:call.method]){
...@@ -454,6 +456,12 @@ BOOL volatile isStop = false; ...@@ -454,6 +456,12 @@ BOOL volatile isStop = false;
result(nil); result(nil);
} }
} }
else if ([@"exitPictureInPictureMode" isEqualToString:call.method]) {
if(_txVodPlayer != nil) {
[_txVodPlayer exitPictureInPicture];
}
result(nil);
}
else { else {
result(FlutterMethodNotImplemented); result(FlutterMethodNotImplemented);
} }
...@@ -522,7 +530,12 @@ BOOL volatile isStop = false; ...@@ -522,7 +530,12 @@ BOOL volatile isStop = false;
{ {
// 交给flutter共享纹理处理首帧事件返回时机 // 交给flutter共享纹理处理首帧事件返回时机
if (EvtID == CODE_ON_RECEIVE_FIRST_FRAME) { if (EvtID == CODE_ON_RECEIVE_FIRST_FRAME) {
currentPlayTime = 0;
return; return;
} else if(EvtID == PLAY_EVT_PLAY_PROGRESS) {
currentPlayTime = [param[EVT_PLAY_PROGRESS] floatValue];
} else if(EvtID == PLAY_EVT_PLAY_BEGIN) {
currentPlayTime = 0;
} }
[_eventSink success:[FTXVodPlayer getParamsWithEvent:EvtID withParams:param]]; [_eventSink success:[FTXVodPlayer getParamsWithEvent:EvtID withParams:param]];
...@@ -769,14 +782,13 @@ BOOL volatile isStop = false; ...@@ -769,14 +782,13 @@ BOOL volatile isStop = false;
if (pipState == TX_VOD_PLAYER_PIP_STATE_RESTORE_UI) { if (pipState == TX_VOD_PLAYER_PIP_STATE_RESTORE_UI) {
self.restoreUI = YES; self.restoreUI = YES;
if (self.delegate && [self.delegate respondsToSelector:@selector(onPlayerPipStateRestoreUI)]) {
[self.delegate onPlayerPipStateRestoreUI];
}
dispatch_async(dispatch_get_main_queue(), ^{ dispatch_async(dispatch_get_main_queue(), ^{
[player exitPictureInPicture]; [player exitPictureInPicture];
[self->_txVodPlayer resume]; [self->_txVodPlayer resume];
}); });
if (self.delegate && [self.delegate respondsToSelector:@selector(onPlayerPipStateRestoreUI:)]) {
[self.delegate onPlayerPipStateRestoreUI:currentPlayTime];
}
} }
} }
......
...@@ -63,7 +63,7 @@ SuperPlayerPlugin* instance; ...@@ -63,7 +63,7 @@ SuperPlayerPlugin* instance;
_eventSink = [FTXPlayerEventSinkQueue new]; _eventSink = [FTXPlayerEventSinkQueue new];
_pipEventSink = [FTXPlayerEventSinkQueue new]; _pipEventSink = [FTXPlayerEventSinkQueue new];
_eventChannel = [FlutterEventChannel eventChannelWithName:@"cloud.tencent.com/playerPlugin/event" binaryMessenger:[registrar messenger]]; _eventChannel = [FlutterEventChannel eventChannelWithName:@"cloud.tencent.com/playerPlugin/event" binaryMessenger:[registrar messenger]];
_pipEventChannel = [FlutterEventChannel eventChannelWithName:@"cloud.tencent.com/playerPlugin/pipEvent" binaryMessenger:[registrar messenger]]; _pipEventChannel = [FlutterEventChannel eventChannelWithName:@"cloud.tencent.com/playerPlugin/componentEvent" binaryMessenger:[registrar messenger]];
[_eventChannel setStreamHandler:self]; [_eventChannel setStreamHandler:self];
[_pipEventChannel setStreamHandler:self]; [_pipEventChannel setStreamHandler:self];
...@@ -194,7 +194,7 @@ SuperPlayerPlugin* instance; ...@@ -194,7 +194,7 @@ SuperPlayerPlugin* instance;
result(@(setResult)); result(@(setResult));
} else if([@"startVideoOrientationService" isEqualToString:call.method]) { } else if([@"startVideoOrientationService" isEqualToString:call.method]) {
// only for android // only for android
result(@(true)); result(@(YES));
} else { } else {
result(FlutterMethodNotImplemented); result(FlutterMethodNotImplemented);
} }
...@@ -263,8 +263,8 @@ SuperPlayerPlugin* instance; ...@@ -263,8 +263,8 @@ SuperPlayerPlugin* instance;
[_pipEventSink success:@{@"event" : @(errorId)}]; [_pipEventSink success:@{@"event" : @(errorId)}];
} }
- (void)onPlayerPipStateRestoreUI { - (void)onPlayerPipStateRestoreUI:(double)playTime {
[_pipEventSink success:@{@"event" : @(EVENT_PIP_MODE_RESTORE_UI)}]; [_pipEventSink success:@{@"event" : @(EVENT_PIP_MODE_RESTORE_UI), EVENT_PIP_PLAY_TIME : @(playTime)}];
} }
#pragma mark - orientation #pragma mark - orientation
......
...@@ -28,7 +28,7 @@ class SuperPlayerPlugin { ...@@ -28,7 +28,7 @@ class SuperPlayerPlugin {
EventChannel eventChannel = EventChannel("cloud.tencent.com/playerPlugin/event"); EventChannel eventChannel = EventChannel("cloud.tencent.com/playerPlugin/event");
eventChannel.receiveBroadcastStream("event").listen(_eventHandler, onError: _errorHandler); eventChannel.receiveBroadcastStream("event").listen(_eventHandler, onError: _errorHandler);
EventChannel pipEventChanne = EventChannel("cloud.tencent.com/playerPlugin/pipEvent"); EventChannel pipEventChanne = EventChannel("cloud.tencent.com/playerPlugin/componentEvent");
pipEventChanne.receiveBroadcastStream("pipEvent").listen(_pipEventHandler, onError: _errorHandler); pipEventChanne.receiveBroadcastStream("pipEvent").listen(_pipEventHandler, onError: _errorHandler);
} }
......
...@@ -311,20 +311,14 @@ class TXLivePlayerController extends ChangeNotifier implements ValueListenable<T ...@@ -311,20 +311,14 @@ class TXLivePlayerController extends ChangeNotifier implements ValueListenable<T
String? playIconForAndroid, String? playIconForAndroid,
String? pauseIconForAndroid, String? pauseIconForAndroid,
String? forwardIconForAndroid}) async { String? forwardIconForAndroid}) async {
if (_isNeedDisposed) return -1; /// live not support
await _initPlayer.future;
if (Platform.isAndroid) {
return await _channel.invokeMethod("enterPictureInPictureMode", {
"backIcon": backIconForAndroid,
"playIcon": playIconForAndroid,
"pauseIcon": pauseIconForAndroid,
"forwardIcon": forwardIconForAndroid
});
} else if (Platform.isIOS) {
return -1;
} else {
return -1; return -1;
} }
/// 退出画中画,如果该播放器处于画中画模式
@override
Future<void> exitPictureInPictureMode() async {
/// live not support
} }
/// 释放播放器资源占用 /// 释放播放器资源占用
......
...@@ -23,4 +23,6 @@ abstract class TXPlayerController { ...@@ -23,4 +23,6 @@ abstract class TXPlayerController {
Future<bool> enableHardwareDecode(bool enable); Future<bool> enableHardwareDecode(bool enable);
Future<int> enterPictureInPictureMode( Future<int> enterPictureInPictureMode(
{String? backIconForAndroid, String? playIconForAndroid, String? pauseIconForAndroid, String? forwardIconForAndroid}); {String? backIconForAndroid, String? playIconForAndroid, String? pauseIconForAndroid, String? forwardIconForAndroid});
Future<void> exitPictureInPictureMode();
void dispose();
} }
\ No newline at end of file
...@@ -304,6 +304,14 @@ class TXVodDownloadMedialnfo { ...@@ -304,6 +304,14 @@ class TXVodDownloadMedialnfo {
} }
} }
///
/// 播放器类型
///
abstract class TXPlayerType {
static const VOD_PLAY = 0;
static const LIVE_PLAY = 1;
}
//视频预下载事件回调Listener //视频预下载事件回调Listener
typedef FTXPredownlodOnCompleteListener = void Function(int taskId, String url); typedef FTXPredownlodOnCompleteListener = void Function(int taskId, String url);
typedef FTXPredownlodOnErrorListener = void Function(int taskId, String url, int code, String msg); typedef FTXPredownlodOnErrorListener = void Function(int taskId, String url, int code, String msg);
......
...@@ -424,6 +424,14 @@ class TXVodPlayerController extends ChangeNotifier implements ValueListenable<TX ...@@ -424,6 +424,14 @@ class TXVodPlayerController extends ChangeNotifier implements ValueListenable<TX
return await _channel.invokeMethod("getDuration"); return await _channel.invokeMethod("getDuration");
} }
/// 退出画中画,如果该播放器处于画中画模式
@override
Future<void> exitPictureInPictureMode() async {
if (_isNeedDisposed) return;
await _initPlayer.future;
return await _channel.invokeMethod("exitPictureInPictureMode");
}
/// 释放controller /// 释放controller
@override @override
void dispose() async { void dispose() async {
...@@ -465,4 +473,5 @@ class TXVodPlayerController extends ChangeNotifier implements ValueListenable<TX ...@@ -465,4 +473,5 @@ class TXVodPlayerController extends ChangeNotifier implements ValueListenable<TX
double? videoTop = 0; double? videoTop = 0;
double? videoRight = 0; double? videoRight = 0;
double? videoBottom = 0; double? videoBottom = 0;
} }
...@@ -25,8 +25,10 @@ part 'cgi/playinfo_protocol.dart'; ...@@ -25,8 +25,10 @@ part 'cgi/playinfo_protocol.dart';
part 'cgi/super_vod_data_loader.dart'; part 'cgi/super_vod_data_loader.dart';
part 'model/superplayer_define.dart'; part 'model/superplayer_define.dart';
part 'model/superplayer_model.dart'; part 'model/superplayer_model.dart';
part 'model/txpipplayer_data.dart';
part 'tools/video_quality_utils.dart'; part 'tools/video_quality_utils.dart';
part 'tools/utils.dart'; part 'tools/utils.dart';
part 'tools/txpip_controller.dart';
part 'ui/superplayer_bottom_view.dart'; part 'ui/superplayer_bottom_view.dart';
part 'ui/superplayer_quality_view.dart'; part 'ui/superplayer_quality_view.dart';
part 'ui/superplayer_title_view.dart'; part 'ui/superplayer_title_view.dart';
......
// Copyright (c) 2022 Tencent. All rights reserved.
part of demo_super_player_lib;
/// 画中画状态存储参数
class TXPipPlayerData {
/// 画中画播放器实例,同时只能存在一个
final TXPlayerController _playerController;
int playerMode = TXPlayerType.VOD_PLAY;
TXPipPlayerData(this._playerController) {
playerMode = _playerController is TXVodPlayerController ? TXPlayerType.VOD_PLAY : playerMode = TXPlayerType.LIVE_PLAY;
}
}
...@@ -70,7 +70,7 @@ class SuperPlayerController { ...@@ -70,7 +70,7 @@ class SuperPlayerController {
} }
void _initVodPlayer() async { void _initVodPlayer() async {
_vodPlayerController = new TXVodPlayerController(); _vodPlayerController = TXVodPlayerController();
await _vodPlayerController.initialize(); await _vodPlayerController.initialize();
_setVodListener(); _setVodListener();
} }
...@@ -349,8 +349,9 @@ class SuperPlayerController { ...@@ -349,8 +349,9 @@ class SuperPlayerController {
_playVodUrl(videoUrl); _playVodUrl(videoUrl);
List<VideoQuality>? qualityList = protocol.getVideoQualityList(); List<VideoQuality>? qualityList = protocol.getVideoQualityList();
_isMultiBitrateStream = _isMultiBitrateStream = protocol.getResolutionNameList() != null ||
protocol.getResolutionNameList() != null || qualityList != null || (videoUrl != null && videoUrl.contains("m3u8")); qualityList != null ||
(videoUrl != null && videoUrl.contains("m3u8"));
_updateVideoQualityList(qualityList, protocol.getDefaultVideoQuality()); _updateVideoQualityList(qualityList, protocol.getDefaultVideoQuality());
} }
...@@ -416,7 +417,8 @@ class SuperPlayerController { ...@@ -416,7 +417,8 @@ class SuperPlayerController {
if (_playAction == SuperPlayerModel.PLAY_ACTION_PRELOAD) { if (_playAction == SuperPlayerModel.PLAY_ACTION_PRELOAD) {
await _vodPlayerController.setAutoPlay(isAutoPlay: false); await _vodPlayerController.setAutoPlay(isAutoPlay: false);
_playAction = SuperPlayerModel.PLAY_ACTION_AUTO_PLAY; _playAction = SuperPlayerModel.PLAY_ACTION_AUTO_PLAY;
} else if (_playAction == SuperPlayerModel.PLAY_ACTION_AUTO_PLAY || _playAction == SuperPlayerModel.PLAY_ACTION_MANUAL_PLAY) { } else if (_playAction == SuperPlayerModel.PLAY_ACTION_AUTO_PLAY ||
_playAction == SuperPlayerModel.PLAY_ACTION_MANUAL_PLAY) {
await _vodPlayerController.setAutoPlay(isAutoPlay: true); await _vodPlayerController.setAutoPlay(isAutoPlay: true);
} }
_setVodListener(); _setVodListener();
...@@ -586,7 +588,9 @@ class SuperPlayerController { ...@@ -586,7 +588,9 @@ class SuperPlayerController {
String title = ""; String title = "";
if (videoModel != null && null != videoModel!.title && videoModel!.title.isNotEmpty) { if (videoModel != null && null != videoModel!.title && videoModel!.title.isNotEmpty) {
title = videoModel!.title; title = videoModel!.title;
} else if (_currentProtocol != null && null != _currentProtocol!.getName() && _currentProtocol!.getName()!.isNotEmpty) { } else if (_currentProtocol != null &&
null != _currentProtocol!.getName() &&
_currentProtocol!.getName()!.isNotEmpty) {
title = _currentProtocol!.getName()!; title = _currentProtocol!.getName()!;
} }
return title; return title;
...@@ -622,7 +626,8 @@ class SuperPlayerController { ...@@ -622,7 +626,8 @@ class SuperPlayerController {
/// 是否是HTTP-FLV协议 /// 是否是HTTP-FLV协议
bool _isFLVPlay(String? videoURL) { bool _isFLVPlay(String? videoURL) {
return (null != videoURL && videoURL.startsWith("http://") || videoURL!.startsWith("https://")) && videoURL.contains(".flv"); return (null != videoURL && videoURL.startsWith("http://") || videoURL!.startsWith("https://")) &&
videoURL.contains(".flv");
} }
/// 重置播放器状态 /// 重置播放器状态
...@@ -655,11 +660,14 @@ class SuperPlayerController { ...@@ -655,11 +660,14 @@ class SuperPlayerController {
Future<void> releasePlayer() async { Future<void> releasePlayer() async {
// 先移除widget的事件监听 // 先移除widget的事件监听
_observer?.onDispose(); _observer?.onDispose();
resetPlayer();
playerStreamController.close(); playerStreamController.close();
// 如果处于画中画模式,播放器暂时不释放
if(!TXPipController.instance.isPlayerInPip(getCurrentController())) {
resetPlayer();
_vodPlayerController.dispose(); _vodPlayerController.dispose();
_livePlayerController.dispose(); _livePlayerController.dispose();
} }
}
/// return true : 执行了退出全屏等操作,消耗了返回事件 false:未消耗事件 /// return true : 执行了退出全屏等操作,消耗了返回事件 false:未消耗事件
bool onBackPress() { bool onBackPress() {
...@@ -740,14 +748,21 @@ class SuperPlayerController { ...@@ -740,14 +748,21 @@ class SuperPlayerController {
/// </h1> /// </h1>
/// @param backIcon playIcon pauseIcon forwardIcon 为播放后退、播放、暂停、前进的图标,如果赋值的话,将会使用传递的图标,否则 /// @param backIcon playIcon pauseIcon forwardIcon 为播放后退、播放、暂停、前进的图标,如果赋值的话,将会使用传递的图标,否则
/// 使用系统默认图标,只支持flutter本地资源图片,传递的时候,与flutter使用图片资源一致,例如: images/back_icon.png /// 使用系统默认图标,只支持flutter本地资源图片,传递的时候,与flutter使用图片资源一致,例如: images/back_icon.png
Future<int> enterPictureInPictureMode({String? backIcon, String? playIcon, String? pauseIcon, String? forwardIcon}) async { Future<int> enterPictureInPictureMode(
{String? backIcon, String? playIcon, String? pauseIcon, String? forwardIcon}) async {
if (_playerUIStatus == SuperPlayerUIStatus.WINDOW_MODE) { if (_playerUIStatus == SuperPlayerUIStatus.WINDOW_MODE) {
if (playerType == SuperPlayerType.VOD) { if (playerType == SuperPlayerType.VOD) {
return _vodPlayerController.enterPictureInPictureMode( return TXPipController.instance.enterPip(_vodPlayerController, _context,
backIconForAndroid: backIcon, playIconForAndroid: playIcon, pauseIconForAndroid: pauseIcon, forwardIconForAndroid: forwardIcon); backIconForAndroid: backIcon,
playIconForAndroid: playIcon,
pauseIconForAndroid: pauseIcon,
forwardIconForAndroid: forwardIcon);
} else { } else {
return _livePlayerController.enterPictureInPictureMode( return TXPipController.instance.enterPip(_livePlayerController, _context,
backIconForAndroid: backIcon, playIconForAndroid: playIcon, pauseIconForAndroid: pauseIcon, forwardIconForAndroid: forwardIcon); backIconForAndroid: backIcon,
playIconForAndroid: playIcon,
pauseIconForAndroid: pauseIcon,
forwardIconForAndroid: forwardIcon);
} }
} }
return TXVodPlayEvent.ERROR_PIP_CAN_NOT_ENTER; return TXVodPlayEvent.ERROR_PIP_CAN_NOT_ENTER;
...@@ -794,6 +809,14 @@ class SuperPlayerController { ...@@ -794,6 +809,14 @@ class SuperPlayerController {
} }
} }
/// 设置播放开始时间
Future<void> setStartTime(double startTime) async {
if (playerType == SuperPlayerType.VOD) {
startPos = startTime;
return await _vodPlayerController.setStartTime(startTime);
}
}
Future<void> setPlayRate(double rate) async { Future<void> setPlayRate(double rate) async {
currentPlayRate = rate; currentPlayRate = rate;
_vodPlayerController.setRate(rate); _vodPlayerController.setRate(rate);
......
// Copyright (c) 2022 Tencent. All rights reserved.
part of demo_super_player_lib;
typedef OnJumpToPipPlayer = void Function(Map params);
typedef OnPopCurrent = void Function();
///
/// 画中画控制器,单例,只能存在一个画中画
///
class TXPipController {
static const ARGUMENT_PIP_START_TIME = "argumentStartTime";
static TXPipController? _instance;
static TXPipController get instance => _sharedInstance();
/// 画中画播放器实例,同时只能存在一个
TXPipPlayerData? _playerData;
final Map<String, dynamic> _extParams = {};
TXPipPlayerRestorePage? _onPipEnterListener;
OnJumpToPipPlayer? _onJumpToPipPlayer;
StreamSubscription? _pipEventSubscription;
/// TXPipController
static TXPipController _sharedInstance() {
_instance ??= TXPipController._internal();
return _instance!;
}
TXPipController._internal();
Future<int> enterPip(TXPlayerController playerController, BuildContext context,
{String? backIconForAndroid,
String? playIconForAndroid,
String? pauseIconForAndroid,
String? forwardIconForAndroid}) async {
if (_playerData != null) {
await exitAndReleaseCurrentPip();
}
_playerData = TXPipPlayerData(playerController);
_pipEventSubscription = SuperPlayerPlugin.instance.onExtraEventBroadcast.listen((event) async {
int eventCode = event["event"];
if ((Platform.isIOS && eventCode == TXVodPlayEvent.EVENT_PIP_MODE_ALREADY_ENTER) ||
(Platform.isAndroid && eventCode == TXVodPlayEvent.EVENT_PIP_MODE_REQUEST_START)) {
_onPipEnterListener?.onNeedSavePipPageState(_extParams);
Navigator.of(context).pop();
} else if ((Platform.isIOS && eventCode == TXVodPlayEvent.EVENT_IOS_PIP_MODE_WILL_EXIT)
|| (Platform.isAndroid && eventCode == TXVodPlayEvent.EVENT_PIP_MODE_ALREADY_EXIT)) {
await exitAndReleaseCurrentPip();
} else if (eventCode == TXVodPlayEvent.EVENT_IOS_PIP_MODE_RESTORE_UI) {
_extParams[ARGUMENT_PIP_START_TIME] = event["playTime"];
await exitAndReleaseCurrentPip();
_onJumpToPipPlayer?.call(_extParams);
} else if(eventCode < 0) {
// pip enter failed
_pipEventSubscription?.cancel();
_playerData = null;
}
});
int enterResult = await _playerData!._playerController.enterPictureInPictureMode(
backIconForAndroid: backIconForAndroid,
playIconForAndroid: playIconForAndroid,
pauseIconForAndroid: pauseIconForAndroid,
forwardIconForAndroid: forwardIconForAndroid);
if (enterResult != TXVodPlayEvent.NO_ERROR) {
_playerData = null;
_pipEventSubscription?.cancel();
}
return enterResult;
}
Future<void> exitAndReleaseCurrentPip() async {
if (null != _playerData && _playerData?._playerController != null) {
if(Platform.isAndroid) {
await _playerData?._playerController.exitPictureInPictureMode();
}
await _playerData?._playerController.stop();
_playerData?._playerController.dispose();
}
_pipEventSubscription?.cancel();
_playerData = null;
}
/// 传入的controller是否处于画中画模式
bool isPlayerInPip(TXPlayerController playerController) {
if (null != _playerData) {
return _playerData!._playerController == playerController;
}
return false;
}
void setPipPlayerPage(Type a, TXPipPlayerRestorePage listener) {
_onPipEnterListener = listener;
}
void setNavigatorHandle(OnJumpToPipPlayer onJumpToPipPlayer) {
_onJumpToPipPlayer = onJumpToPipPlayer;
}
}
abstract class TXPipPlayerRestorePage {
/// 当需要保存画中画界面相关元素的时候,会回调该方法
void onNeedSavePipPageState(Map<String, dynamic> params);
}
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论