diff --git a/Demo/app/build.gradle b/Demo/app/build.gradle index 1276f51..8ad7c29 100644 --- a/Demo/app/build.gradle +++ b/Demo/app/build.gradle @@ -8,8 +8,8 @@ android { applicationId "com.tencent.liteav.demo" minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion - versionCode 14 - versionName "9.2.1029" + versionCode 24 + versionName "9.3.1169" multiDexEnabled true ndk { @@ -63,6 +63,4 @@ dependencies { exclude group: 'com.google.code.gson', module: 'gson' }) - compile 'com.tencent.bugly:crashreport_upgrade:1.5.1' - compile 'com.tencent.bugly:nativecrashreport:3.8.0' } \ No newline at end of file diff --git a/Demo/app/proguard-rules.pro b/Demo/app/proguard-rules.pro index 8d4438d..0673350 100644 --- a/Demo/app/proguard-rules.pro +++ b/Demo/app/proguard-rules.pro @@ -20,27 +20,3 @@ # hide the original source file name. #-renamesourcefileattribute SourceFile -keep class com.tencent.** { *; } - --dontwarn com.tencent.bugly.** --keep public class com.tencent.bugly.**{*;} - --keep public class * extends com.qq.taf.jce.JceStruct{*;} - -#wup的协议包: --keep public class com.qq.jce.*{ -public * ; -protected * ; -} - -#native jni接口 --keepclasseswithmembernames class * { - native ; -} - -#native 回调接口 --keep public interface com.tencent.feedback.eup.jni.NativeExceptionHandler{ -*; -} --keep public class com.tencent.feedback.eup.jni.NativeExceptionUpload{ -*; -} \ No newline at end of file diff --git a/Demo/app/src/main/assets/153307-yc.lrc b/Demo/app/src/main/assets/153307-yc.lrc new file mode 100644 index 0000000..a4c5ead --- /dev/null +++ b/Demo/app/src/main/assets/153307-yc.lrc @@ -0,0 +1,66 @@ +[00:00.00]北京欢迎你 +[00:02.96]作词:林夕 作曲:小柯 +[00:03.65]演唱:群星 +[00:05.96] +[00:13.88](陈天佳)迎接另一个晨曦 带来全新空气 +[00:19.66](刘欢)气息改变情味不变 茶香飘满情谊 +[00:25.71](那英)我家大门常打开 开放怀抱等你 +[00:31.82](孙燕姿)拥抱过就有了默契 你会爱上这里 +[00:38.04](孙悦)不管远近都是客人 请不用客气 +[00:43.79](王力宏)相约好了再一起 我们欢迎你 +[00:50.14](韩红)我家种着万年青 开放每段传奇 +[00:55.92](周华健)为传统的土壤播种 为你留下回忆 +[01:02.31](梁咏琪)陌生熟悉都是客人 请不用拘礼 +[01:08.13](羽泉)第几次来没关系 有太多话题 +[01:15.86](成龙)北京欢迎你 为你开天辟地 +[01:22.54](任贤齐)流动中的魅力充满着朝气 +[01:28.77](蔡依林)北京欢迎你 在太阳下分享呼吸 +[01:35.14](孙楠)在黄土地刷新成绩 +[01:40.43] +[02:05.36](周笔畅)我家大门常打开 开怀容纳天地 +[02:11.71](韦唯)岁月绽放青春笑容 迎接这个日期 +[02:17.76](黄晓明)天大地大都是朋友 请不用客气 +[02:23.72](韩庚)画意诗情带笑意 只为等待你 +[02:29.34](汪峰)北京欢迎你 像音乐感动你 +[02:35.36](莫文蔚)让我们都加油去超越自己 +[02:41.51](谭晶)北京欢迎你 有梦想谁都了不起 +[02:48.02](陈奕迅)有勇气就会有奇迹 +[02:53.87](阎维文)北京欢迎你 为你开天辟地 +[02:59.69](戴玉强)流动中的魅力充满着朝气 +[03:05.84](王霞.李双松)北京欢迎你 在太阳下分享呼吸 +[03:12.26](廖昌永)在黄土地刷新成绩 +[03:18.17](林依轮)北京欢迎你 像音乐感动你 +[03:23.96](张娜拉)让我们都加油去超越自己 +[03:29.78](林俊杰)北京欢迎你 有梦想谁都了不起 +[03:36.60](阿杜)有勇气就会有奇迹 +[03:41.74](京剧) 北京欢迎你呀 +[03:49.33](容祖儿)我家大门常打开 开放怀抱等你 +[03:55.11](李宇春)拥抱过就有了默契 你会爱上这里 +[04:01.38](黄大炜)不管远近都是客人 请不用客气 +[04:07.34](陈坤)相约好了再一起 我们欢迎你 +[04:12.96](谢霆锋)北京欢迎你 为你开天辟地 +[04:18.74](韩磊)流动中的魅力充满着朝气 +[04:24.89](徐若瑄)北京欢迎你 在太阳下分享呼吸 +[04:31.32](费翔)在黄土地刷新成绩 +[04:36.61] +[05:02.12](汤灿)我家大门常打开 开怀容纳天地 +[05:08.22](林志玲 张梓琳)岁月绽放青春笑容 迎接这个日期 +[05:14.18](张靓颖)天大地大都是朋友 请不用客气 +[05:20.25](许茹芸 伍思凯)画意诗情带笑意 只为等待你 +[05:25.92](杨坤.范玮琪)北京欢迎你 像音乐感动你 +[05:31.55](游鸿明.周晓欧)让我们都加油去超越自己 +[05:37.80](沙宝亮.满文军)北京欢迎你 有梦想谁都了不起 +[05:44.32](金海心.何润东)有勇气就会有奇迹 +[05:50.18](飞儿.庞龙)北京欢迎你 为你开天辟地 +[05:55.92](吴克群.齐峰)流动中的魅力充满着朝气 +[06:02.18](5566.胡彦斌)北京欢迎你 在太阳下分享呼吸 +[06:08.59](郑希怡.刀郎)在黄土地刷新成绩 +[06:14.46](纪敏 屠洪刚 吴彤)北京欢迎你 像音乐感动你 +[06:20.27](郭容 刘耕宏 腾格尔)让我们都加油去超越自己 +[06:26.61](金莎 苏醒 韦嘉)北京欢迎你 有梦想谁都了不起 +[06:33.06](付丽珊 黄征 房祖明)有勇气就会有奇迹 +[06:39.04](全体)北京欢迎你 有梦想谁都了不起 +[06:45.14](全体)有勇气就会有奇迹 +[06:50.87](全体)北京欢迎你 有梦想谁都了不起 +[06:57.13](全体)有勇气就会有奇迹 +[07:13.08] \ No newline at end of file diff --git a/Demo/app/src/main/assets/153307-yc.mp3 b/Demo/app/src/main/assets/153307-yc.mp3 new file mode 100644 index 0000000..5cc6574 Binary files /dev/null and b/Demo/app/src/main/assets/153307-yc.mp3 differ diff --git a/Demo/app/src/main/java/com/tencent/liteav/demo/DemoApplication.java b/Demo/app/src/main/java/com/tencent/liteav/demo/DemoApplication.java index a357303..10d16ed 100644 --- a/Demo/app/src/main/java/com/tencent/liteav/demo/DemoApplication.java +++ b/Demo/app/src/main/java/com/tencent/liteav/demo/DemoApplication.java @@ -1,29 +1,20 @@ package com.tencent.liteav.demo; -import android.app.ApplicationErrorReport; import android.content.Context; import android.os.Build; import android.os.StrictMode; + import androidx.multidex.MultiDexApplication; -import android.util.Log; -import com.tencent.bugly.Bugly; -import com.tencent.bugly.beta.Beta; -import com.tencent.bugly.beta.download.DownloadListener; -import com.tencent.bugly.beta.download.DownloadTask; -import com.tencent.bugly.beta.upgrade.UpgradeStateListener; -import com.tencent.bugly.crashreport.CrashReport; import com.tencent.rtmp.TXLiveBase; +import com.tencent.rtmp.TXLiveBaseListener; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Method; public class DemoApplication extends MultiDexApplication { - private static String TAG = "DemoApplication"; - private static final String BUGLY_APPID = ""; //配置bugly组件的appId - private static final String BUGLY_APP_CHANNEL = ""; // 配置bugly组件的APP渠道号 - private static final boolean BUGLY_ENABLE_DEBUG = true; //配置bugly组件的调试模式(true或者false) + private static String TAG = "DemoApplication"; // private RefWatcher mRefWatcher; private static DemoApplication instance; @@ -41,9 +32,17 @@ public void onCreate() { mAppContext = this.getApplicationContext(); instance = this; - TXLiveBase.setConsoleEnabled(true); - initBugly(); + TXLiveBase.getInstance().setLicence(instance, licenceUrl, licenseKey); + TXLiveBase.setListener(new TXLiveBaseListener() { + @Override + public void onUpdateNetworkTime(int errCode, String errMsg) { + if (errCode != 0) { + TXLiveBase.updateNetworkTime(); + } + } + }); + TXLiveBase.updateNetworkTime(); // 短视频licence设置 StrictMode.VmPolicy.Builder builder = new StrictMode.VmPolicy.Builder(); @@ -78,57 +77,4 @@ private void closeAndroidPDialog() { e.printStackTrace(); } } - - //配置bugly组件的APP ID,bugly组件为腾讯提供的用于crash上报,分析和升级的开放组件,如果您不需要该组件,可以自行移除 - private void initBugly() { - CrashReport.UserStrategy strategy = new CrashReport.UserStrategy(getApplicationContext()); - strategy.setAppVersion(TXLiveBase.getSDKVersionStr()); - strategy.setAppChannel(BUGLY_APP_CHANNEL); - //监听安装包下载状态 - Beta.downloadListener = new DownloadListener() { - @Override - public void onReceive(DownloadTask downloadTask) { - } - - @Override - public void onCompleted(DownloadTask downloadTask) { - Log.d(TAG, "downloadListener download apk file success"); - } - - @Override - public void onFailed(DownloadTask downloadTask, int i, String s) { - Log.d(TAG, "downloadListener download apk file fail"); - } - }; - - //监听APP升级状态 - Beta.upgradeStateListener = new UpgradeStateListener() { - @Override - public void onUpgradeFailed(boolean b) { - Log.d(TAG, "upgradeStateListener upgrade failed"); - } - - @Override - public void onUpgradeSuccess(boolean b) { - Log.d(TAG, "upgradeStateListener upgrade success"); - } - - @Override - public void onUpgradeNoVersion(boolean b) { - Log.d(TAG, "upgradeStateListener upgrade has no new version"); - } - - @Override - public void onUpgrading(boolean b) { - Log.d(TAG, "upgradeStateListener upgrading"); - } - - @Override - public void onDownloadCompleted(boolean b) { - Log.d(TAG, "upgradeStateListener download apk file success"); - } - }; - Bugly.init(getApplicationContext(), BUGLY_APPID, BUGLY_ENABLE_DEBUG, strategy); - } - } \ No newline at end of file diff --git a/Demo/app/src/main/java/com/tencent/liteav/demo/MainActivity.java b/Demo/app/src/main/java/com/tencent/liteav/demo/MainActivity.java index bdb86de..9bea9e8 100644 --- a/Demo/app/src/main/java/com/tencent/liteav/demo/MainActivity.java +++ b/Demo/app/src/main/java/com/tencent/liteav/demo/MainActivity.java @@ -7,8 +7,6 @@ import android.net.Uri; import android.os.AsyncTask; import android.os.Bundle; -import androidx.recyclerview.widget.LinearLayoutManager; -import androidx.recyclerview.widget.RecyclerView; import android.util.Log; import android.view.LayoutInflater; import android.view.View; @@ -16,9 +14,12 @@ import android.widget.ImageView; import android.widget.TextView; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; import com.tencent.liteav.demo.common.widget.expandableadapter.BaseExpandableRecyclerViewAdapter; import com.tencent.liteav.demo.player.demo.SuperPlayerActivity; +import com.tencent.liteav.demo.player.demo.shortvideo.view.ShortVideoActivity; import com.tencent.rtmp.TXLiveBase; import java.io.File; @@ -34,9 +35,9 @@ public class MainActivity extends Activity { private static final String TAG = "MainActivity"; - private TextView mMainTitle; - private TextView mTvVersion; - private RecyclerView mRvList; + private TextView mMainTitle; + private TextView mTvVersion; + private RecyclerView mRvList; private MainExpandableAdapter mAdapter; @Override @@ -52,7 +53,7 @@ protected void onCreate(Bundle savedInstanceState) { setContentView(R.layout.activity_main); mTvVersion = (TextView) findViewById(R.id.main_tv_version); - mTvVersion.setText(getString(R.string.app_tv_super_player_version, TXLiveBase.getSDKVersionStr()+"(9.2.1029)")); + mTvVersion.setText(getString(R.string.app_tv_super_player_version, TXLiveBase.getSDKVersionStr()+"(9.3.1169)")); mMainTitle = (TextView) findViewById(R.id.main_title); mMainTitle.setOnLongClickListener(new View.OnLongClickListener() { @@ -137,6 +138,7 @@ private List initGroupData() { // 初始化播放器 List playerChildList = new ArrayList<>(); playerChildList.add(new ChildBean(getString(R.string.app_item_super_player), R.drawable.play, 3, SuperPlayerActivity.class)); + playerChildList.add(new ChildBean(getString(R.string.app_item_shortvideo_player), R.drawable.play, 3, ShortVideoActivity.class)); if (playerChildList.size() != 0) { GroupBean playerGroupBean = new GroupBean(getString(R.string.app_item_player), R.drawable.composite, playerChildList); groupList.add(playerGroupBean); @@ -148,7 +150,7 @@ private List initGroupData() { private static class MainExpandableAdapter extends BaseExpandableRecyclerViewAdapter { private List mListGroupBean; - private GroupBean mGroupBean; + private GroupBean mGroupBean; public void setSelectedChildBean(GroupBean groupBean) { boolean isExpand = isExpand(groupBean); @@ -222,7 +224,7 @@ public void onBindChildViewHolder(ChildVH holder, GroupBean groupBean, ChildBean public static class GroupVH extends BaseExpandableRecyclerViewAdapter.BaseGroupViewHolder { ImageView ivLogo; - TextView textView; + TextView textView; GroupVH(View itemView) { super(itemView); @@ -238,7 +240,7 @@ protected void onExpandStatusChanged(RecyclerView.Adapter relatedAdapter, boolea public static class ChildVH extends RecyclerView.ViewHolder { TextView textView; - View divideView; + View divideView; ChildVH(View itemView) { super(itemView); @@ -249,9 +251,9 @@ public static class ChildVH extends RecyclerView.ViewHolder { } private class GroupBean implements BaseExpandableRecyclerViewAdapter.BaseGroupBean { - private String mName; + private String mName; private List mChildList; - private int mIconId; + private int mIconId; public GroupBean(String name, int iconId, List list) { mName = name; @@ -289,9 +291,9 @@ public int getIconId() { private class ChildBean { public String mName; - public int mIconId; - public Class mTargetClass; - public int mType; + public int mIconId; + public Class mTargetClass; + public int mType; public ChildBean(String name, int iconId, int type, Class targetActivityClass) { this.mName = name; diff --git a/Demo/app/src/main/res/layout/app_activity_my_info.xml b/Demo/app/src/main/res/layout/app_activity_my_info.xml index cd05712..bce3ca0 100644 --- a/Demo/app/src/main/res/layout/app_activity_my_info.xml +++ b/Demo/app/src/main/res/layout/app_activity_my_info.xml @@ -43,6 +43,8 @@ android:id="@+id/tv_show_name" android:layout_width="wrap_content" android:layout_height="wrap_content" + android:maxWidth="300dp" + android:singleLine="true" android:layout_marginTop="10dp" android:textColor="@color/app_color_white" android:textSize="18sp" diff --git a/Demo/app/src/main/res/values-en/strings.xml b/Demo/app/src/main/res/values-en/strings.xml index 617e39c..4aebae1 100755 --- a/Demo/app/src/main/res/values-en/strings.xml +++ b/Demo/app/src/main/res/values-en/strings.xml @@ -41,6 +41,7 @@ MLVB Superplayer + Short Video Player Player Shooting diff --git a/Demo/app/src/main/res/values-zh/strings.xml b/Demo/app/src/main/res/values-zh/strings.xml index 1277c0f..692427a 100755 --- a/Demo/app/src/main/res/values-zh/strings.xml +++ b/Demo/app/src/main/res/values-zh/strings.xml @@ -44,6 +44,7 @@ 超级播放器 播放器 + 短视频播放 视频录制 特效编辑 diff --git a/Demo/app/src/main/res/values/strings.xml b/Demo/app/src/main/res/values/strings.xml index 77cc388..130cf2a 100644 --- a/Demo/app/src/main/res/values/strings.xml +++ b/Demo/app/src/main/res/values/strings.xml @@ -44,6 +44,7 @@ 超级播放器 播放器 + 短视频播放 视频录制 特效编辑 diff --git a/Demo/superplayerdemo/build.gradle b/Demo/superplayerdemo/build.gradle index 633dca5..a48b2dc 100644 --- a/Demo/superplayerdemo/build.gradle +++ b/Demo/superplayerdemo/build.gradle @@ -34,6 +34,7 @@ dependencies { compile 'com.squareup.okhttp3:okhttp:3.11.0' compile 'androidx.multidex:multidex:2.0.0' compile 'com.github.bumptech.glide:glide:3.7.0' + compile 'androidx.cardview:cardview:1.0.0' compile 'com.google.code.gson:gson:2.3.1' compile('com.blankj:utilcode:1.25.9', { exclude group: 'com.google.code.gson', module: 'gson' diff --git a/Demo/superplayerdemo/src/main/AndroidManifest.xml b/Demo/superplayerdemo/src/main/AndroidManifest.xml index b6664be..3da4ca9 100644 --- a/Demo/superplayerdemo/src/main/AndroidManifest.xml +++ b/Demo/superplayerdemo/src/main/AndroidManifest.xml @@ -17,7 +17,14 @@ - + + diff --git a/Demo/superplayerdemo/src/main/java/com/tencent/liteav/demo/player/demo/SuperPlayerActivity.java b/Demo/superplayerdemo/src/main/java/com/tencent/liteav/demo/player/demo/SuperPlayerActivity.java index eee4edc..8c803cc 100644 --- a/Demo/superplayerdemo/src/main/java/com/tencent/liteav/demo/player/demo/SuperPlayerActivity.java +++ b/Demo/superplayerdemo/src/main/java/com/tencent/liteav/demo/player/demo/SuperPlayerActivity.java @@ -42,6 +42,7 @@ import com.tencent.liteav.demo.player.expand.model.utils.SuperVodListLoader; import com.tencent.liteav.demo.player.expand.ui.TCVodPlayerListAdapter; import com.tencent.liteav.demo.player.expand.model.entity.VideoModel; +import com.tencent.liteav.demo.superplayer.model.VipWatchModel; import com.tencent.rtmp.TXLiveBase; import com.tencent.rtmp.TXLiveConstants; @@ -345,7 +346,7 @@ public void run() { private void updateVodList() { if (mDefaultVideo) { - ArrayList superPlayerModels = mSuperVodListLoader.loadDefaultVodList(); + ArrayList superPlayerModels = mSuperVodListLoader.loadDefaultVodList(this.getApplicationContext()); mSuperVodListLoader.getVodInfoOneByOne(superPlayerModels); mImageAdd.setVisibility(VISIBLE); } else { @@ -432,7 +433,9 @@ protected void onResume() { if (mSuperPlayerView.getPlayerState() == SuperPlayerDef.PlayerState.PLAYING || mSuperPlayerView.getPlayerState() == SuperPlayerDef.PlayerState.PAUSE) { Log.i(TAG, "onResume state :" + mSuperPlayerView.getPlayerState()); - mSuperPlayerView.onResume(); + if(!mSuperPlayerView.isShowingVipView()) { + mSuperPlayerView.onResume(); + } if (mSuperPlayerView.getPlayerMode() == SuperPlayerDef.PlayerMode.FLOAT) { mSuperPlayerView.switchPlayMode(SuperPlayerDef.PlayerMode.WINDOW); } @@ -506,6 +509,7 @@ public void onItemClick(int position, final VideoModel videoModel) { private void playVideoModel(VideoModel videoModel) { final SuperPlayerModel superPlayerModelV3 = new SuperPlayerModel(); superPlayerModelV3.appId = videoModel.appid; + superPlayerModelV3.vipWatchMode=videoModel.vipWatchModel; if (!TextUtils.isEmpty(videoModel.videoURL)) { if (isSuperPlayerVideo(videoModel)) { playSuperPlayerVideo(videoModel); diff --git a/Demo/superplayerdemo/src/main/java/com/tencent/liteav/demo/player/demo/shortvideo/adapter/AbsPlayerRecyclerViewAdapter.java b/Demo/superplayerdemo/src/main/java/com/tencent/liteav/demo/player/demo/shortvideo/adapter/AbsPlayerRecyclerViewAdapter.java new file mode 100644 index 0000000..c1e1bce --- /dev/null +++ b/Demo/superplayerdemo/src/main/java/com/tencent/liteav/demo/player/demo/shortvideo/adapter/AbsPlayerRecyclerViewAdapter.java @@ -0,0 +1,67 @@ +package com.tencent.liteav.demo.player.demo.shortvideo.adapter; + +import android.content.Context; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +import androidx.recyclerview.widget.RecyclerView; + +import java.util.List; + +public abstract class AbsPlayerRecyclerViewAdapter extends RecyclerView.Adapter { + + private List mList; + public Context mContext; + + public AbsPlayerRecyclerViewAdapter(List list) { + this.mList = list; + } + + @Override + public K onCreateViewHolder(ViewGroup parent, int viewType) { + mContext = parent.getContext(); + K holder = onCreateHolder(parent); + + bindListener(holder); + return holder; + } + + @Override + public void onBindViewHolder(K holder, int position) { + onHolder(holder, mList.get(position), position); + } + + @Override + public int getItemViewType(int position) { + return position; + } + + @Override + public int getItemCount() { + return mList == null ? 0 : mList.size(); + } + + + public abstract void onHolder(K holder, T bean, int position); + + public abstract K onCreateHolder(ViewGroup parent); + + + public View getViewByRes(int res, ViewGroup parent) { + return LayoutInflater.from(mContext).inflate(res, parent, false); + } + + + private void bindListener(final K holder) { + if (holder == null) { + return; + } + View itemView = holder.itemView; + if (itemView == null) { + return; + } + ViewGroup.LayoutParams params = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT); + itemView.setLayoutParams(params); + } +} diff --git a/Demo/superplayerdemo/src/main/java/com/tencent/liteav/demo/player/demo/shortvideo/adapter/AbsViewHolder.java b/Demo/superplayerdemo/src/main/java/com/tencent/liteav/demo/player/demo/shortvideo/adapter/AbsViewHolder.java new file mode 100644 index 0000000..5aa52e7 --- /dev/null +++ b/Demo/superplayerdemo/src/main/java/com/tencent/liteav/demo/player/demo/shortvideo/adapter/AbsViewHolder.java @@ -0,0 +1,12 @@ +package com.tencent.liteav.demo.player.demo.shortvideo.adapter; + +import android.view.View; + +import androidx.recyclerview.widget.RecyclerView; + + +public abstract class AbsViewHolder extends RecyclerView.ViewHolder { + public AbsViewHolder(View itemView) { + super(itemView); + } +} diff --git a/Demo/superplayerdemo/src/main/java/com/tencent/liteav/demo/player/demo/shortvideo/adapter/ShortVideoListAdapter.java b/Demo/superplayerdemo/src/main/java/com/tencent/liteav/demo/player/demo/shortvideo/adapter/ShortVideoListAdapter.java new file mode 100644 index 0000000..60eadb0 --- /dev/null +++ b/Demo/superplayerdemo/src/main/java/com/tencent/liteav/demo/player/demo/shortvideo/adapter/ShortVideoListAdapter.java @@ -0,0 +1,132 @@ +package com.tencent.liteav.demo.player.demo.shortvideo.adapter; + +import android.content.Context; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ImageView; +import android.widget.TextView; + +import androidx.recyclerview.widget.RecyclerView; + +import com.bumptech.glide.Glide; +import com.bumptech.glide.load.engine.DiskCacheStrategy; +import com.tencent.liteav.demo.player.R; +import com.tencent.liteav.demo.player.demo.shortvideo.bean.ShortVideoBean; + +import java.util.List; + +/** + * 列表页面的RecyclerView的Adapter + */ +public class ShortVideoListAdapter extends RecyclerView.Adapter { + private static final String TAG = "ShortVideoDemo:ShortVideoListAdapter"; + private Context mContext; + private List mPlayerModelList; + private IOnItemClickListener mIOnItemClickListener; + + public ShortVideoListAdapter(Context context, IOnItemClickListener onItemClickListener, List shortVideoBeanList) { + mContext = context; + mIOnItemClickListener = onItemClickListener; + mPlayerModelList = shortVideoBeanList; + } + + @Override + public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { + View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.player_item_short_video_list, parent, false); + return new ViewHolder(view); + } + + @Override + public void onBindViewHolder(final ViewHolder holder, final int pos) { + final ShortVideoBean videoModel = mPlayerModelList.get(pos); + Glide.with(mContext).load(videoModel.placeholderImage). + diskCacheStrategy(DiskCacheStrategy.ALL).centerCrop() + .placeholder(R.color.superplayer_color_gray) + .into(holder.mThumb); + if (videoModel.duration > 0) { + String tempString = formattedTime(videoModel.duration); + holder.mDuration.setText(tempString); + } else { + holder.mDuration.setText(""); + } + if (videoModel.title != null) { + holder.mTitle.setText(videoModel.title); + } + holder.mTitle.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + if (mIOnItemClickListener != null) { + mIOnItemClickListener.onItemClick(pos); + } + } + }); + holder.mThumb.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + if (mIOnItemClickListener != null) { + mIOnItemClickListener.onItemClick(pos); + } + } + }); + } + + @Override + public int getItemCount() { + return mPlayerModelList.size(); + } + + + public class ViewHolder extends RecyclerView.ViewHolder { + private TextView mDuration; + private TextView mTitle; + private ImageView mThumb; + + public ViewHolder(final View itemView) { + super(itemView); + mThumb = itemView.findViewById(R.id.iv_short_video); + mTitle = itemView.findViewById(R.id.tv_short_video); + mDuration = itemView.findViewById(R.id.tv_short_video_duration); + } + } + + + public interface IOnItemClickListener { + void onItemClick(int position); + } + + /** + * 将 秒转换成 h m s + * + * @param second + * @return + */ + private static String formattedTime(long second) { + String hs, ms, ss, formatTime; + long h, m, s; + h = second / 3600; + m = (second % 3600) / 60; + s = (second % 3600) % 60; + if (h < 10) { + hs = "0" + h; + } else { + hs = "" + h; + } + if (m < 10) { + ms = "0" + m; + } else { + ms = "" + m; + } + if (s < 10) { + ss = "0" + s; + } else { + ss = "" + s; + } + if (h > 0) { + formatTime = hs + ":" + ms + ":" + ss; + } else { + formatTime = ms + ":" + ss; + } + return formatTime; + } +} diff --git a/Demo/superplayerdemo/src/main/java/com/tencent/liteav/demo/player/demo/shortvideo/adapter/ShortVideoPageAdapter.java b/Demo/superplayerdemo/src/main/java/com/tencent/liteav/demo/player/demo/shortvideo/adapter/ShortVideoPageAdapter.java new file mode 100644 index 0000000..2bfd966 --- /dev/null +++ b/Demo/superplayerdemo/src/main/java/com/tencent/liteav/demo/player/demo/shortvideo/adapter/ShortVideoPageAdapter.java @@ -0,0 +1,28 @@ +package com.tencent.liteav.demo.player.demo.shortvideo.adapter; + + +import androidx.fragment.app.Fragment; +import androidx.fragment.app.FragmentManager; +import androidx.fragment.app.FragmentPagerAdapter; + +import java.util.List; + +public class ShortVideoPageAdapter extends FragmentPagerAdapter { + List mFragments; + + public ShortVideoPageAdapter(FragmentManager fm, List fragments) { + super(fm); + this.mFragments = fragments; + } + + @Override + public Fragment getItem(int position) { + return mFragments.get(position); + } + + @Override + public int getCount() { + return mFragments.size(); + } + +} diff --git a/Demo/superplayerdemo/src/main/java/com/tencent/liteav/demo/player/demo/shortvideo/adapter/ShortVideoPlayAdapter.java b/Demo/superplayerdemo/src/main/java/com/tencent/liteav/demo/player/demo/shortvideo/adapter/ShortVideoPlayAdapter.java new file mode 100644 index 0000000..ce8f9b0 --- /dev/null +++ b/Demo/superplayerdemo/src/main/java/com/tencent/liteav/demo/player/demo/shortvideo/adapter/ShortVideoPlayAdapter.java @@ -0,0 +1,62 @@ +package com.tencent.liteav.demo.player.demo.shortvideo.adapter; + +import android.view.View; +import android.view.ViewGroup; +import android.widget.ImageView; + +import androidx.annotation.NonNull; + +import com.bumptech.glide.Glide; +import com.bumptech.glide.load.engine.DiskCacheStrategy; +import com.tencent.liteav.demo.player.R; +import com.tencent.liteav.demo.player.demo.shortvideo.bean.ShortVideoBean; +import com.tencent.liteav.demo.player.demo.shortvideo.view.TXVideoBaseView; +import com.tencent.rtmp.TXLog; +import com.tencent.rtmp.ui.TXCloudVideoView; + +import java.util.List; + +public class ShortVideoPlayAdapter extends AbsPlayerRecyclerViewAdapter { + + private static final String TAG = "ShortVideoDemo:ShortVideoPlayAdapter"; + + public ShortVideoPlayAdapter(List list) { + super(list); + } + + @Override + public void onHolder(VideoViewHolder holder, ShortVideoBean bean, int position) { + ViewGroup.LayoutParams layoutParams = holder.itemView.getLayoutParams(); + layoutParams.height = ViewGroup.LayoutParams.MATCH_PARENT; + Glide.with(mContext).load(bean.placeholderImage). + diskCacheStrategy(DiskCacheStrategy.ALL).centerCrop() + .into(holder.mImageViewCover); + } + + + @Override + public VideoViewHolder onCreateHolder(ViewGroup viewGroup) { + return new VideoViewHolder(getViewByRes(R.layout.player_item_short_video_play, viewGroup)); + } + + @Override + public void onViewDetachedFromWindow(@NonNull ShortVideoPlayAdapter.VideoViewHolder holder) { + super.onViewDetachedFromWindow(holder); + TXLog.i(TAG,"onViewDetachedFromWindow"); + TXVideoBaseView videoView = (TXVideoBaseView) holder.mRootView.findViewById(R.id.baseItemView); + videoView.stopPlayer(); + } + + public class VideoViewHolder extends AbsViewHolder { + public View mRootView; + public ImageView mImageViewCover; + public TXCloudVideoView mVideoView; + + public VideoViewHolder(View rootView) { + super(rootView); + this.mRootView = rootView; + this.mImageViewCover = rootView.findViewById(R.id.iv_cover); + this.mVideoView = rootView.findViewById(R.id.tcv_video_view); + } + } +} diff --git a/Demo/superplayerdemo/src/main/java/com/tencent/liteav/demo/player/demo/shortvideo/base/AbsBaseActivity.java b/Demo/superplayerdemo/src/main/java/com/tencent/liteav/demo/player/demo/shortvideo/base/AbsBaseActivity.java new file mode 100644 index 0000000..205a394 --- /dev/null +++ b/Demo/superplayerdemo/src/main/java/com/tencent/liteav/demo/player/demo/shortvideo/base/AbsBaseActivity.java @@ -0,0 +1,25 @@ +package com.tencent.liteav.demo.player.demo.shortvideo.base; + +import android.os.Bundle; + +import androidx.annotation.Nullable; +import androidx.appcompat.app.AppCompatActivity; + +public abstract class AbsBaseActivity extends AppCompatActivity { + + protected abstract void initLayout(@Nullable Bundle savedInstanceState); + + protected abstract void initView(); + + protected abstract void initData(); + + + @Override + protected void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + initLayout(savedInstanceState); + initView(); + initData(); + } + +} diff --git a/Demo/superplayerdemo/src/main/java/com/tencent/liteav/demo/player/demo/shortvideo/base/AbsBaseFragment.java b/Demo/superplayerdemo/src/main/java/com/tencent/liteav/demo/player/demo/shortvideo/base/AbsBaseFragment.java new file mode 100644 index 0000000..fa06743 --- /dev/null +++ b/Demo/superplayerdemo/src/main/java/com/tencent/liteav/demo/player/demo/shortvideo/base/AbsBaseFragment.java @@ -0,0 +1,35 @@ +package com.tencent.liteav.demo.player.demo.shortvideo.base; + +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +import androidx.annotation.LayoutRes; +import androidx.annotation.Nullable; +import androidx.fragment.app.Fragment; + +public abstract class AbsBaseFragment extends Fragment { + + protected abstract @LayoutRes + int getLayoutResId(); + + protected abstract void initViews(@Nullable Bundle savedInstanceState); + + protected abstract void initData(); + + + @Nullable + @Override + public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { + View view = inflater.inflate(getLayoutResId(), container, false); + return view; + } + + @Override + public void onViewCreated(View view, @Nullable Bundle savedInstanceState) { + super.onViewCreated(view, savedInstanceState); + initViews(savedInstanceState); + initData(); + } +} diff --git a/Demo/superplayerdemo/src/main/java/com/tencent/liteav/demo/player/demo/shortvideo/bean/DataBeanParser.java b/Demo/superplayerdemo/src/main/java/com/tencent/liteav/demo/player/demo/shortvideo/bean/DataBeanParser.java new file mode 100644 index 0000000..32f759a --- /dev/null +++ b/Demo/superplayerdemo/src/main/java/com/tencent/liteav/demo/player/demo/shortvideo/bean/DataBeanParser.java @@ -0,0 +1,101 @@ +package com.tencent.liteav.demo.player.demo.shortvideo.bean; + +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +import java.util.ArrayList; +import java.util.List; + +public class DataBeanParser { + protected JSONObject response; + + public DataBeanParser(JSONObject response) { + this.response = response; + } + + /** + * 获取视频名称 + * + * @return + */ + public String name() { + try { + JSONObject basicInfo = response.getJSONObject("media").getJSONObject("basicInfo"); + if (basicInfo != null) { + return basicInfo.getString("name"); + } + } catch (JSONException e) { + e.printStackTrace(); + } + return null; + } + + /** + * 获取视频名称 + * + * @return + */ + public String coverUrl() { + try { + JSONObject basicInfo = response.getJSONObject("media").getJSONObject("basicInfo"); + if (basicInfo != null) { + return basicInfo.getString("coverUrl"); + } + } catch (JSONException e) { + e.printStackTrace(); + } + return null; + } + + /** + * 获取视频名称 + * + * @return + */ + public String url() { + try { + JSONObject basicInfo = response.getJSONObject("media").getJSONObject("streamingInfo").getJSONObject("plainOutput"); + if (basicInfo != null) { + return basicInfo.getString("url"); + } + } catch (JSONException e) { + e.printStackTrace(); + } + return null; + } + + /** + * 获取视频名称 + * + * @return + */ + public int duration() { + try { + JSONObject basicInfo = response.getJSONObject("media").getJSONObject("basicInfo"); + if (basicInfo != null) { + return basicInfo.getInt("duration"); + } + } catch (JSONException e) { + e.printStackTrace(); + } + return 0; + } + + public List getSubStreamDTOArray() { + List subStreamsDTOList = new ArrayList<>(); + try { + JSONArray jsonElements = response.getJSONObject("media").getJSONObject("streamingInfo").getJSONObject("plainOutput").getJSONArray("subStreams"); + for (int i = 0; i < jsonElements.length(); i++) { + String type = jsonElements.getJSONObject(i).getString("type"); + subStreamsDTOList.add(new SubStreamsDTO(type)); + } + return subStreamsDTOList; + } catch (JSONException e) { + e.printStackTrace(); + } + return null; + } +} + + diff --git a/Demo/superplayerdemo/src/main/java/com/tencent/liteav/demo/player/demo/shortvideo/bean/ShortVideoBean.java b/Demo/superplayerdemo/src/main/java/com/tencent/liteav/demo/player/demo/shortvideo/bean/ShortVideoBean.java new file mode 100644 index 0000000..7b94c4b --- /dev/null +++ b/Demo/superplayerdemo/src/main/java/com/tencent/liteav/demo/player/demo/shortvideo/bean/ShortVideoBean.java @@ -0,0 +1,93 @@ +package com.tencent.liteav.demo.player.demo.shortvideo.bean; + + +import android.graphics.Bitmap; + +import java.util.List; + + +public class ShortVideoBean { + + /** + * 视频标题 + */ + public String title; + + /** + * 视频URL + */ + public String videoURL; + + /** + * 视频封面本地图片 + */ + public String placeholderImage; + + /** + * 视频时长 + */ + public int duration; + + /** + * appId + */ + public int appid; + + /** + * 视频的fileid + */ + public String fileid; + + /** + * 等于v2或者v4或者为空 + */ + public String appidType; + + /** + * 缓存第一帧图片用于快速显示 + */ + public Bitmap bitmap; + + public int bitRateIndex; + + public ShortVideoBean(int appid, String fileid, String appidType) { + this.appid = appid; + this.fileid = fileid; + this.appidType = appidType; + } + + /** + * VIDEO 不同清晰度的URL链接 + */ + public List multiVideoURLs; + public int playDefaultIndex; // 指定多码率情况下,默认播放的连接Index + + public static class VideoPlayerURL { + + public VideoPlayerURL() { + } + + public VideoPlayerURL(String title, String url) { + this.title = title; + this.url = url; + } + + /** + * 视频标题 + */ + public String title; + + /** + * 视频URL + */ + public String url; + + @Override + public String toString() { + return "SuperPlayerUrl{" + + "title='" + title + '\'' + + ", url='" + url + '\'' + + '}'; + } + } +} diff --git a/Demo/superplayerdemo/src/main/java/com/tencent/liteav/demo/player/demo/shortvideo/bean/SubStreamsDTO.java b/Demo/superplayerdemo/src/main/java/com/tencent/liteav/demo/player/demo/shortvideo/bean/SubStreamsDTO.java new file mode 100644 index 0000000..13484b4 --- /dev/null +++ b/Demo/superplayerdemo/src/main/java/com/tencent/liteav/demo/player/demo/shortvideo/bean/SubStreamsDTO.java @@ -0,0 +1,15 @@ +package com.tencent.liteav.demo.player.demo.shortvideo.bean; + +import java.io.Serializable; + +public class SubStreamsDTO implements Serializable { + + public SubStreamsDTO(String type) { + this.type = type; + } + + public String type; + public int width; + public int height; + public String resolutionName; +} \ No newline at end of file diff --git a/Demo/superplayerdemo/src/main/java/com/tencent/liteav/demo/player/demo/shortvideo/core/PlayerManager.java b/Demo/superplayerdemo/src/main/java/com/tencent/liteav/demo/player/demo/shortvideo/core/PlayerManager.java new file mode 100644 index 0000000..0543435 --- /dev/null +++ b/Demo/superplayerdemo/src/main/java/com/tencent/liteav/demo/player/demo/shortvideo/core/PlayerManager.java @@ -0,0 +1,128 @@ +package com.tencent.liteav.demo.player.demo.shortvideo.core; + +import android.content.Context; + +import com.tencent.liteav.demo.player.demo.shortvideo.bean.ShortVideoBean; +import com.tencent.rtmp.TXLog; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class PlayerManager { + private static final String TAG = "ShortVideoDemo:PlayerManager"; + private final static int sMaxPlayerSize = 10; + private Map mUrlPlayerMap; + + private ShortVideoBean mLastPlayedVideoBean; + private Context mContext; + private volatile static PlayerManager mInstance; + + + private PlayerManager(Context context) { + mContext = context.getApplicationContext(); + mUrlPlayerMap = new HashMap<>(); + } + + public static PlayerManager getInstance(Context context) { + if (mInstance == null) { + synchronized (PlayerManager.class) { + if (mInstance == null) { + mInstance = new PlayerManager(context); + } + } + } + return mInstance; + } + + public void updateManager(List shortVideoBeanList) { + if (shortVideoBeanList == null || shortVideoBeanList.isEmpty()) { + return; + } + if (shortVideoBeanList.size() > sMaxPlayerSize) { + throw new IllegalArgumentException("shortVideoBeanList is larger than sMaxPlayerSize"); + } + + List lastBeanList = playedShortVideoBean(); + TXLog.i(TAG, " [updateManager]" + ",urlList = " + shortVideoBeanList.toString() + ",lastBeanList = " + lastBeanList.toString()); + + //找到 lastUrlList中不包含urlList的 lastUrlList为上次传进来的urlList urlList为这次传进来的urlList + List exprList = findDiffBeanList(shortVideoBeanList, lastBeanList); + //找到 urlList中不包含lastUrlList的 + List newList = findDiffBeanList(lastBeanList, shortVideoBeanList); + if (exprList != null) { + for (int i = 0; i < exprList.size(); i++) { + TXLog.i(TAG, "[updateManager] exprUrl " + exprList.get(i).videoURL); + } + } + if (newList != null) { + for (int i = 0; i < newList.size(); i++) { + TXLog.i(TAG, "[updateManager] newUrl " + newList.get(i).videoURL); + } + } + if (newList.size() > 0) { + for (int i = 0; i < newList.size(); i++) { + TXVodPlayerWrapper tempPlayer = null; + if (exprList.size() > 0) { + tempPlayer = mUrlPlayerMap.remove(exprList.remove(0)); + } + if (tempPlayer == null) { + tempPlayer = new TXVodPlayerWrapper(mContext); + } + + tempPlayer.preStartPlay(newList.get(i)); + + mUrlPlayerMap.put(newList.get(i), tempPlayer); + } + } + + if (exprList.size() > 0) { + for (int i = 0; i < exprList.size(); i++) { + TXVodPlayerWrapper exprPlayer = mUrlPlayerMap.get(exprList.get(i)); + mUrlPlayerMap.remove(exprList.get(i)); + exprPlayer.stopPlay(); + exprPlayer = null; + } + } + + if (shortVideoBeanList.contains(mLastPlayedVideoBean)) { + TXLog.i(TAG, " [updateManager]" + ",mLastPlayedBean = " + mLastPlayedVideoBean.videoURL); + if (mUrlPlayerMap.get(mLastPlayedVideoBean) != null) { + mUrlPlayerMap.get(mLastPlayedVideoBean).preStartPlay(mLastPlayedVideoBean); + } + } + } + + public TXVodPlayerWrapper getPlayer(ShortVideoBean bean) { + mLastPlayedVideoBean = bean; + return mUrlPlayerMap.get(bean); + } + + + private List findDiffBeanList(List playUrlList, List lastPlayUrlList) { + List exprList = new ArrayList<>(); + for (int i = 0; i < lastPlayUrlList.size(); i++) { + if (!playUrlList.contains(lastPlayUrlList.get(i))) { + exprList.add(lastPlayUrlList.get(i)); + } + } + return exprList; + } + + public void releasePlayer() { + for (TXVodPlayerWrapper txVodPlayerWrapper : mUrlPlayerMap.values()) { + txVodPlayerWrapper.stopPlay(); + } + mUrlPlayerMap.clear(); + mInstance = null; + } + + private List playedShortVideoBean() { + List urlList = new ArrayList<>(); + for (ShortVideoBean bean : mUrlPlayerMap.keySet()) { + urlList.add(bean); + } + return urlList; + } +} diff --git a/Demo/superplayerdemo/src/main/java/com/tencent/liteav/demo/player/demo/shortvideo/core/ShortVideoModel.java b/Demo/superplayerdemo/src/main/java/com/tencent/liteav/demo/player/demo/shortvideo/core/ShortVideoModel.java new file mode 100644 index 0000000..0555b76 --- /dev/null +++ b/Demo/superplayerdemo/src/main/java/com/tencent/liteav/demo/player/demo/shortvideo/core/ShortVideoModel.java @@ -0,0 +1,218 @@ +package com.tencent.liteav.demo.player.demo.shortvideo.core; + +import android.text.TextUtils; + +import com.tencent.liteav.basic.log.TXCLog; +import com.tencent.liteav.demo.player.demo.shortvideo.bean.DataBeanParser; +import com.tencent.liteav.demo.player.demo.shortvideo.bean.ShortVideoBean; +import com.tencent.liteav.demo.player.demo.shortvideo.bean.SubStreamsDTO; +import com.tencent.rtmp.TXLog; + +import org.json.JSONException; +import org.json.JSONObject; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; + +import okhttp3.Call; +import okhttp3.Callback; +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.Response; + +public class ShortVideoModel { + private static final String TAG = "ShortVideoDemo:ShortVideoModel"; + private volatile static ShortVideoModel mInstance; + private static final int APP_ID = 1500005830; + private final String BASE_URL = "http://playvideo.qcloud.com/getplayinfo/v4"; + private final String BASE_URLS = "https://playvideo.qcloud.com/getplayinfo/v4"; + private final String BASE_URL_V2 = "http://playvideo.qcloud.com/getplayinfo/v2"; + private final String BASE_URLS_V2 = "https://playvideo.qcloud.com/getplayinfo/v2"; + private final String V2 = "v2"; + private final String V4 = "v4"; + private static final String[] FILE_IDS = new String[]{"3701925920152292697", "3701925920152048882", "3701925920152283492" + , "3701925920152283699", "3701925920152049422", "3701925920152049645" + , "3701925920152293774", "3701925920152293840", "3701925920152050112" + , "3701925920152294230", "3701925920152285056", "3701925920152285302" + , "3701925920152050929", "3701925920152131172", "3701925920152286184" + , "3701925920152286399"}; + + private ArrayList source_list; + private ArrayList data_list; + private ExecutorService mExecutorService; + private boolean mIsHttps = true; + private OkHttpClient mHttpClient; + private int mTotalSize; + private IOnDataLoadFullListener mOnDataLoadFullListener; + + private ShortVideoModel() { + mExecutorService = Executors.newSingleThreadExecutor(); + mHttpClient = new OkHttpClient(); + mHttpClient.newBuilder().connectTimeout(5, TimeUnit.SECONDS); + source_list = new ArrayList<>(); + data_list = new ArrayList<>(); + } + + public static ShortVideoModel getInstance() { + if (mInstance == null) { + synchronized (ShortVideoModel.class) { + if (mInstance == null) { + mInstance = new ShortVideoModel(); + } + } + } + return mInstance; + } + + public void release() { + mInstance = null; + mExecutorService.shutdown(); + } + + private int getFileIDSLength() { + return FILE_IDS.length; + } + + + public void loadDefaultVideo() { + source_list.clear(); + data_list.clear(); + for (int i = 0; i < FILE_IDS.length; i++) { + source_list.add(new ShortVideoBean(APP_ID, FILE_IDS[i], V4)); + } + } + + + public void setOnDataLoadFullListener(IOnDataLoadFullListener listener) { + mOnDataLoadFullListener = listener; + } + + public void getVideoByFileId() { + mExecutorService.execute(new Runnable() { + @Override + public void run() { + mTotalSize = 0; + for (int i = 0, size = source_list.size(); i < size; i++) { + final ShortVideoBean model = source_list.get(i); + String urlStr = makeUrlString(model.appid, model.fileid, null, null, -1, null, model.appidType); + Request request = new Request.Builder().url(urlStr).build(); + Call call = mHttpClient.newCall(request); + call.enqueue(new Callback() { + @Override + public void onFailure(Call call, IOException e) { + TXCLog.e(TAG, "onFailure"); + //获取请求信息失败 + checkIfReady(); + } + + @Override + public void onResponse(Call call, Response response) throws IOException { + String content = response.body().string(); + parseJson(model, content); + } + }); + } + } + }); + } + + private void parseJson(ShortVideoBean videoModel, String content) { + if (TextUtils.isEmpty(content)) { + TXCLog.e(TAG, "parseJson err, content is empty!"); + checkIfReady(); + return; + } + try { + JSONObject jsonObject = new JSONObject(content); + int code = jsonObject.getInt("code"); + if (code != 0) { + String message = jsonObject.getString("message"); + TXCLog.e(TAG, message + ""); + checkIfReady(); + return; + } + DataBeanParser dataBeanParser = new DataBeanParser(jsonObject); + videoModel.placeholderImage = dataBeanParser.coverUrl(); + videoModel.duration = dataBeanParser.duration(); + videoModel.title = dataBeanParser.name(); + videoModel.videoURL = dataBeanParser.url(); + List subStreamsDTOList = dataBeanParser.getSubStreamDTOArray(); + videoModel.bitRateIndex = findBitRateIndex(subStreamsDTOList); + TXLog.i(TAG, "[parseJson] betterIndex " + videoModel.bitRateIndex); + data_list.add(videoModel); + checkIfReady(); + } catch (JSONException e) { + e.printStackTrace(); + TXCLog.e(TAG, e.getMessage() + ""); + checkIfReady(); + } + } + + private int findBitRateIndex(List subStreamsDTOList) { + for (int i = subStreamsDTOList.size() - 1; i >= 0; i--) { + if (subStreamsDTOList.get(i).type.equals("video")) { + return i; + } + } + return 0; + } + + private String makeUrlString(int appId, String fileId, String timeout, String us, int exper, String sign, String appidType) { + String urlStr; + if (TextUtils.equals(appidType, V2)) { + if (!mIsHttps) { + urlStr = String.format("%s/%d/%s", BASE_URL_V2, appId, fileId); + } else { + urlStr = String.format("%s/%d/%s", BASE_URLS_V2, appId, fileId); + } + } else { + if (!mIsHttps) { + urlStr = String.format("%s/%d/%s", BASE_URL, appId, fileId); + } else { + urlStr = String.format("%s/%d/%s", BASE_URLS, appId, fileId); + } + } + + String query = makeQueryString(timeout, us, exper, sign); + if (query != null) { + urlStr = urlStr + "?" + query; + } + return urlStr; + } + + private String makeQueryString(String timeout, String us, int exper, String sign) { + StringBuilder str = new StringBuilder(); + if (timeout != null) { + str.append("t=" + timeout + "&"); + } + if (us != null) { + str.append("us=" + us + "&"); + } + if (sign != null) { + str.append("sign=" + sign + "&"); + } + if (exper >= 0) { + str.append("exper=" + exper + "&"); + } + if (str.length() > 1) { + str.deleteCharAt(str.length() - 1); + } + return str.toString(); + } + + private synchronized void checkIfReady() { + mTotalSize++; + TXLog.i(TAG, "mTotalSize" + mTotalSize); + if (mTotalSize == getFileIDSLength()) { + mOnDataLoadFullListener.onLoaded(data_list); + } + } + + public interface IOnDataLoadFullListener { + void onLoaded(List videoBeanList); + } +} diff --git a/Demo/superplayerdemo/src/main/java/com/tencent/liteav/demo/player/demo/shortvideo/core/TXVodPlayerWrapper.java b/Demo/superplayerdemo/src/main/java/com/tencent/liteav/demo/player/demo/shortvideo/core/TXVodPlayerWrapper.java new file mode 100644 index 0000000..805e928 --- /dev/null +++ b/Demo/superplayerdemo/src/main/java/com/tencent/liteav/demo/player/demo/shortvideo/core/TXVodPlayerWrapper.java @@ -0,0 +1,149 @@ +package com.tencent.liteav.demo.player.demo.shortvideo.core; + +import android.content.Context; +import android.os.Bundle; + +import com.tencent.liteav.demo.player.demo.shortvideo.bean.ShortVideoBean; +import com.tencent.rtmp.ITXVodPlayListener; +import com.tencent.rtmp.TXLog; +import com.tencent.rtmp.TXVodPlayConfig; +import com.tencent.rtmp.TXVodPlayer; +import com.tencent.rtmp.ui.TXCloudVideoView; + +import static com.tencent.rtmp.TXLiveConstants.PLAY_EVT_PLAY_BEGIN; +import static com.tencent.rtmp.TXLiveConstants.PLAY_EVT_PLAY_END; +import static com.tencent.rtmp.TXLiveConstants.PLAY_EVT_PLAY_PROGRESS; +import static com.tencent.rtmp.TXLiveConstants.PLAY_EVT_VOD_PLAY_PREPARED; + +public class TXVodPlayerWrapper implements ITXVodPlayListener { + private static final String TAG = "ShortVideoDemo:TXVodPlayerWrapper"; + private TXVodPlayer mVodPlayer; + private ISeekBarChangeListener mSeekBarChangeListener; + private TxVodStatus mStatus; + private String mUrl; + private boolean mStartOnPrepare; + private TXVodPlayConfig mTXVodPlayConfig; + + public TXVodPlayerWrapper(Context context) { + mVodPlayer = new TXVodPlayer(context); + mVodPlayer.setVodListener(this); + mTXVodPlayConfig = new TXVodPlayConfig(); + mTXVodPlayConfig.setProgressInterval(1); + mTXVodPlayConfig.setSmoothSwitchBitrate(true); + mTXVodPlayConfig.setMaxBufferSize(5); + mVodPlayer.setConfig(mTXVodPlayConfig); + } + + @Override + public void onPlayEvent(TXVodPlayer txVodPlayer, int event, Bundle bundle) { + switch (event) { + case PLAY_EVT_VOD_PLAY_PREPARED: + playerStatusChanged(TxVodStatus.TX_VIDEO_PLAYER_STATUS_PREPARED); + TXLog.i(TAG, "[onPlayEvent] , startOnPrepare," + mStartOnPrepare + ",mVodPlayer " + mVodPlayer.hashCode()+" mUrl " +mUrl); + if (mStartOnPrepare) { + mVodPlayer.resume(); + mStartOnPrepare = false; + playerStatusChanged(TxVodStatus.TX_VIDEO_PLAYER_STATUS_PLAYING); + } + break; + case PLAY_EVT_PLAY_BEGIN: + TXLog.i(TAG, "[onPlayEvent] , PLAY_EVT_PLAY_BEGIN," + mVodPlayer.hashCode() + ",url " + mUrl); + break; + case PLAY_EVT_PLAY_PROGRESS: + if (mSeekBarChangeListener != null) { + mSeekBarChangeListener.seekbarChanged(bundle); + } + break; + case PLAY_EVT_PLAY_END: + playerStatusChanged(TxVodStatus.TX_VIDEO_PLAYER_STATUS_ENDED); + break; + default: + break; + } + } + + @Override + public void onNetStatus(TXVodPlayer txVodPlayer, Bundle bundle) { + + } + + + public void pausePlay() { + mVodPlayer.pause(); + playerStatusChanged(TxVodStatus.TX_VIDEO_PLAYER_STATUS_PAUSED); + } + + + public void resumePlay() { + if (mStatus == TxVodStatus.TX_VIDEO_PLAYER_STATUS_PREPARED || mStatus == TxVodStatus.TX_VIDEO_PLAYER_STATUS_PAUSED) { + mVodPlayer.resume(); + playerStatusChanged(TxVodStatus.TX_VIDEO_PLAYER_STATUS_PLAYING); + } else { + mStartOnPrepare = true; + } + TXLog.i(TAG, "[resumePlay] , startOnPrepare, " + mStartOnPrepare + " mVodPlayer " + mVodPlayer.hashCode() + " url " + mUrl); + } + + + public void seekTo(int time) { + mVodPlayer.seek(time); + } + + + public boolean isPlaying() { + return mVodPlayer.isPlaying(); + } + + + public void stopPlay() { + mVodPlayer.stopPlay(true); + } + + public void setPlayerView(TXCloudVideoView txCloudVideoView) { + mVodPlayer.setPlayerView(txCloudVideoView); + } + + + public void preStartPlay(ShortVideoBean bean) { + this.mUrl = bean.videoURL; + this.mStatus = TxVodStatus.TX_VIDEO_PLAYER_STATUS_UNLOAD; + mStartOnPrepare = false; + mVodPlayer.setLoop(true); + mVodPlayer.stopPlay(true); + TXLog.i(TAG, "[preStartPlay] , startOnPrepare ," + mStartOnPrepare + ", mVodPlayer " + mVodPlayer.hashCode()); + mVodPlayer.setAutoPlay(false); + mVodPlayer.startPlay(bean.videoURL); + mVodPlayer.setBitrateIndex(bean.bitRateIndex); + } + + private void playerStatusChanged(TxVodStatus status) { + this.mStatus = status; + TXLog.i(TAG," [playerStatusChanged] mVodPlayer" +mVodPlayer.hashCode()+" mStatus "+mStatus ); + } + + public void setVodChangeListener(ISeekBarChangeListener listener) { + mSeekBarChangeListener = listener; + + } + + public enum TxVodStatus { + TX_VIDEO_PLAYER_STATUS_UNLOAD, // 未加载 + TX_VIDEO_PLAYER_STATUS_PREPARED, // 准备播放 + TX_VIDEO_PLAYER_STATUS_LOADING, // 加载中 + TX_VIDEO_PLAYER_STATUS_PLAYING, // 播放中 + TX_VIDEO_PLAYER_STATUS_PAUSED, // 暂停 + TX_VIDEO_PLAYER_STATUS_ENDED, // 播放完成 + } + + public interface ISeekBarChangeListener { + void seekbarChanged(Bundle bundle); + } + + public TXVodPlayer getVodPlayer() { + return mVodPlayer; + } + + public String getUrl() { + return mUrl; + } +} diff --git a/Demo/superplayerdemo/src/main/java/com/tencent/liteav/demo/player/demo/shortvideo/view/ShortVideoActivity.java b/Demo/superplayerdemo/src/main/java/com/tencent/liteav/demo/player/demo/shortvideo/view/ShortVideoActivity.java new file mode 100644 index 0000000..944ba65 --- /dev/null +++ b/Demo/superplayerdemo/src/main/java/com/tencent/liteav/demo/player/demo/shortvideo/view/ShortVideoActivity.java @@ -0,0 +1,132 @@ +package com.tencent.liteav.demo.player.demo.shortvideo.view; + +import android.os.Bundle; + +import androidx.annotation.Nullable; +import androidx.fragment.app.Fragment; +import androidx.viewpager.widget.ViewPager; + +import com.tencent.liteav.demo.player.R; +import com.tencent.liteav.demo.player.demo.shortvideo.adapter.ShortVideoListAdapter; +import com.tencent.liteav.demo.player.demo.shortvideo.adapter.ShortVideoPageAdapter; +import com.tencent.liteav.demo.player.demo.shortvideo.base.AbsBaseActivity; +import com.tencent.liteav.demo.player.demo.shortvideo.bean.ShortVideoBean; +import com.tencent.liteav.demo.player.demo.shortvideo.core.ShortVideoModel; +import com.tencent.rtmp.TXLog; + +import java.util.ArrayList; +import java.util.List; + +public class ShortVideoActivity extends AbsBaseActivity implements ViewPager.OnPageChangeListener, ShortVideoListAdapter.IOnItemClickListener, ShortVideoModel.IOnDataLoadFullListener { + + private static final String TAG = "ShortVideoDemo:ShortVideoActivity"; + + private static final int PLAY_FRAGMENT = 0; + + private static final int LIST_FRAGMENT = 1; + + + private ViewPager mViewPager; + + private List mFragmentList; + + private IOnItemClickListener mItemClickListener; + + private IOnListPageScrolledListener mIOnListPageScrolledListener; + + private List mShortVideoBeanList; + + private List mOnListDataLoadedList; + + @Override + protected void initLayout(@Nullable Bundle savedInstanceState) { + setContentView(R.layout.player_activity_shortvideo); + } + + @Override + protected void initView() { + ShortVideoModel.getInstance().setOnDataLoadFullListener(this); + mViewPager = findViewById(R.id.viewpager_short_video); + } + + @Override + protected void initData() { + mFragmentList = new ArrayList<>(); + mFragmentList.add(new ShortVideoPlayFragment()); + ShortVideoListFragment shortVideoListFragment = new ShortVideoListFragment(this); + mFragmentList.add(shortVideoListFragment); + mViewPager.setAdapter(new ShortVideoPageAdapter(getSupportFragmentManager(), mFragmentList)); + mViewPager.addOnPageChangeListener(this); + mViewPager.setCurrentItem(PLAY_FRAGMENT); + mShortVideoBeanList = new ArrayList<>(); + mOnListDataLoadedList = new ArrayList<>(); + ShortVideoModel.getInstance().loadDefaultVideo(); + ShortVideoModel.getInstance().getVideoByFileId(); + } + + + @Override + public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { + if (position == LIST_FRAGMENT) { + mIOnListPageScrolledListener.onListPageScrolled(); + } else if (position == PLAY_FRAGMENT) { + TXLog.i(TAG, "onPageScrolled of play fragment"); + } else { + TXLog.i(TAG, "onPageScrolled other case"); + } + } + + @Override + public void onPageSelected(int position) { + + } + + @Override + public void onPageScrollStateChanged(int state) { + + } + + @Override + public void onItemClick(int position) { + mViewPager.setCurrentItem(PLAY_FRAGMENT); + mItemClickListener.onItemClick(position); + TXLog.i(TAG, "from list position " + position); + } + + + public void setCurrentItemPlayFragment() { + mViewPager.setCurrentItem(PLAY_FRAGMENT); + } + + interface IOnItemClickListener { + void onItemClick(int position); + } + + public void setOnItemClickListener(IOnItemClickListener listener) { + mItemClickListener = listener; + } + + interface IOnListPageScrolledListener { + void onListPageScrolled(); + } + + public void setOnListPageScrolledListener(IOnListPageScrolledListener listener) { + mIOnListPageScrolledListener = listener; + } + + interface IOnListDataLoadedListener { + void onLoaded(List shortVideoBeanList); + } + + public void setOnListDataLoadedListener(IOnListDataLoadedListener listener) { + mOnListDataLoadedList.add(listener); + } + + @Override + public void onLoaded(List videoBeanList) { + for (int i = 0; i < mOnListDataLoadedList.size(); i++) { + mOnListDataLoadedList.get(i).onLoaded(videoBeanList); + } + ShortVideoModel.getInstance().release(); + } +} diff --git a/Demo/superplayerdemo/src/main/java/com/tencent/liteav/demo/player/demo/shortvideo/view/ShortVideoListFragment.java b/Demo/superplayerdemo/src/main/java/com/tencent/liteav/demo/player/demo/shortvideo/view/ShortVideoListFragment.java new file mode 100644 index 0000000..a092eed --- /dev/null +++ b/Demo/superplayerdemo/src/main/java/com/tencent/liteav/demo/player/demo/shortvideo/view/ShortVideoListFragment.java @@ -0,0 +1,84 @@ +package com.tencent.liteav.demo.player.demo.shortvideo.view; + +import android.os.Bundle; +import android.view.View; +import android.widget.ImageButton; + +import androidx.annotation.Nullable; +import androidx.recyclerview.widget.RecyclerView; +import androidx.recyclerview.widget.StaggeredGridLayoutManager; + +import com.tencent.liteav.demo.player.R; +import com.tencent.liteav.demo.player.demo.shortvideo.adapter.ShortVideoListAdapter; +import com.tencent.liteav.demo.player.demo.shortvideo.base.AbsBaseFragment; +import com.tencent.liteav.demo.player.demo.shortvideo.bean.ShortVideoBean; + +import java.util.ArrayList; +import java.util.List; + + +public class ShortVideoListFragment extends AbsBaseFragment implements ShortVideoActivity.IOnListDataLoadedListener { + private static final String TAG = "ShortVideoDemo:ShortVideoListFragment"; + private RecyclerView mRecyclerView; + + private ShortVideoListAdapter mAdapter; + + private ImageButton mBackList; + + private ShortVideoListAdapter.IOnItemClickListener mIOnItemClickListener; + + private List mShortVideoBeanList; + + public ShortVideoListFragment() { + } + + public ShortVideoListFragment(ShortVideoListAdapter.IOnItemClickListener listener) { + mIOnItemClickListener = listener; + } + + @Override + protected int getLayoutResId() { + return R.layout.player_fragment_short_video_list; + } + + @Override + protected void initViews(@Nullable Bundle savedInstanceState) { + + ((ShortVideoActivity) getActivity()).setOnListDataLoadedListener(this); + mShortVideoBeanList = new ArrayList<>(); + mBackList = getActivity().findViewById(R.id.ib_back); + mRecyclerView = getActivity().findViewById(R.id.recycler_view_short_video_list); + mBackList.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + ((ShortVideoActivity) getActivity()).setCurrentItemPlayFragment(); + } + }); + } + + + @Override + protected void initData() { + } + + + @Override + public void onDestroy() { + super.onDestroy(); + } + + + @Override + public void onLoaded(List shortVideoBeanList) { + mShortVideoBeanList = shortVideoBeanList; + mAdapter = new ShortVideoListAdapter(getContext(), mIOnItemClickListener, mShortVideoBeanList); + final RecyclerView.LayoutManager layoutManager = new StaggeredGridLayoutManager(2, StaggeredGridLayoutManager.VERTICAL); + getActivity().runOnUiThread(new Runnable() { + @Override + public void run() { + mRecyclerView.setLayoutManager(layoutManager); + mRecyclerView.setAdapter(mAdapter); + } + }); + } +} diff --git a/Demo/superplayerdemo/src/main/java/com/tencent/liteav/demo/player/demo/shortvideo/view/ShortVideoPlayFragment.java b/Demo/superplayerdemo/src/main/java/com/tencent/liteav/demo/player/demo/shortvideo/view/ShortVideoPlayFragment.java new file mode 100644 index 0000000..d424437 --- /dev/null +++ b/Demo/superplayerdemo/src/main/java/com/tencent/liteav/demo/player/demo/shortvideo/view/ShortVideoPlayFragment.java @@ -0,0 +1,198 @@ +package com.tencent.liteav.demo.player.demo.shortvideo.view; + +import android.content.Context; +import android.os.Bundle; +import android.view.View; +import android.widget.ImageButton; +import android.widget.RelativeLayout; +import android.widget.TextView; + +import androidx.annotation.Nullable; + +import com.tencent.liteav.demo.player.R; +import com.tencent.liteav.demo.player.demo.shortvideo.base.AbsBaseFragment; +import com.tencent.liteav.demo.player.demo.shortvideo.bean.ShortVideoBean; +import com.tencent.rtmp.TXLog; + +import java.util.List; + +import static android.view.View.GONE; +import static android.view.View.VISIBLE; + +public class ShortVideoPlayFragment extends AbsBaseFragment implements ShortVideoActivity.IOnItemClickListener, ShortVideoActivity.IOnListPageScrolledListener, View.OnClickListener, ShortVideoActivity.IOnListDataLoadedListener { + private static final String TAG = "ShortVideoDemo:ShortVideoPlayFragment"; + private static final String SHARE_PREFERENCE_NAME = "tx_short_video_player_guide_setting"; + private static final String KEY_GUIDE_ONE = "is_guide_one_finish"; + private static final String KEY_GUIDE_TWO = "is_guide_two_finish"; + private static final String KEY_GUIDE_THREE = "is_guide_three_finish"; + private static final String KEY_GUIDE_FOUR = "is_guide_four_finish"; + + private RelativeLayout mMaskOne; + private RelativeLayout mMaskTwo; + private RelativeLayout mMaskThree; + private RelativeLayout mMaskFour; + private TextView mMaskOneIKnow; + private TextView mMaskTwoIKnow; + private TextView mMaskThreeIKnow; + private TextView mMaskFourIKnow; + private ImageButton mBack; + private SuperShortVideoView mSuperShortVideoView; + + @Override + protected int getLayoutResId() { + return R.layout.player_fragment_short_video_play; + } + + + @Override + protected void initViews(@Nullable Bundle savedInstanceState) { + ((ShortVideoActivity) getActivity()).setOnItemClickListener(this); + ((ShortVideoActivity) getActivity()).setOnListDataLoadedListener(this); + ((ShortVideoActivity) getActivity()).setOnListPageScrolledListener(this); + mMaskOne = getActivity().findViewById(R.id.rl_mask_one); + mMaskTwo = getActivity().findViewById(R.id.rl_mask_two); + mMaskThree = getActivity().findViewById(R.id.rl_mask_three); + mMaskFour = getActivity().findViewById(R.id.rl_mask_four); + mSuperShortVideoView = getActivity().findViewById(R.id.super_short_video_view_play_fragment); + mMaskOneIKnow = getActivity().findViewById(R.id.tv_mask_one_i_know); + mMaskOneIKnow.setOnClickListener(this); + mMaskTwoIKnow = getActivity().findViewById(R.id.tv_mask_two_i_know); + mMaskTwoIKnow.setOnClickListener(this); + mMaskThreeIKnow = getActivity().findViewById(R.id.tv_mask_three_i_know); + mMaskThreeIKnow.setOnClickListener(this); + mMaskFourIKnow = getActivity().findViewById(R.id.tv_mask_four_i_know); + mMaskFourIKnow.setOnClickListener(this); + mBack = getActivity().findViewById(R.id.ib_back_play); + mBack.setOnClickListener(this); + initMask(); + } + + private void initMask() { + boolean isFinishOne = getBoolean(KEY_GUIDE_ONE); + boolean isFinishTwo = getBoolean(KEY_GUIDE_TWO); + boolean isFinishThree = getBoolean(KEY_GUIDE_THREE); + boolean isFinishFour = getBoolean(KEY_GUIDE_FOUR); + if (!isFinishOne) { + mMaskOne.setVisibility(View.VISIBLE); + mMaskTwo.setVisibility(View.GONE); + mMaskThree.setVisibility(View.GONE); + mMaskFour.setVisibility(View.GONE); + } else if (!isFinishTwo) { + mMaskOne.setVisibility(View.GONE); + mMaskTwo.setVisibility(View.VISIBLE); + mMaskThree.setVisibility(View.GONE); + mMaskFour.setVisibility(View.GONE); + } else if (!isFinishThree) { + mMaskOne.setVisibility(View.GONE); + mMaskTwo.setVisibility(View.GONE); + mMaskThree.setVisibility(View.VISIBLE); + mMaskFour.setVisibility(View.GONE); + } else if (!isFinishFour) { + mMaskOne.setVisibility(View.GONE); + mMaskTwo.setVisibility(View.GONE); + mMaskThree.setVisibility(View.GONE); + mMaskFour.setVisibility(View.VISIBLE); + } else { + mMaskOne.setVisibility(View.GONE); + mMaskTwo.setVisibility(View.GONE); + mMaskThree.setVisibility(View.GONE); + mMaskFour.setVisibility(View.GONE); + } + } + + + @Override + public void onClick(View v) { + if (v.getId() == R.id.ib_back_play) { + getActivity().finish(); + } else if (v.getId() == R.id.tv_mask_one_i_know) { + mMaskOne.setVisibility(GONE); + mMaskTwo.setVisibility(VISIBLE); + mMaskThree.setVisibility(GONE); + mMaskFour.setVisibility(GONE); + putBoolean(KEY_GUIDE_ONE, true); + putBoolean(KEY_GUIDE_TWO, false); + putBoolean(KEY_GUIDE_THREE, false); + putBoolean(KEY_GUIDE_FOUR, false); + } else if (v.getId() == R.id.tv_mask_two_i_know) { + mMaskOne.setVisibility(GONE); + mMaskTwo.setVisibility(GONE); + mMaskThree.setVisibility(VISIBLE); + mMaskFour.setVisibility(GONE); + putBoolean(KEY_GUIDE_ONE, true); + putBoolean(KEY_GUIDE_TWO, true); + putBoolean(KEY_GUIDE_THREE, false); + putBoolean(KEY_GUIDE_FOUR, false); + } else if (v.getId() == R.id.tv_mask_three_i_know) { + mMaskOne.setVisibility(GONE); + mMaskTwo.setVisibility(GONE); + mMaskThree.setVisibility(GONE); + mMaskFour.setVisibility(VISIBLE); + putBoolean(KEY_GUIDE_ONE, true); + putBoolean(KEY_GUIDE_TWO, true); + putBoolean(KEY_GUIDE_THREE, true); + putBoolean(KEY_GUIDE_FOUR, false); + } else if (v.getId() == R.id.tv_mask_four_i_know) { + mMaskOne.setVisibility(GONE); + mMaskTwo.setVisibility(GONE); + mMaskThree.setVisibility(GONE); + mMaskFour.setVisibility(GONE); + putBoolean(KEY_GUIDE_ONE, true); + putBoolean(KEY_GUIDE_TWO, true); + putBoolean(KEY_GUIDE_THREE, true); + putBoolean(KEY_GUIDE_FOUR, true); + } else { + TXLog.i(TAG, "onClick in other case"); + } + } + + @Override + protected void initData() { + + } + + + @Override + public void onStop() { + super.onStop(); + mSuperShortVideoView.pause(); + } + + + @Override + public void onDestroyView() { + super.onDestroyView(); + mSuperShortVideoView.releasePlayer(); + ((ShortVideoActivity) getActivity()).setOnItemClickListener(null); + ((ShortVideoActivity) getActivity()).setOnListDataLoadedListener(null); + ((ShortVideoActivity) getActivity()).setOnListPageScrolledListener(null); + } + + private void putBoolean(String key, boolean value) { + getContext().getSharedPreferences(SHARE_PREFERENCE_NAME, Context.MODE_PRIVATE).edit().putBoolean(key, value).apply(); + } + + private boolean getBoolean(String key) { + return getContext().getSharedPreferences(SHARE_PREFERENCE_NAME, Context.MODE_PRIVATE).getBoolean(key, false); + } + + /** + * listAdapter的点击事件 + * + * @param position + */ + @Override + public void onItemClick(final int position) { + mSuperShortVideoView.onItemClick(position); + } + + @Override + public void onListPageScrolled() { + mSuperShortVideoView.onListPageScrolled(); + } + + @Override + public void onLoaded(List shortVideoBeanList) { + mSuperShortVideoView.setDataSource(shortVideoBeanList); + } +} diff --git a/Demo/superplayerdemo/src/main/java/com/tencent/liteav/demo/player/demo/shortvideo/view/SuperShortVideoView.java b/Demo/superplayerdemo/src/main/java/com/tencent/liteav/demo/player/demo/shortvideo/view/SuperShortVideoView.java new file mode 100644 index 0000000..b55f8e3 --- /dev/null +++ b/Demo/superplayerdemo/src/main/java/com/tencent/liteav/demo/player/demo/shortvideo/view/SuperShortVideoView.java @@ -0,0 +1,187 @@ +package com.tencent.liteav.demo.player.demo.shortvideo.view; + +import android.content.Context; +import android.os.Handler; +import android.os.Message; +import android.util.AttributeSet; +import android.view.LayoutInflater; +import android.view.View; +import android.widget.RelativeLayout; + +import androidx.annotation.NonNull; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.PagerSnapHelper; +import androidx.recyclerview.widget.RecyclerView; + +import com.tencent.liteav.demo.player.R; +import com.tencent.liteav.demo.player.demo.shortvideo.adapter.ShortVideoPlayAdapter; +import com.tencent.liteav.demo.player.demo.shortvideo.bean.ShortVideoBean; +import com.tencent.liteav.demo.player.demo.shortvideo.core.PlayerManager; +import com.tencent.liteav.demo.player.demo.shortvideo.core.TXVodPlayerWrapper; +import com.tencent.rtmp.TXLog; + +import java.util.ArrayList; +import java.util.List; + +public class SuperShortVideoView extends RelativeLayout { + private static final String TAG = "ShortVideoDemo:SuperShortVideoView"; + private static final int MAX_PLAYER_COUNT_ON_PASS = 3; + private View mRootView; + private RecyclerView mRecyclerView; + private ShortVideoPlayAdapter mAdapter; + private List mUrlList; + private LinearLayoutManager mLayoutManager; + private PagerSnapHelper mSnapHelper; + private int mLastPositionInIDLE = -1; + private TXVideoBaseView mBaseItemView; + private Handler mHandler; + + + public SuperShortVideoView(Context context) { + super(context); + init(context); + } + + public SuperShortVideoView(Context context, AttributeSet attrs) { + super(context, attrs); + init(context); + } + + public SuperShortVideoView(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + init(context); + } + + public void init(Context context) { + mRootView = LayoutInflater.from(context).inflate(R.layout.super_short_video_view, null); + addView(mRootView); + mRecyclerView = mRootView.findViewById(R.id.rv_super_short_video); + mUrlList = new ArrayList<>(); + mSnapHelper = new PagerSnapHelper(); + mSnapHelper.attachToRecyclerView(mRecyclerView); + mAdapter = new ShortVideoPlayAdapter(mUrlList); + mLayoutManager = new LinearLayoutManager(getContext(), LinearLayoutManager.VERTICAL, false); + mRecyclerView.setLayoutManager(mLayoutManager); + mRecyclerView.setAdapter(mAdapter); + mLayoutManager.scrollToPosition(0); + mHandler = new Handler() { + @Override + public void handleMessage(@NonNull Message msg) { + super.handleMessage(msg); + mUrlList.clear(); + mUrlList.addAll((List) msg.obj); + mAdapter.notifyDataSetChanged(); + } + }; + addListener(); + } + + public void setDataSource(final List dataSource) { + TXLog.i(TAG, "[setDataSource]"); + Message message = new Message(); + message.obj = dataSource; + mHandler.sendMessage(message); + } + + private void addListener() { + mRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() { + @Override + public void onScrolled(RecyclerView recyclerView, int dx, int dy) { + if (!recyclerView.canScrollVertically(1) || !recyclerView.canScrollVertically(-1)) { + onScrollStateChanged(recyclerView, RecyclerView.SCROLL_STATE_IDLE); + } + } + + @Override + public void onScrollStateChanged(RecyclerView recyclerView, int newState) { + switch (newState) { + case RecyclerView.SCROLL_STATE_IDLE://停止滚动 + View view = mSnapHelper.findSnapView(mLayoutManager); + int position = recyclerView.getChildAdapterPosition(view); + TXLog.i(TAG, "[SCROLL_STATE_IDLE] mLastPositionInIDLE " + mLastPositionInIDLE + " position " + position); + if (mLastPositionInIDLE != position) { + onPageSelectedMethod(position); + mLastPositionInIDLE = position; + TXLog.i(TAG, "[SCROLL_STATE_IDLE] into [startPlay] "); + } + break; + case RecyclerView.SCROLL_STATE_DRAGGING://拖动 + break; + case RecyclerView.SCROLL_STATE_SETTLING://惯性滑动 + break; + } + } + }); + } + + private void onPageSelectedMethod(int position) { + View view = mSnapHelper.findSnapView(mLayoutManager); + mBaseItemView = (TXVideoBaseView) view.findViewById(R.id.baseItemView); + TXLog.i(TAG, "onPageSelected " + position); + List tempUrlList = initUrlList(position, MAX_PLAYER_COUNT_ON_PASS); + PlayerManager.getInstance(getContext()).updateManager(tempUrlList); + TXVodPlayerWrapper txVodPlayerWrapper = PlayerManager.getInstance(getContext()).getPlayer(mUrlList.get(position)); + TXLog.i(TAG, "txVodPlayerWrapper " + txVodPlayerWrapper + "url-- " + mUrlList.get(position).videoURL); + TXLog.i(TAG, "txVodPlayerWrapper " + txVodPlayerWrapper); + mBaseItemView.setTXVodPlayer(txVodPlayerWrapper); + mBaseItemView.startPlay(); + } + + /** + * 初始化向PlayManager传递的urlList + * + * @param startIndex 开始的索引 + * @param maxCount 传递的urlList的数目 + * @return + */ + private List initUrlList(int startIndex, int maxCount) { + + int i = startIndex - 1; + if (i + maxCount > mUrlList.size()) { + i = mUrlList.size() - maxCount; + } + if (i < 0) { + i = 0; + } + int addedCount = 0; + List cacheList = new ArrayList<>(); + while (i < mUrlList.size() && addedCount < maxCount) { + cacheList.add(mUrlList.get(i)); + addedCount++; + i++; + } + return cacheList; + } + + public void pause() { + if (mBaseItemView != null) { + mBaseItemView.pausePlayer(); + } + } + + public void releasePlayer() { + mHandler.removeCallbacksAndMessages(null); + mHandler = null; + if (mBaseItemView != null) { + mBaseItemView.stopPlayer(); + } + PlayerManager.getInstance(getContext()).releasePlayer(); + } + + public void onListPageScrolled() { + if (mBaseItemView != null) { + mBaseItemView.pausePlayer(); + } + } + + public void onItemClick(final int position) { + mRecyclerView.scrollToPosition(position); + mRecyclerView.post(new Runnable() { + @Override + public void run() { + TXLog.i(TAG, "onItemClick"); + onPageSelectedMethod(position); + } + }); + } +} diff --git a/Demo/superplayerdemo/src/main/java/com/tencent/liteav/demo/player/demo/shortvideo/view/TXVideoBaseView.java b/Demo/superplayerdemo/src/main/java/com/tencent/liteav/demo/player/demo/shortvideo/view/TXVideoBaseView.java new file mode 100644 index 0000000..0826ffd --- /dev/null +++ b/Demo/superplayerdemo/src/main/java/com/tencent/liteav/demo/player/demo/shortvideo/view/TXVideoBaseView.java @@ -0,0 +1,191 @@ +package com.tencent.liteav.demo.player.demo.shortvideo.view; + +import android.content.Context; +import android.graphics.Color; +import android.os.Bundle; +import android.text.SpannableStringBuilder; +import android.text.Spanned; +import android.text.style.ForegroundColorSpan; +import android.util.AttributeSet; +import android.view.LayoutInflater; +import android.view.View; +import android.widget.ImageView; +import android.widget.RelativeLayout; +import android.widget.SeekBar; +import android.widget.TextView; + +import com.tencent.liteav.demo.player.R; +import com.tencent.liteav.demo.player.demo.shortvideo.core.TXVodPlayerWrapper; +import com.tencent.rtmp.TXLiveConstants; +import com.tencent.rtmp.TXLog; +import com.tencent.rtmp.ui.TXCloudVideoView; + +import java.util.Locale; + +/** + * 沉浸式播放组件 + */ +public class TXVideoBaseView extends RelativeLayout implements View.OnClickListener, SeekBar.OnSeekBarChangeListener, TXVodPlayerWrapper.ISeekBarChangeListener { + private static final String TAG = "ShortVideoDemo:TXVideoBaseView"; + private View mRootView; + private SeekBar mSeekBar; + private TXCloudVideoView mTXCloudVideoView; + private ImageView mIvCover; + private ImageView mPauseImageView; + private TextView mProgressTime; + + private TXVodPlayerWrapper mTXVodPlayerWrapper; + private boolean mStartSeek = false; + private long mTrackingTouchTS = 0; + + + public TXVideoBaseView(Context context) { + super(context); + init(context); + } + + public TXVideoBaseView(Context context, AttributeSet attrs) { + super(context, attrs); + init(context); + } + + public TXVideoBaseView(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + init(context); + } + + public void setTXVodPlayer(TXVodPlayerWrapper TXVodPlayerWrapper) { + mTXVodPlayerWrapper = TXVodPlayerWrapper; + mTXVodPlayerWrapper.setPlayerView(mTXCloudVideoView); + mTXVodPlayerWrapper.setVodChangeListener(this); + TXLog.i(TAG, "[setTXVodPlayer] , PLAY_EVT_PLAY_PROGRESS," + mTXVodPlayerWrapper.getVodPlayer().hashCode() + " url " + TXVodPlayerWrapper.getUrl()); + } + + private void init(Context context) { + mRootView = LayoutInflater.from(context).inflate(R.layout.player_item_base_view, null); + addView(mRootView); + mSeekBar = mRootView.findViewById(R.id.seekbar_short_video); + mSeekBar.setOnSeekBarChangeListener(this); + mIvCover = mRootView.findViewById(R.id.iv_cover); + mPauseImageView = mRootView.findViewById(R.id.iv_pause); + mPauseImageView.setOnClickListener(this); + mTXCloudVideoView = mRootView.findViewById(R.id.tcv_video_view); + mTXCloudVideoView.setOnClickListener(this); + mProgressTime = mRootView.findViewById(R.id.tv_progress_time); + setProgressTimeColor(mProgressTime.getText().toString()); + } + + public void addCustomView(View customView, LayoutParams params) { + addView(customView, params); + } + + private void handlePlayProgress(Bundle param) { + if (mStartSeek) { + return; + } + + int progress = param.getInt(TXLiveConstants.EVT_PLAY_PROGRESS); + int duration = param.getInt(TXLiveConstants.EVT_PLAY_DURATION);//单位为s + + int progressMS = param.getInt(TXLiveConstants.EVT_PLAY_PROGRESS_MS); + int durationMS = param.getInt(TXLiveConstants.EVT_PLAY_DURATION_MS); + + long curTS = System.currentTimeMillis(); + // 避免滑动进度条松开的瞬间可能出现滑动条瞬间跳到上一个位置 + if (Math.abs(curTS - mTrackingTouchTS) < 500) { + return; + } + mTrackingTouchTS = curTS; + + if (mSeekBar != null) { + mSeekBar.setMax(durationMS); + mSeekBar.setProgress(progressMS); + } + if (mProgressTime != null) { + String tempString = String.format(Locale.CHINA, "%02d:%02d/%02d:%02d", (progress) / 60, progress % 60, (duration) / 60, duration % 60); + setProgressTimeColor(tempString); + } + } + + + @Override + public void onClick(View v) { + int id = v.getId(); + if (id == R.id.tcv_video_view || id == R.id.iv_pause) { + if (!mTXVodPlayerWrapper.isPlaying()) { + mTXVodPlayerWrapper.resumePlay(); + mPauseImageView.setVisibility(View.GONE); + } else { + pausePlayer(); + } + } + } + + @Override + public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { + if (mProgressTime != null) { + String tempString = String.format(Locale.CHINA, "%02d:%02d/%02d:%02d", (progress / 1000) / 60, (progress / 1000) % 60, (seekBar.getMax() / 1000) / 60, (seekBar.getMax() / 1000) % 60); + setProgressTimeColor(tempString); + } + } + + private void setProgressTimeColor(String value) { + SpannableStringBuilder builder = new SpannableStringBuilder(value); + ForegroundColorSpan gray = new ForegroundColorSpan(Color.GRAY); + ForegroundColorSpan white = new ForegroundColorSpan(Color.WHITE); + builder.setSpan(gray, 5, 11, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + builder.setSpan(white, 0, 5, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + mProgressTime.setText(builder); + } + + @Override + public void onStartTrackingTouch(SeekBar seekBar) { + mStartSeek = true; + // 解决viewPager和的滑动冲突问题 + getParent().requestDisallowInterceptTouchEvent(true); + } + + + @Override + public void onStopTrackingTouch(SeekBar seekBar) { + // 解决viewPager和的滑动冲突问题 + getParent().requestDisallowInterceptTouchEvent(false); + if (mTXVodPlayerWrapper != null) { + TXLog.i(TAG, "[onStopTrackingTouch] seekBar.getProgress() " + seekBar.getProgress()); + mTXVodPlayerWrapper.seekTo(seekBar.getProgress() / 1000); + } + mTrackingTouchTS = System.currentTimeMillis(); + mStartSeek = false; + } + + + public void pausePlayer() { + if (mTXVodPlayerWrapper != null) { + mTXVodPlayerWrapper.pausePlay(); + mPauseImageView.setVisibility(View.VISIBLE); + } + } + + + public void startPlay() { + if (mTXVodPlayerWrapper != null) { + mPauseImageView.setVisibility(View.GONE); + mTXVodPlayerWrapper.resumePlay(); + TXLog.i(TAG, "[startPlay] mTXVodPlayerWrapper.url " + mTXVodPlayerWrapper.getUrl()); + } + } + + public void stopPlayer() { + mTXCloudVideoView.removeVideoView(); + if (mTXVodPlayerWrapper != null) { + mTXVodPlayerWrapper.stopPlay(); + TXLog.i(TAG, "[stopPlayer] mTXVodPlayerWrapper.url " + mTXVodPlayerWrapper.getUrl()); + mPauseImageView.setVisibility(View.GONE); + } + } + + @Override + public void seekbarChanged(Bundle bundle) { + handlePlayProgress(bundle); + } +} diff --git a/Demo/superplayerdemo/src/main/java/com/tencent/liteav/demo/player/expand/model/entity/VideoModel.java b/Demo/superplayerdemo/src/main/java/com/tencent/liteav/demo/player/expand/model/entity/VideoModel.java index 86fae95..9cad2c3 100755 --- a/Demo/superplayerdemo/src/main/java/com/tencent/liteav/demo/player/expand/model/entity/VideoModel.java +++ b/Demo/superplayerdemo/src/main/java/com/tencent/liteav/demo/player/expand/model/entity/VideoModel.java @@ -1,6 +1,7 @@ package com.tencent.liteav.demo.player.expand.model.entity; +import com.tencent.liteav.demo.superplayer.model.VipWatchModel; import java.util.List; /** @@ -49,6 +50,8 @@ public class VideoModel { */ public List multiVideoURLs; public int playDefaultIndex; // 指定多码率情况下,默认播放的连接Index + public VipWatchModel vipWatchModel = null; + public static class VideoPlayerURL { diff --git a/Demo/superplayerdemo/src/main/java/com/tencent/liteav/demo/player/expand/model/utils/SuperVodListLoader.java b/Demo/superplayerdemo/src/main/java/com/tencent/liteav/demo/player/expand/model/utils/SuperVodListLoader.java index cbd5c3c..c4b1898 100644 --- a/Demo/superplayerdemo/src/main/java/com/tencent/liteav/demo/player/expand/model/utils/SuperVodListLoader.java +++ b/Demo/superplayerdemo/src/main/java/com/tencent/liteav/demo/player/expand/model/utils/SuperVodListLoader.java @@ -1,10 +1,14 @@ package com.tencent.liteav.demo.player.expand.model.utils; +import android.content.Context; import android.os.Handler; import android.os.HandlerThread; import android.text.TextUtils; import com.tencent.liteav.basic.log.TXCLog; +import com.tencent.liteav.demo.player.R; +import com.tencent.liteav.demo.superplayer.model.VipWatchModel; +import com.tencent.liteav.demo.superplayer.model.entity.PlayInfoStream; import com.tencent.liteav.demo.player.expand.model.entity.VideoModel; import com.tencent.liteav.demo.superplayer.model.entity.PlayInfoStream; @@ -52,7 +56,7 @@ public void setOnVodInfoLoadListener(OnVodInfoLoadListener listener) { mOnVodInfoLoadListener = listener; } - public ArrayList loadDefaultVodList() { + public ArrayList loadDefaultVodList(Context applicationContext) { ArrayList list = new ArrayList<>(); VideoModel model = new VideoModel(); model.appid = 1252463788; @@ -82,6 +86,8 @@ public ArrayList loadDefaultVodList() { model = new VideoModel(); model.appid = 1252463788; model.fileid = "4564972819219081699"; + model.title = applicationContext.getString(R.string.superplayer_vip_title); + model.vipWatchModel = new VipWatchModel(applicationContext.getString(R.string.superplayer_vip_watch_tip), 15); list.add(model); return list; @@ -217,9 +223,14 @@ private void parseJson(VideoModel videoModel, String content) { if (stream != null) { videoModel.duration = stream.getDuration(); } - videoModel.title = playInfoResponse.description(); - if (videoModel.title == null || videoModel.title.length() == 0) { - videoModel.title = playInfoResponse.name(); + String title = playInfoResponse.description(); + if (TextUtils.isEmpty(title)) { + title = playInfoResponse.name(); + } + if (videoModel.vipWatchModel != null) { + videoModel.title = title + videoModel.title; + } else { + videoModel.title = title; } if (mOnVodInfoLoadListener != null) { mOnVodInfoLoadListener.onSuccess(videoModel); @@ -229,9 +240,14 @@ private void parseJson(VideoModel videoModel, String content) { if (media != null) { JSONObject basicInfo = media.optJSONObject("basicInfo"); if (basicInfo != null) { - videoModel.title = basicInfo.optString("description"); - if (videoModel.title == null || videoModel.title.length() == 0) { - videoModel.title = basicInfo.optString("name"); + String title = basicInfo.optString("description"); + if (TextUtils.isEmpty(title)) { + title = basicInfo.optString("name"); + } + if (videoModel.vipWatchModel != null) { + videoModel.title = title + videoModel.title; + } else { + videoModel.title = title; } videoModel.placeholderImage = basicInfo.optString("coverUrl"); videoModel.duration = basicInfo.optInt("duration"); diff --git a/Demo/superplayerdemo/src/main/res/drawable-xxhdpi/player_left_move.png b/Demo/superplayerdemo/src/main/res/drawable-xxhdpi/player_left_move.png new file mode 100644 index 0000000..5189ca5 Binary files /dev/null and b/Demo/superplayerdemo/src/main/res/drawable-xxhdpi/player_left_move.png differ diff --git a/Demo/superplayerdemo/src/main/res/drawable-xxhdpi/player_pause.png b/Demo/superplayerdemo/src/main/res/drawable-xxhdpi/player_pause.png new file mode 100644 index 0000000..c87814c Binary files /dev/null and b/Demo/superplayerdemo/src/main/res/drawable-xxhdpi/player_pause.png differ diff --git a/Demo/superplayerdemo/src/main/res/drawable-xxhdpi/player_point_hit.png b/Demo/superplayerdemo/src/main/res/drawable-xxhdpi/player_point_hit.png new file mode 100644 index 0000000..6910669 Binary files /dev/null and b/Demo/superplayerdemo/src/main/res/drawable-xxhdpi/player_point_hit.png differ diff --git a/Demo/superplayerdemo/src/main/res/drawable-xxhdpi/player_pull_move.png b/Demo/superplayerdemo/src/main/res/drawable-xxhdpi/player_pull_move.png new file mode 100644 index 0000000..2c4f373 Binary files /dev/null and b/Demo/superplayerdemo/src/main/res/drawable-xxhdpi/player_pull_move.png differ diff --git a/Demo/superplayerdemo/src/main/res/drawable-xxhdpi/player_thumb.png b/Demo/superplayerdemo/src/main/res/drawable-xxhdpi/player_thumb.png new file mode 100644 index 0000000..c27d992 Binary files /dev/null and b/Demo/superplayerdemo/src/main/res/drawable-xxhdpi/player_thumb.png differ diff --git a/Demo/superplayerdemo/src/main/res/drawable-xxhdpi/player_up_move.png b/Demo/superplayerdemo/src/main/res/drawable-xxhdpi/player_up_move.png new file mode 100644 index 0000000..6521d62 Binary files /dev/null and b/Demo/superplayerdemo/src/main/res/drawable-xxhdpi/player_up_move.png differ diff --git a/Demo/superplayerdemo/src/main/res/drawable/player_short_video_time.xml b/Demo/superplayerdemo/src/main/res/drawable/player_short_video_time.xml new file mode 100644 index 0000000..66d122a --- /dev/null +++ b/Demo/superplayerdemo/src/main/res/drawable/player_short_video_time.xml @@ -0,0 +1,10 @@ + + + + + + + + \ No newline at end of file diff --git a/Demo/superplayerdemo/src/main/res/drawable/player_thumb_view.xml b/Demo/superplayerdemo/src/main/res/drawable/player_thumb_view.xml new file mode 100644 index 0000000..33560b3 --- /dev/null +++ b/Demo/superplayerdemo/src/main/res/drawable/player_thumb_view.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Demo/superplayerdemo/src/main/res/drawable/player_video_seek_bar.xml b/Demo/superplayerdemo/src/main/res/drawable/player_video_seek_bar.xml new file mode 100644 index 0000000..db7c801 --- /dev/null +++ b/Demo/superplayerdemo/src/main/res/drawable/player_video_seek_bar.xml @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Demo/superplayerdemo/src/main/res/layout/player_activity_shortvideo.xml b/Demo/superplayerdemo/src/main/res/layout/player_activity_shortvideo.xml new file mode 100644 index 0000000..dfcc1a1 --- /dev/null +++ b/Demo/superplayerdemo/src/main/res/layout/player_activity_shortvideo.xml @@ -0,0 +1,11 @@ + + + + + + \ No newline at end of file diff --git a/Demo/superplayerdemo/src/main/res/layout/player_activity_video.xml b/Demo/superplayerdemo/src/main/res/layout/player_activity_video.xml new file mode 100644 index 0000000..9a65a01 --- /dev/null +++ b/Demo/superplayerdemo/src/main/res/layout/player_activity_video.xml @@ -0,0 +1,11 @@ + + + + + + \ No newline at end of file diff --git a/Demo/superplayerdemo/src/main/res/layout/player_fragment_short_video_list.xml b/Demo/superplayerdemo/src/main/res/layout/player_fragment_short_video_list.xml new file mode 100644 index 0000000..19a71ce --- /dev/null +++ b/Demo/superplayerdemo/src/main/res/layout/player_fragment_short_video_list.xml @@ -0,0 +1,42 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/Demo/superplayerdemo/src/main/res/layout/player_fragment_short_video_play.xml b/Demo/superplayerdemo/src/main/res/layout/player_fragment_short_video_play.xml new file mode 100644 index 0000000..41ede25 --- /dev/null +++ b/Demo/superplayerdemo/src/main/res/layout/player_fragment_short_video_play.xml @@ -0,0 +1,239 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Demo/superplayerdemo/src/main/res/layout/player_item_base_view.xml b/Demo/superplayerdemo/src/main/res/layout/player_item_base_view.xml new file mode 100644 index 0000000..936d90f --- /dev/null +++ b/Demo/superplayerdemo/src/main/res/layout/player_item_base_view.xml @@ -0,0 +1,60 @@ + + + + + + + + + + + + + + + + + diff --git a/Demo/superplayerdemo/src/main/res/layout/player_item_short_video_list.xml b/Demo/superplayerdemo/src/main/res/layout/player_item_short_video_list.xml new file mode 100644 index 0000000..2512519 --- /dev/null +++ b/Demo/superplayerdemo/src/main/res/layout/player_item_short_video_list.xml @@ -0,0 +1,50 @@ + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Demo/superplayerdemo/src/main/res/layout/player_item_short_video_play.xml b/Demo/superplayerdemo/src/main/res/layout/player_item_short_video_play.xml new file mode 100644 index 0000000..384cf82 --- /dev/null +++ b/Demo/superplayerdemo/src/main/res/layout/player_item_short_video_play.xml @@ -0,0 +1,12 @@ + + + + + + \ No newline at end of file diff --git a/Demo/superplayerdemo/src/main/res/layout/player_item_video.xml b/Demo/superplayerdemo/src/main/res/layout/player_item_video.xml new file mode 100644 index 0000000..f04cf19 --- /dev/null +++ b/Demo/superplayerdemo/src/main/res/layout/player_item_video.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + diff --git a/Demo/superplayerdemo/src/main/res/layout/player_shortvideo_activity.xml b/Demo/superplayerdemo/src/main/res/layout/player_shortvideo_activity.xml new file mode 100644 index 0000000..5b3bbc8 --- /dev/null +++ b/Demo/superplayerdemo/src/main/res/layout/player_shortvideo_activity.xml @@ -0,0 +1,59 @@ + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Demo/superplayerdemo/src/main/res/layout/player_shortvideo_item.xml b/Demo/superplayerdemo/src/main/res/layout/player_shortvideo_item.xml new file mode 100644 index 0000000..dbb8d85 --- /dev/null +++ b/Demo/superplayerdemo/src/main/res/layout/player_shortvideo_item.xml @@ -0,0 +1,36 @@ + + + + + + + + + \ No newline at end of file diff --git a/Demo/superplayerdemo/src/main/res/layout/super_short_video_view.xml b/Demo/superplayerdemo/src/main/res/layout/super_short_video_view.xml new file mode 100644 index 0000000..86e4767 --- /dev/null +++ b/Demo/superplayerdemo/src/main/res/layout/super_short_video_view.xml @@ -0,0 +1,9 @@ + + + + \ No newline at end of file diff --git a/Demo/superplayerdemo/src/main/res/values-en/strings.xml b/Demo/superplayerdemo/src/main/res/values-en/strings.xml index df8b00d..ea3733a 100644 --- a/Demo/superplayerdemo/src/main/res/values-en/strings.xml +++ b/Demo/superplayerdemo/src/main/res/values-en/strings.xml @@ -26,4 +26,6 @@ Hardware acceleration enabled. The playback will restart. Hardware acceleration disabled. The playback will restart. No playback URL. + - Try function demo + You can try it for %ss, open VIP to watch the full video diff --git a/Demo/superplayerdemo/src/main/res/values/colors.xml b/Demo/superplayerdemo/src/main/res/values/colors.xml index 3ccbc53..0d1733c 100644 --- a/Demo/superplayerdemo/src/main/res/values/colors.xml +++ b/Demo/superplayerdemo/src/main/res/values/colors.xml @@ -1,5 +1,5 @@ - + #ffffff #000000 #00000000 diff --git a/Demo/superplayerdemo/src/main/res/values/strings.xml b/Demo/superplayerdemo/src/main/res/values/strings.xml index fc97a85..7585ef6 100644 --- a/Demo/superplayerdemo/src/main/res/values/strings.xml +++ b/Demo/superplayerdemo/src/main/res/values/strings.xml @@ -28,4 +28,6 @@ 已开启硬件解码加速,切换会重启播放流程! 已关闭硬件解码加速,切换会重启播放流程! 无播放地址 + - 试看功能演示 + 可试看%ss,开通VIP观看完整视频 diff --git a/Demo/superplayerkit/src/main/java/com/tencent/liteav/demo/superplayer/SuperPlayerModel.java b/Demo/superplayerkit/src/main/java/com/tencent/liteav/demo/superplayer/SuperPlayerModel.java index 632e197..f2c946f 100644 --- a/Demo/superplayerkit/src/main/java/com/tencent/liteav/demo/superplayer/SuperPlayerModel.java +++ b/Demo/superplayerkit/src/main/java/com/tencent/liteav/demo/superplayer/SuperPlayerModel.java @@ -1,6 +1,7 @@ package com.tencent.liteav.demo.superplayer; +import com.tencent.liteav.demo.superplayer.model.VipWatchModel; import com.tencent.liteav.demo.superplayer.model.entity.SuperPlayerVideoIdV2; import java.util.List; @@ -56,6 +57,8 @@ public class SuperPlayerModel { public String title = ""; // 视频文件名 (用于显示在UI层);使用file id播放,若未指定title,则使用FileId返回的Title;使用url播放需要指定title,否则title显示为空 + public VipWatchModel vipWatchMode = null; + public static class SuperPlayerURL { public SuperPlayerURL(String url, String qualityName) { this.qualityName = qualityName; diff --git a/Demo/superplayerkit/src/main/java/com/tencent/liteav/demo/superplayer/SuperPlayerView.java b/Demo/superplayerkit/src/main/java/com/tencent/liteav/demo/superplayer/SuperPlayerView.java index bc8fcf3..6297f24 100644 --- a/Demo/superplayerkit/src/main/java/com/tencent/liteav/demo/superplayer/SuperPlayerView.java +++ b/Demo/superplayerkit/src/main/java/com/tencent/liteav/demo/superplayer/SuperPlayerView.java @@ -13,6 +13,8 @@ import android.os.AsyncTask; import android.os.Binder; import android.os.Build; +import android.os.Handler; +import android.os.Looper; import android.provider.MediaStore; import android.provider.Settings; import android.util.AttributeSet; @@ -31,6 +33,7 @@ import com.tencent.liteav.demo.superplayer.model.SuperPlayer; import com.tencent.liteav.demo.superplayer.model.SuperPlayerImpl; import com.tencent.liteav.demo.superplayer.model.SuperPlayerObserver; +import com.tencent.liteav.demo.superplayer.model.VipWatchModel; import com.tencent.liteav.demo.superplayer.model.entity.PlayImageSpriteInfo; import com.tencent.liteav.demo.superplayer.model.entity.PlayKeyFrameDescInfo; import com.tencent.liteav.demo.superplayer.model.entity.VideoQuality; @@ -184,6 +187,19 @@ public void playWithModel(final SuperPlayerModel model) { } else { mSuperPlayer.play(model.url); } + mFullScreenPlayer.setVipWatchModel(model.vipWatchMode); + mWindowPlayer.setVipWatchModel(model.vipWatchMode); + mFloatPlayer.setVipWatchModel(model.vipWatchMode); + } + + /** + * 设置VipWatchModel 数据,传入null可隐藏掉展示的VIP页面 + * @param vipWatchModel + */ + public void setVipWatchModel(VipWatchModel vipWatchModel){ + mFullScreenPlayer.setVipWatchModel(vipWatchModel); + mWindowPlayer.setVipWatchModel(vipWatchModel); + mFloatPlayer.setVipWatchModel(vipWatchModel); } /** @@ -237,6 +253,15 @@ private void updateTitle(String title) { mFullScreenPlayer.updateTitle(title); } + /** + * 用于判断VIP试看页面是否已经展示出来了 + * @return + */ + public boolean isShowingVipView(){ + return mFullScreenPlayer.isShowingVipView() || mWindowPlayer.isShowingVipView() || mFloatPlayer.isShowingVipView(); + } + + /** * resume生命周期回调 */ @@ -532,6 +557,45 @@ public void onMirrorToggle(boolean isMirror) { public void onHWAccelerationToggle(boolean isAccelerate) { mSuperPlayer.enableHardwareDecode(isAccelerate); } + + @Override + public void onClickHandleVip() { + Intent intent = new Intent(Intent.ACTION_VIEW); + intent.setData(Uri.parse("https://cloud.tencent.com/document/product/454/18872")); + if(getContext() instanceof Activity){ + getContext().startActivity(intent); + } + } + + @Override + public void onClickVipTitleBack( SuperPlayerDef.PlayerMode playerMode) { + mFullScreenPlayer.hideVipView(); + mWindowPlayer.hideVipView(); + mFloatPlayer.hideVipView(); + } + + @Override + public void onClickVipRetry() { + mControllerCallback.onSeekTo(0); + postDelayed(new Runnable() { + @Override + public void run() { + mControllerCallback.onResume(); + //隐藏VIP View + mFullScreenPlayer.hideVipView(); + mWindowPlayer.hideVipView(); + mFloatPlayer.hideVipView(); + } + }, 500); + + } + + @Override + public void onCloseVipTip() { + mFullScreenPlayer.hideTipView(); + mWindowPlayer.hideTipView(); + mFloatPlayer.hideTipView(); + } }; /** @@ -720,6 +784,7 @@ public void onPlayLoading() { public void onPlayProgress(long current, long duration) { mWindowPlayer.updateVideoProgress(current, duration); mFullScreenPlayer.updateVideoProgress(current, duration); + mFloatPlayer.updateVideoProgress(current, duration); } @Override @@ -757,6 +822,7 @@ public void onSwitchStreamEnd(boolean success, SuperPlayerDef.PlayerType playerT public void onPlayerTypeChange(SuperPlayerDef.PlayerType playType) { mWindowPlayer.updatePlayType(playType); mFullScreenPlayer.updatePlayType(playType); + mFloatPlayer.updatePlayType(playType); } @Override diff --git a/Demo/superplayerkit/src/main/java/com/tencent/liteav/demo/superplayer/model/SuperPlayerImpl.java b/Demo/superplayerkit/src/main/java/com/tencent/liteav/demo/superplayer/model/SuperPlayerImpl.java index 5c86251..3603816 100644 --- a/Demo/superplayerkit/src/main/java/com/tencent/liteav/demo/superplayer/model/SuperPlayerImpl.java +++ b/Demo/superplayerkit/src/main/java/com/tencent/liteav/demo/superplayer/model/SuperPlayerImpl.java @@ -297,6 +297,11 @@ public void playWithModel(final SuperPlayerModel model) { @Override public void onSuccess(IPlayInfoProtocol protocol, PlayInfoParams param) { TXCLog.i(TAG, "onSuccess: protocol params = " + param.toString()); + IPlayInfoProtocol tmpProtocol = mCurrentProtocol; + if (tmpProtocol == null) { + return; + } + mReportVodStartTime = System.currentTimeMillis(); mVodPlayer.setPlayerView(mVideoView); // 设置HLS安全加固加解密参数 @@ -305,10 +310,11 @@ public void onSuccess(IPlayInfoProtocol protocol, PlayInfoParams param) { setOverlayKeyIv(param.videoId.overlayKey, param.videoId.overlayIv); } } - playModeVideo(mCurrentProtocol); + + playModeVideo(tmpProtocol); updatePlayerType(SuperPlayerDef.PlayerType.VOD); updatePlayProgress(0, 0); - updateVideoImageSpriteAndKeyFrame(mCurrentProtocol.getImageSpriteInfo(), mCurrentProtocol.getKeyFrameDescInfo()); + updateVideoImageSpriteAndKeyFrame(tmpProtocol.getImageSpriteInfo(), tmpProtocol.getKeyFrameDescInfo()); } @Override diff --git a/Demo/superplayerkit/src/main/java/com/tencent/liteav/demo/superplayer/model/VipWatchModel.java b/Demo/superplayerkit/src/main/java/com/tencent/liteav/demo/superplayer/model/VipWatchModel.java new file mode 100644 index 0000000..02d9b45 --- /dev/null +++ b/Demo/superplayerkit/src/main/java/com/tencent/liteav/demo/superplayer/model/VipWatchModel.java @@ -0,0 +1,29 @@ +package com.tencent.liteav.demo.superplayer.model; + +public class VipWatchModel { //展示的提示语信息 + private String tipStr = null; + //试看的时间 默认值是long的最大值,表示不展示VIP试看内容的时间,单位为秒 + private long canWatchTime = Long.MAX_VALUE; + + + public VipWatchModel(String tipStr, long canWatchTime) { + this.tipStr = String.format(tipStr, canWatchTime); + this.canWatchTime = canWatchTime; + } + + public String getTipStr() { + return tipStr; + } + + public void setTipStr(String tipStr) { + this.tipStr = tipStr; + } + + public long getCanWatchTime() { + return canWatchTime; + } + + public void setCanWatchTime(long canWatchTime) { + this.canWatchTime = canWatchTime; + } +} \ No newline at end of file diff --git a/Demo/superplayerkit/src/main/java/com/tencent/liteav/demo/superplayer/ui/player/AbsPlayer.java b/Demo/superplayerkit/src/main/java/com/tencent/liteav/demo/superplayer/ui/player/AbsPlayer.java index 4d2279a..ba01d2c 100644 --- a/Demo/superplayerkit/src/main/java/com/tencent/liteav/demo/superplayer/ui/player/AbsPlayer.java +++ b/Demo/superplayerkit/src/main/java/com/tencent/liteav/demo/superplayer/ui/player/AbsPlayer.java @@ -7,9 +7,11 @@ import android.widget.RelativeLayout; import com.tencent.liteav.demo.superplayer.SuperPlayerDef; +import com.tencent.liteav.demo.superplayer.model.VipWatchModel; import com.tencent.liteav.demo.superplayer.model.entity.PlayImageSpriteInfo; import com.tencent.liteav.demo.superplayer.model.entity.PlayKeyFrameDescInfo; import com.tencent.liteav.demo.superplayer.model.entity.VideoQuality; +import com.tencent.liteav.demo.superplayer.ui.view.VipWatchView; import java.util.List; @@ -19,7 +21,9 @@ public abstract class AbsPlayer extends RelativeLayout implements Player { protected static final int MAX_SHIFT_TIME = 7200; // demo演示直播时移是MAX_SHIFT_TIMEs,即2小时 - protected Callback mControllerCallback; // 播放控制回调 + protected Callback mControllerCallback; // 播放控制回调 + protected VipWatchView mVipWatchView; //用于展示VIP 试看的view控件 + protected Runnable mHideViewRunnable = new Runnable() { @Override @@ -119,6 +123,33 @@ public void updateKeyFrameDescInfo(List list) { } + + public void setVipWatchModel(VipWatchModel vipWatchModel) { + if (mVipWatchView != null) { + mVipWatchView.setVipWatchMode(vipWatchModel); + } + } + + public void hideVipView() { + if (mVipWatchView != null) { + mVipWatchView.hideVipView(); + } + } + + public void hideTipView() { + if (mVipWatchView != null) { + mVipWatchView.hideTipView(); + } + } + + public boolean isShowingVipView(){ + if (mVipWatchView != null) { + return mVipWatchView.isShowing(); + }else { + return false; + } + } + /** * 设置控件的可见性 * diff --git a/Demo/superplayerkit/src/main/java/com/tencent/liteav/demo/superplayer/ui/player/FloatPlayer.java b/Demo/superplayerkit/src/main/java/com/tencent/liteav/demo/superplayer/ui/player/FloatPlayer.java index 3a08128..d49f50a 100644 --- a/Demo/superplayerkit/src/main/java/com/tencent/liteav/demo/superplayer/ui/player/FloatPlayer.java +++ b/Demo/superplayerkit/src/main/java/com/tencent/liteav/demo/superplayer/ui/player/FloatPlayer.java @@ -10,6 +10,7 @@ import com.tencent.liteav.demo.superplayer.R; import com.tencent.liteav.demo.superplayer.SuperPlayerDef; import com.tencent.liteav.demo.superplayer.SuperPlayerGlobalConfig; +import com.tencent.liteav.demo.superplayer.ui.view.VipWatchView; import com.tencent.rtmp.ui.TXCloudVideoView; import java.lang.reflect.Field; @@ -21,7 +22,7 @@ *

* 2、关闭悬浮窗{@link #onClick(View)} */ -public class FloatPlayer extends AbsPlayer implements View.OnClickListener { +public class FloatPlayer extends AbsPlayer implements View.OnClickListener, VipWatchView.VipWatchViewClickListener { private TXCloudVideoView mFloatVideoView; // 悬浮窗中的视频播放view @@ -33,6 +34,9 @@ public class FloatPlayer extends AbsPlayer implements View.OnClickListener { private float mXInView; // 滑动事件距离自身左边界的距离 private float mYInView; // 滑动事件距离自身上边界的距离 + private SuperPlayerDef.PlayerType mPlayType; // 当前播放视频类型 + + public FloatPlayer(Context context) { super(context); initView(context); @@ -56,6 +60,8 @@ private void initView(Context context) { mFloatVideoView = (TXCloudVideoView) findViewById(R.id.superplayer_float_cloud_video_view); ImageView ivClose = (ImageView) findViewById(R.id.superplayer_iv_close); ivClose.setOnClickListener(this); + mVipWatchView = findViewById(R.id.superplayer_vip_watch_view); + mVipWatchView.setVipWatchViewClickListener(this); } /** @@ -113,6 +119,18 @@ public boolean onTouchEvent(MotionEvent event) { return true; } + @Override + public void updatePlayType(SuperPlayerDef.PlayerType type) { + mPlayType = type; + } + + @Override + public void updateVideoProgress(long current, long duration) { + if (mPlayType == SuperPlayerDef.PlayerType.VOD) { + mVipWatchView.setCurrentTime(current); + } + } + /** * 获取系统状态栏高度 */ @@ -146,4 +164,41 @@ private void updateViewPosition() { mControllerCallback.onFloatPositionChange(x, y); } } + + @Override + public void onClickVipTitleBack() { + if (mControllerCallback != null) { + mControllerCallback.onClickVipTitleBack(SuperPlayerDef.PlayerMode.FLOAT); + mControllerCallback.onSeekTo(0); + } + } + + @Override + public void onClickVipRetry() { + if (mControllerCallback != null) { + mControllerCallback.onClickVipRetry(); + } + } + + @Override + public void onShowVipView() { + if (mControllerCallback != null) { + mControllerCallback.onPause(); + } + } + + @Override + public void onClickVipBtn() { + if (mControllerCallback != null) { + mControllerCallback.onClickHandleVip(); + } + } + + @Override + public void onCloseVipTip() { + if (mControllerCallback != null) { + mControllerCallback.onCloseVipTip(); + } + } + } diff --git a/Demo/superplayerkit/src/main/java/com/tencent/liteav/demo/superplayer/ui/player/FullScreenPlayer.java b/Demo/superplayerkit/src/main/java/com/tencent/liteav/demo/superplayer/ui/player/FullScreenPlayer.java index 0df3144..6a58bb4 100644 --- a/Demo/superplayerkit/src/main/java/com/tencent/liteav/demo/superplayer/ui/player/FullScreenPlayer.java +++ b/Demo/superplayerkit/src/main/java/com/tencent/liteav/demo/superplayer/ui/player/FullScreenPlayer.java @@ -17,6 +17,7 @@ import com.tencent.liteav.demo.superplayer.R; import com.tencent.liteav.demo.superplayer.SuperPlayerDef; +import com.tencent.liteav.demo.superplayer.model.VipWatchModel; import com.tencent.liteav.demo.superplayer.model.entity.PlayImageSpriteInfo; import com.tencent.liteav.demo.superplayer.model.entity.PlayKeyFrameDescInfo; import com.tencent.liteav.demo.superplayer.model.net.LogReport; @@ -24,6 +25,7 @@ import com.tencent.liteav.demo.superplayer.ui.view.PointSeekBar; import com.tencent.liteav.demo.superplayer.ui.view.VideoProgressLayout; import com.tencent.liteav.demo.superplayer.model.entity.VideoQuality; +import com.tencent.liteav.demo.superplayer.ui.view.VipWatchView; import com.tencent.liteav.demo.superplayer.ui.view.VodMoreView; import com.tencent.liteav.demo.superplayer.ui.view.VodQualityView; import com.tencent.liteav.demo.superplayer.ui.view.VolumeBrightnessProgressLayout; @@ -57,7 +59,7 @@ * 8、硬件加速监听{@link #onHWAcceleration(boolean)} */ public class FullScreenPlayer extends AbsPlayer implements View.OnClickListener, - VodMoreView.Callback, VodQualityView.Callback, PointSeekBar.OnSeekBarChangeListener, PointSeekBar.OnSeekBarPointClickListener { + VodMoreView.Callback, VodQualityView.Callback, PointSeekBar.OnSeekBarChangeListener, PointSeekBar.OnSeekBarPointClickListener, VipWatchView.VipWatchViewClickListener { // UI控件 private RelativeLayout mLayoutTop; // 顶部标题栏布局 @@ -105,6 +107,7 @@ public class FullScreenPlayer extends AbsPlayer implements View.OnClickListener, private List mVideoQualityList; // 画质列表 private boolean mFirstShowQuality; // 是都是首次显示画质信息 + public FullScreenPlayer(Context context) { super(context); initialize(context); @@ -276,6 +279,8 @@ private void initView(Context context) { mGestureVolumeBrightnessProgressLayout = (VolumeBrightnessProgressLayout) findViewById(R.id.superplayer_gesture_progress); mGestureVideoProgressLayout = (VideoProgressLayout) findViewById(R.id.superplayer_video_progress_layout); mIvWatermark = (ImageView) findViewById(R.id.superplayer_large_iv_water_mark); + mVipWatchView = findViewById(R.id.superplayer_vip_watch_view); + mVipWatchView.setVipWatchViewClickListener(this); } /** @@ -439,7 +444,7 @@ public void updateTitle(String title) { } /** - * 更新是屁播放进度 + * 更新实时播放进度 * * @param current 当前进度(秒) * @param duration 视频总时长(秒) @@ -460,6 +465,8 @@ public void updateVideoProgress(long current, long duration) { long leftTime = mDuration - mProgress; mDuration = mDuration > MAX_SHIFT_TIME ? MAX_SHIFT_TIME : mDuration; percentage = 1 - (float) leftTime / (float) mDuration; + }else { + mVipWatchView.setCurrentTime(current); } if (percentage >= 0 && percentage <= 1) { @@ -593,6 +600,9 @@ public boolean onTouchEvent(MotionEvent event) { mControllerCallback.onSeekTo(seekTime); } mIsChangingSeekBarProgress = false; + if (mPlayType == SuperPlayerDef.PlayerType.VOD) { + mVipWatchView.setCurrentTime(seekTime); + } } } @@ -771,9 +781,16 @@ public void onStopTrackingTouch(PointSeekBar seekBar) { toggleView(mLayoutReplay, false); float percentage = ((float) curProgress) / maxProgress; int position = (int) (mDuration * percentage); + boolean showResult = mVipWatchView.canShowVipWatchView(position); if (mControllerCallback != null) { mControllerCallback.onSeekTo(position); - mControllerCallback.onResume(); + if (!showResult) { + mControllerCallback.onResume(); + return; + } + } + if (showResult) { + mVipWatchView.setCurrentTime(position); } } break; @@ -828,10 +845,14 @@ public void run() { private void setThumbnail(int progress) { float percentage = ((float) progress) / mSeekBarProgress.getMax(); float seekTime = (mDuration * percentage); - if (mTXImageSprite != null) { - Bitmap bitmap = mTXImageSprite.getThumbnail(seekTime); - if (bitmap != null) { - mGestureVideoProgressLayout.setThumbnail(bitmap); + if (mVipWatchView.canShowVipWatchView(seekTime)) { + mGestureVideoProgressLayout.hideThumbnail(); + } else { + if (mTXImageSprite != null) { + Bitmap bitmap = mTXImageSprite.getThumbnail(seekTime); + if (bitmap != null) { + mGestureVideoProgressLayout.setThumbnail(bitmap); + } } } } @@ -895,6 +916,43 @@ public void onQualitySelect(VideoQuality quality) { mVodQualityView.setVisibility(View.GONE); } + + @Override + public void onClickVipTitleBack() { + if (mControllerCallback != null) { + mControllerCallback.onBackPressed(SuperPlayerDef.PlayerMode.FULLSCREEN); + mControllerCallback.onClickVipTitleBack(SuperPlayerDef.PlayerMode.FULLSCREEN); + } + } + + @Override + public void onClickVipRetry() { + if (mControllerCallback != null) { + mControllerCallback.onClickVipRetry(); + } + } + + @Override + public void onShowVipView() { + if (mControllerCallback != null) { + mControllerCallback.onPause(); + } + } + + @Override + public void onClickVipBtn() { + if (mControllerCallback != null) { + mControllerCallback.onClickHandleVip(); + } + } + + @Override + public void onCloseVipTip() { + if (mControllerCallback != null) { + mControllerCallback.onCloseVipTip(); + } + } + /** * 隐藏锁屏按钮的runnable */ diff --git a/Demo/superplayerkit/src/main/java/com/tencent/liteav/demo/superplayer/ui/player/Player.java b/Demo/superplayerkit/src/main/java/com/tencent/liteav/demo/superplayer/ui/player/Player.java index 6bf942b..918c55a 100644 --- a/Demo/superplayerkit/src/main/java/com/tencent/liteav/demo/superplayer/ui/player/Player.java +++ b/Demo/superplayerkit/src/main/java/com/tencent/liteav/demo/superplayer/ui/player/Player.java @@ -2,6 +2,7 @@ import android.graphics.Bitmap; +import android.view.View; import com.tencent.liteav.demo.superplayer.SuperPlayerDef; import com.tencent.liteav.demo.superplayer.model.entity.PlayImageSpriteInfo; @@ -219,5 +220,30 @@ interface Callback { * @param isAccelerate 开启:true 关闭:false */ void onHWAccelerationToggle(boolean isAccelerate); + + /** + * 当用户点击了 开通VIP会员按钮的回调事件 + */ + void onClickHandleVip(); + + /** + * 当点击了VIP试看界面的返回按钮的的回调 + * * @param playMode 当前播放模式: + * 窗口模式 {@link SuperPlayerDef.PlayerMode#WINDOW } + * 全屏模式 {@link SuperPlayerDef.PlayerMode#FULLSCREEN } + * 悬浮窗模式 {@link SuperPlayerDef.PlayerMode#FLOAT } + */ + void onClickVipTitleBack(SuperPlayerDef.PlayerMode playMode); + + /** + * 但点击了VIP页面的重新试看按钮 + */ + void onClickVipRetry(); + + /** + * 当点击了提示语的关闭按钮 + */ + void onCloseVipTip(); + } } diff --git a/Demo/superplayerkit/src/main/java/com/tencent/liteav/demo/superplayer/ui/player/WindowPlayer.java b/Demo/superplayerkit/src/main/java/com/tencent/liteav/demo/superplayer/ui/player/WindowPlayer.java index 28a8b13..73df8b3 100644 --- a/Demo/superplayerkit/src/main/java/com/tencent/liteav/demo/superplayer/ui/player/WindowPlayer.java +++ b/Demo/superplayerkit/src/main/java/com/tencent/liteav/demo/superplayer/ui/player/WindowPlayer.java @@ -17,9 +17,11 @@ import com.tencent.liteav.demo.superplayer.R; import com.tencent.liteav.demo.superplayer.SuperPlayerDef; +import com.tencent.liteav.demo.superplayer.model.VipWatchModel; import com.tencent.liteav.demo.superplayer.model.utils.VideoGestureDetector; import com.tencent.liteav.demo.superplayer.ui.view.PointSeekBar; import com.tencent.liteav.demo.superplayer.ui.view.VideoProgressLayout; +import com.tencent.liteav.demo.superplayer.ui.view.VipWatchView; import com.tencent.liteav.demo.superplayer.ui.view.VolumeBrightnessProgressLayout; /** @@ -36,7 +38,7 @@ * {@link #onStopTrackingTouch(PointSeekBar)} */ public class WindowPlayer extends AbsPlayer implements View.OnClickListener, - PointSeekBar.OnSeekBarChangeListener { + PointSeekBar.OnSeekBarChangeListener, VipWatchView.VipWatchViewClickListener { // UI控件 private LinearLayout mLayoutTop; // 顶部标题栏布局 @@ -163,7 +165,7 @@ public void onSeekGesture(int progress) { mGestureVideoProgressLayout.setProgress(progress); mGestureVideoProgressLayout.show(); - float percentage = ((float) progress) / mSeekBarProgress.getMax(); + float percentage = ((float) progress) / mSeekBarProgress.getMax(); float currentTime = (mDuration * percentage); if (mPlayType == SuperPlayerDef.PlayerType.LIVE || mPlayType == SuperPlayerDef.PlayerType.LIVE_SHIFT) { if (mLivePushDuration > MAX_SHIFT_TIME) { @@ -221,6 +223,8 @@ private void initView(Context context) { setBackground(mBackgroundBmp); mIvWatermark = (ImageView) findViewById(R.id.superplayer_small_iv_water_mark); + mVipWatchView = findViewById(R.id.superplayer_vip_watch_view); + mVipWatchView.setVipWatchViewClickListener(this); } /** @@ -383,6 +387,8 @@ public void updateVideoProgress(long current, long duration) { long leftTime = mDuration - mProgress; mDuration = mDuration > MAX_SHIFT_TIME ? MAX_SHIFT_TIME : mDuration; percentage = 1 - (float) leftTime / (float) mDuration; + } else { + mVipWatchView.setCurrentTime(current); } if (percentage >= 0 && percentage <= 1) { @@ -520,7 +526,7 @@ public boolean onTouchEvent(MotionEvent event) { } mSeekBarProgress.setProgress(progress); - int seekTime; + int seekTime; float percentage = progress * 1.0f / mSeekBarProgress.getMax(); if (mPlayType == SuperPlayerDef.PlayerType.LIVE || mPlayType == SuperPlayerDef.PlayerType.LIVE_SHIFT) { if (mLivePushDuration > MAX_SHIFT_TIME) { @@ -535,6 +541,10 @@ public boolean onTouchEvent(MotionEvent event) { mControllerCallback.onSeekTo(seekTime); } mIsChangingSeekBarProgress = false; + if (mPlayType == SuperPlayerDef.PlayerType.VOD) { + mVipWatchView.setCurrentTime(seekTime); + } + } if (event.getAction() == MotionEvent.ACTION_DOWN) { @@ -580,7 +590,7 @@ public void onClick(View view) { public void onProgressChanged(PointSeekBar seekBar, int progress, boolean fromUser) { if (mGestureVideoProgressLayout != null && fromUser) { mGestureVideoProgressLayout.show(); - float percentage = ((float) progress) / seekBar.getMax(); + float percentage = ((float) progress) / seekBar.getMax(); float currentTime = (mDuration * percentage); if (mPlayType == SuperPlayerDef.PlayerType.LIVE || mPlayType == SuperPlayerDef.PlayerType.LIVE_SHIFT) { if (mLivePushDuration > MAX_SHIFT_TIME) { @@ -613,9 +623,16 @@ public void onStopTrackingTouch(PointSeekBar seekBar) { toggleView(mLayoutReplay, false); float percentage = ((float) curProgress) / maxProgress; int position = (int) (mDuration * percentage); + boolean showResult = mVipWatchView.canShowVipWatchView(position); if (mControllerCallback != null) { mControllerCallback.onSeekTo(position); - mControllerCallback.onResume(); + if (!showResult) { + mControllerCallback.onResume(); + return; + } + } + if (showResult) { + mVipWatchView.setCurrentTime(position); } } break; @@ -633,4 +650,42 @@ public void onStopTrackingTouch(PointSeekBar seekBar) { } postDelayed(mHideViewRunnable, 7000); } + + + + @Override + public void onClickVipTitleBack() { + if (mControllerCallback != null) { + mControllerCallback.onClickVipTitleBack(SuperPlayerDef.PlayerMode.WINDOW); + mControllerCallback.onSeekTo(0); + } + } + + @Override + public void onClickVipRetry() { + if (mControllerCallback != null) { + mControllerCallback.onClickVipRetry(); + } + } + + @Override + public void onShowVipView() { + if (mControllerCallback != null) { + mControllerCallback.onPause(); + } + } + + @Override + public void onClickVipBtn() { + if (mControllerCallback != null) { + mControllerCallback.onClickHandleVip(); + } + } + + @Override + public void onCloseVipTip() { + if (mControllerCallback != null) { + mControllerCallback.onCloseVipTip(); + } + } } diff --git a/Demo/superplayerkit/src/main/java/com/tencent/liteav/demo/superplayer/ui/view/VideoProgressLayout.java b/Demo/superplayerkit/src/main/java/com/tencent/liteav/demo/superplayer/ui/view/VideoProgressLayout.java index b9797d2..89fc568 100644 --- a/Demo/superplayerkit/src/main/java/com/tencent/liteav/demo/superplayer/ui/view/VideoProgressLayout.java +++ b/Demo/superplayerkit/src/main/java/com/tencent/liteav/demo/superplayer/ui/view/VideoProgressLayout.java @@ -87,6 +87,13 @@ public void setThumbnail(Bitmap bitmap) { mIvThumbnail.setImageBitmap(bitmap); } + /** + * 设置缩略图 + */ + public void hideThumbnail(){ + mIvThumbnail.setVisibility(GONE); + } + /** * 设置progressbar的可见性 * diff --git a/Demo/superplayerkit/src/main/java/com/tencent/liteav/demo/superplayer/ui/view/VipWatchView.java b/Demo/superplayerkit/src/main/java/com/tencent/liteav/demo/superplayer/ui/view/VipWatchView.java new file mode 100644 index 0000000..5de0bc7 --- /dev/null +++ b/Demo/superplayerkit/src/main/java/com/tencent/liteav/demo/superplayer/ui/view/VipWatchView.java @@ -0,0 +1,171 @@ +package com.tencent.liteav.demo.superplayer.ui.view; + +import android.content.Context; +import android.text.TextUtils; +import android.util.AttributeSet; +import android.view.LayoutInflater; +import android.view.View; +import android.widget.LinearLayout; +import android.widget.RelativeLayout; +import android.widget.TextView; +import com.tencent.liteav.demo.superplayer.R; +import com.tencent.liteav.demo.superplayer.model.VipWatchModel; + +/** + * VIP 试看界面View + *

+ * showTip(string tip,int times) 展示tip提示view + * 参数说明:tip指展示的文本信息 times是指试看的最长时间,单位为秒 + *

+ * setCurrentTime(int currentTime) + */ +public class VipWatchView extends RelativeLayout implements View.OnClickListener { + + private LinearLayout mLayoutTips = null; + private TextView mTextTips = null; //用于展示 "可试看30s,开通VIP观看完整视频"的提示语信息 + private RelativeLayout mLayoutVip = null; //用于展示VIP重试的view + private VipWatchModel mVipWatchModel = null; // + private VipWatchViewClickListener mVipWatchViewClickListener = null; + + + public VipWatchView(Context context) { + super(context); + initView(); + } + + public VipWatchView(Context context, AttributeSet attrs) { + super(context, attrs); + initView(); + } + + public VipWatchView(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + initView(); + } + + public void setVipWatchViewClickListener(VipWatchViewClickListener mVipWatchViewClickListener) { + this.mVipWatchViewClickListener = mVipWatchViewClickListener; + } + + @Override + public void onClick(View v) { + if (mVipWatchViewClickListener == null) { + return; + } + if (R.id.vip_watch_tip_close == v.getId()) { //关闭按钮 + mVipWatchViewClickListener.onCloseVipTip(); + } else if (R.id.vip_watch_back_img == v.getId()) { //返回按钮 + mVipWatchViewClickListener.onClickVipTitleBack(); + } else if (R.id.vip_watch_retry_btn == v.getId()) { //重试按钮 + mVipWatchViewClickListener.onClickVipRetry(); + } else if (R.id.vip_watch_handle_vip_btn == v.getId()) { + mVipWatchViewClickListener.onClickVipBtn(); + } + } + + public interface VipWatchViewClickListener { + //当点击左上角返回按钮时回调 + void onClickVipTitleBack(); + + //当用于点击重看的时候回调 + void onClickVipRetry(); + + //当展示VIP view的时候回调 + void onShowVipView(); + + //当点击开通VIP按钮的时候回调 + void onClickVipBtn(); + + //当点击关闭提示控件时回调此方法 + void onCloseVipTip(); + } + + /** + * 初始化页面元素 + */ + private void initView() { + LayoutInflater.from(getContext()).inflate(R.layout.superplayer_vod_vipwatch_view, this); + mLayoutTips = findViewById(R.id.vip_watch_tip_view); + mTextTips = findViewById(R.id.vip_watch_tip_txt); + findViewById(R.id.vip_watch_tip_close).setOnClickListener(this); + findViewById(R.id.vip_watch_back_img).setOnClickListener(this); + findViewById(R.id.vip_watch_retry_btn).setOnClickListener(this); + mLayoutVip = findViewById(R.id.vip_view); + findViewById(R.id.vip_watch_handle_vip_btn).setOnClickListener(this); + } + + + /** + * 展示tip提示view + * 可调用多次,以最后一次的数据为准 + */ + public void setVipWatchMode(VipWatchModel vipWatchModel) { + hideVipView(); + this.mVipWatchModel = vipWatchModel; + if (mVipWatchModel != null && !TextUtils.isEmpty(mVipWatchModel.getTipStr())) { + setVisibility(VISIBLE); + mTextTips.setText(mVipWatchModel.getTipStr()); + mLayoutTips.setVisibility(VISIBLE); + } else { + setVisibility(GONE); + mTextTips.setText(""); + mLayoutTips.setVisibility(GONE); + } + } + + /** + * 当视频开始播放的时候,视频播放进度更新时,调用测方法,设置视频当前的播放位置,单位为秒 + * + * @param currentTime 视频已经播放了多长时间,单位为秒 + */ + public void setCurrentTime(float currentTime) { + if (canShowVipWatchView(currentTime)) { + showVipView(); + } + } + + /** + * 用于判断是否可以展示VIP view了 + * @param currentTime 当前视频的播放位置(时间节点) + * @return + */ + public boolean canShowVipWatchView(float currentTime) { + return (mVipWatchModel != null && currentTime >= mVipWatchModel.getCanWatchTime() && !isShowing()); + } + + /** + * 隐藏提示语view + */ + public void hideTipView() { + mLayoutTips.setVisibility(View.GONE); + setVisibility(View.GONE); + } + + /** + * 隐藏调VIP view + */ + public void hideVipView() { + mLayoutVip.setVisibility(GONE); + setVisibility(View.GONE); + } + + /** + * 判断是否展示了全黑的界面 + * + * @return true 表示页面展示的是试看VIP界面 + */ + public boolean isShowing() { + return mLayoutVip.getVisibility() == View.VISIBLE; + } + + private void showVipView() { + if (mVipWatchViewClickListener != null) { + mVipWatchViewClickListener.onShowVipView(); + } + setVisibility(View.VISIBLE); + mLayoutTips.setVisibility(GONE); + mLayoutVip.setVisibility(VISIBLE); + } + + +} diff --git a/Demo/superplayerkit/src/main/res/drawable/superplayer_shape_vip_tip_bg.xml b/Demo/superplayerkit/src/main/res/drawable/superplayer_shape_vip_tip_bg.xml new file mode 100644 index 0000000..75210a8 --- /dev/null +++ b/Demo/superplayerkit/src/main/res/drawable/superplayer_shape_vip_tip_bg.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/Demo/superplayerkit/src/main/res/drawable/superplayer_vipwatch_yellow_shape.xml b/Demo/superplayerkit/src/main/res/drawable/superplayer_vipwatch_yellow_shape.xml new file mode 100755 index 0000000..b803584 --- /dev/null +++ b/Demo/superplayerkit/src/main/res/drawable/superplayer_vipwatch_yellow_shape.xml @@ -0,0 +1,8 @@ + + + + + + + diff --git a/Demo/superplayerkit/src/main/res/layout/superplayer_vod_player_float.xml b/Demo/superplayerkit/src/main/res/layout/superplayer_vod_player_float.xml index ae86c44..e3014ca 100644 --- a/Demo/superplayerkit/src/main/res/layout/superplayer_vod_player_float.xml +++ b/Demo/superplayerkit/src/main/res/layout/superplayer_vod_player_float.xml @@ -17,5 +17,9 @@ android:layout_marginTop="10dp" android:layout_marginRight="10dp" android:src="@drawable/superplayer_ic_float_close" /> + \ No newline at end of file diff --git a/Demo/superplayerkit/src/main/res/layout/superplayer_vod_player_fullscreen.xml b/Demo/superplayerkit/src/main/res/layout/superplayer_vod_player_fullscreen.xml index 30eae04..85ae650 100644 --- a/Demo/superplayerkit/src/main/res/layout/superplayer_vod_player_fullscreen.xml +++ b/Demo/superplayerkit/src/main/res/layout/superplayer_vod_player_fullscreen.xml @@ -238,4 +238,9 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:visibility="gone" /> + + \ No newline at end of file diff --git a/Demo/superplayerkit/src/main/res/layout/superplayer_vod_player_window.xml b/Demo/superplayerkit/src/main/res/layout/superplayer_vod_player_window.xml index b3019e8..0b0c434 100644 --- a/Demo/superplayerkit/src/main/res/layout/superplayer_vod_player_window.xml +++ b/Demo/superplayerkit/src/main/res/layout/superplayer_vod_player_window.xml @@ -152,4 +152,9 @@ android:layout_width="match_parent" android:layout_height="match_parent" android:visibility="gone" /> + + \ No newline at end of file diff --git a/Demo/superplayerkit/src/main/res/layout/superplayer_vod_vipwatch_view.xml b/Demo/superplayerkit/src/main/res/layout/superplayer_vod_vipwatch_view.xml new file mode 100644 index 0000000..e674685 --- /dev/null +++ b/Demo/superplayerkit/src/main/res/layout/superplayer_vod_vipwatch_view.xml @@ -0,0 +1,101 @@ + + + + + + + + + + + + + + + + + + + + + +