diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 000000000..006a2927d --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1,4 @@ +# These are supported funding model platforms + +github: [CarGuo] +custom: https://raw.githubusercontent.com/CarGuo/GSYGithubAppFlutter/master/thanks.jpg diff --git a/.travis.yml b/.travis.yml index 2fbbeaf78..519248b75 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,16 +2,24 @@ language: android jdk: oraclejdk8 android: components: - - platform-tools - - tools - - build-tools-23.0.1 - - android-22 - - extra-android-support - - extra-android-m2repository - + - platform-tools + - tools + - build-tools-28.0.3 + - android-28 + - extra-android-support + - extra-android-m2repository before_install: - - chmod +x gradlew - +- chmod +x gradlew script: - - cd gsyVideoPlayer - - ../gradlew assembleRelease \ No newline at end of file +- cd app +- "../gradlew assembleRelease" + +deploy: + provider: releases + api_key: + secure: 37XZTS6kJROMJtr77FuCwhlYMUaSWzh2wC8aBE49ZSzhJIkhUy3BiaVK2h+AyQtMT7qb0D2c3efuNViJKwR8lQ7BZG5pSG/2bky3ilXINfnCn84SzUXGyHYPzCT/VzjNMcrnkqlawTvtPmoR5ohWJ1RD+oiL2RXMCbjQYmkTA4SZQqdOvcmGsw1rbqtC3085SmF5nB27IAOX29phZSJ/T3J3MTDmFuosfBrQBmFdzOt/9A1n/Qew4npF5xitvQC/rjhVelEXOC015HaJN2co802l8XDLQp2sZvNU5UHaX2ENawe08tHNpuNMKDxwaGVwi9t579OJCbOiNerIIRcM6ZYTy97OdxsMHFhfW+NvMae4pf1B4EwsgI+La9stfERW4XUuK6xL3tmvUZEvre5tuat+Y6aZfzDDObmt1/V8eljGycYt2ab7/sSppi97NBOI/dYAeLv5a1MTrqqfkA0igx5tNcnGPVENW9XmgXyeMHOyBdvYA2plnhhG/ef2G+QxMC+zMH1QTKYceObaKQs9COv3X5pdAH5fDQ1L+oNELG4Q3VlyEDCQ54n9dfrMNVu9V7IyA04iWilv30aOOIFNCJfXP+b2HtB7idV+9UDDsyln+ynesrs3nqQ0dd8MjOxKSIS+GSfqKVLg4r6H9qIAz0U4RbFKc+jlvAjQowhbpa4= + file: build/outputs/apk/release/app-release.apk + skip_cleanup: true + on: + tags: true + all_branches: true \ No newline at end of file diff --git a/01.gif b/01.gif deleted file mode 100644 index 2be922404..000000000 Binary files a/01.gif and /dev/null differ diff --git a/01.jpg b/01.jpg deleted file mode 100644 index f5b5c3130..000000000 Binary files a/01.jpg and /dev/null differ diff --git a/02.gif b/02.gif deleted file mode 100644 index aed60f7a3..000000000 Binary files a/02.gif and /dev/null differ diff --git a/02.jpg b/02.jpg deleted file mode 100644 index 9dc318ce1..000000000 Binary files a/02.jpg and /dev/null differ diff --git a/03.gif b/03.gif deleted file mode 100644 index be6c14d95..000000000 Binary files a/03.gif and /dev/null differ diff --git a/03.jpg b/03.jpg deleted file mode 100644 index d134ec584..000000000 Binary files a/03.jpg and /dev/null differ diff --git a/04.gif b/04.gif deleted file mode 100644 index 554de9831..000000000 Binary files a/04.gif and /dev/null differ diff --git a/04.jpg b/04.jpg deleted file mode 100644 index feb7b8a8e..000000000 Binary files a/04.jpg and /dev/null differ diff --git a/05.gif b/05.gif deleted file mode 100644 index dbd4af180..000000000 Binary files a/05.gif and /dev/null differ diff --git a/09.gif b/09.gif deleted file mode 100644 index 094334329..000000000 Binary files a/09.gif and /dev/null differ diff --git a/DECODERS.md b/DECODERS.md deleted file mode 100644 index 7c9267daf..000000000 --- a/DECODERS.md +++ /dev/null @@ -1,34 +0,0 @@ - -### 项目是基于ijkplayer编译的so,ijkplayer基于FFPMEG - ---------- - -#### 出于so大小考虑,编译so只支持了常用的视频编码,如果需要支持额外类型,可重新编译ijkplayer源码,配置module.sh然后编译so,替换现在项目中的so,注意so的版本要和ijk的java版本一致。编译流程可参考首页编译https的so流程。 - ---------- - -#### 说明:如果出现有声音没画面,或者有画面没声音的异常情况,请先了解以下内容。(DEMO中有些列表开启了静音模式,属于正常情况)** - -简单来说,mp4并不是视频编码,可以称为视频容器,而H264/H263等这样才是视频编码,AAC为音频编码等。 - -对于视频相关的,推荐雷宵骅的视频基础:[视音频编解码技术零基础学习方法](http://blog.csdn.net/leixiaohua1020/article/details/18893769),这里你可以了解到视频和音频相关编码和协议的东西。 - - -项目默认支持的视频编码和音频编码有如下,更多详细配置可查看[编译配置文件](https://github.com/CarGuo/GSYVideoPlayer/blob/master/module-lite-hevc.sh)。 - -``` - --enable-decoder=aac" - --enable-decoder=aac_latm" - --enable-decoder=flv" - --enable-decoder=h263" - --enable-decoder=h263i" - --enable-decoder=h263p" - --enable-decoder=h264" - --enable-decoder=mp3*" - --enable-decoder=vp6" - --enable-decoder=vp6a" - --enable-decoder=vp6f" - --enable-decoder=hevc" - -``` - diff --git a/LICENSE b/LICENSE new file mode 100644 index 000000000..261eeb9e9 --- /dev/null +++ b/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/QUESTION.md b/QUESTION.md deleted file mode 100644 index 39edeefdc..000000000 --- a/QUESTION.md +++ /dev/null @@ -1,159 +0,0 @@ -## GSYVideoPlayer 问题集锦 - -#### 0、依赖不成功的,如果是jitpach的依赖,记得在project下的build.gradle文件jitpack的依赖。 -``` -allprojects { - repositories { - ... - maven { url 'https://jitpack.io' } - } -} -``` -#### 1、导入项目不成功? - - 项目最外部有一个dependencies.gradle,所有的项目依赖都在这里面,然后参考项目根目录的build.gradle,在最顶部有apply from: 'dependencies.gradle',这样gsyVideoPlayer就可以找到对应的依赖了。gradle方便可参考察[Android蹲坑的疑难杂症集锦(兼Gradle) 二](http://www.jianshu.com/p/86e4b336c17d) - - 其次,因为so有五个平台,远程依赖库比较大,依赖的时候如果有条件,可以开启vpn,用L2TP协议,依赖下载会快一些。 - -#### 2、ClassNotFoundException和混淆 - - 确保你的拆包Application配置正常,若是开启混淆,确保混淆已经添加 。 - - 而且有时候你需要的是clear一下。 - -``` --keep class tv.danmaku.ijk.** { *; } --dontwarn tv.danmaku.ijk.** --keep class com.shuyu.gsyvideoplayer.** { *; } --dontwarn com.shuyu.gsyvideoplayer.** - -``` - -#### 3、找不到对应的so或者链接so错误。 - - 可以配置ndk abiFilters,确保使用到的so文件夹下都有对用的so文件,用Analyze Apk查看so是否应打包到各个文件夹。 - - 参考[#issue23](https://github.com/CarGuo/GSYVideoPlayer/issues/23) - - 参考[#issue24](https://github.com/CarGuo/GSYVideoPlayer/issues/24) - - 参考[ndk 过滤选择介绍](https://www.diycode.cc/topics/691) - - 是否已经添加下方的代码到gradle -``` -android { - - - ··· - - defaultConfig { - ··· - ndk { - - //APP的build.gradle设置支持的SO库架构 - - abiFilters 'armeabi', 'armeabi-v7a', 'x86' - } - - } -} -``` - -``` -sourceSets { - main { - jniLibs.srcDirs = ['libs'] - } -} -``` - -#### 3、全屏的时候自动停止了 - - 是否监听了列表滑动了,在监听里更新了列表之类的。 - -#### 4、目前不支持3gp或者mepg。 - -如果拍摄的视频播放不了,可以尝试用使用系统录制的项目:[VideoRecord](https://github.com/CarGuo/VideoRecord) -或者使用JAVACV录制的项目:[FFmpegRecorder](https://github.com/CrazyOrr/FFmpegRecorder ),测试视频是否可以播放。 - -黑屏相关issues: -https://github.com/Bilibili/ijkplayer/issues/2541 -https://github.com/Bilibili/ijkplayer/pull/1875 - - -#### 5、m3u8\HLS的格式视频请关闭cache - -缓存不支持m3u8\HLS,播放m3u8\HLS格式,需要cacheWithPlay为false - -``` -setUp(String url, boolean cacheWithPlay····) - -``` - -#### 6、为什么拖动视屏会弹回来,因为ijk的FFMPEG对关键帧问题,目前无解。 - -#### 7、视频旋转后重新开始,配置AndroidManifest.xml。 -``` - -``` - -#### 8、弱网络加载速度太慢。 -若网络环境不好,可以尝试关闭缓存来播放,因为proxy方式的边播边缓存对于网络的要求有点高。 - -#### 9、如何设置cookie。 -在setUp的时候,设置带有 Map mapHeadData 参数的方法,在Ijk内部其实就是转为setOption方法。 -可参考ijkPlayer的[issues-1150](https://github.com/Bilibili/ijkplayer/issues/1150) - -#### 10、多个分片播放的功能,请查阅: -[issue64](https://github.com/CarGuo/GSYVideoPlayer/issues/64) -[issue490](https://github.com/Bilibili/ijkplayer/issues/490) -[分片播放资料](http://www.jianshu.com/p/ea794a357b48) - -#### 11、有画面没声音,有声音没画面。 -1、这种情况一般都是so里没有打包支持的格式,如果需要支持你想要的格式,可以自己重新编译so,在module配置文件加上需要额外支持的格式。github首页有编译教程。 - -[2、某些时候,TextureView需要开启硬件加速](https://github.com/CarGuo/GSYVideoPlayer/issues/266) - -#### 12、视频声音画面不同步。 - -模拟器的不接! - -1、是否关闭了硬解码! - -2、Activity的config是否配置了! - -3、视频的分辨率和帧数是多少,机器是否支持的了 - -第3步可以通过尝试降低倍数: - -``` -VideoOptionModel videoOptionModel = - new VideoOptionModel(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "framedrop", 50); -List list = new ArrayList<>(); -list.add(videoOptionModel); -GSYVideoManager.instance().setOptionModelList(list); -``` - -#### 13、url视频文件中文名。 - -对于如http://xxxxxxx.中文.mp4的url,如果出现 http 400 error的情况,请自行转换中文url到url编码; -如 http://tool.oschina.net/encode?type=4 这里转化。 - - -#### 14、rtsp播放失败问题 - -https://github.com/CarGuo/GSYVideoPlayer/issues/232 - -https://github.com/CarGuo/GSYVideoPlayer/issues/207 - - -#### 15、m3u8拖动seek之后,加载很长时间 - -https://github.com/Bilibili/ijkplayer/issues/2874 - -https://github.com/CarGuo/GSYVideoPlayer/issues/252 - - diff --git a/README.md b/README.md index 09eae9c05..e4a375fdc 100644 --- a/README.md +++ b/README.md @@ -1,136 +1,276 @@ -![](https://github.com/CarGuo/GSYVideoPlayer/blob/master/home_logo.png) - -## 基于[IJKPlayer](https://github.com/Bilibili/ijkplayer),实现了多功能的视频播放器。 (请仔细阅读下方各项说明,大多数问题可在下方找到解答)。 - -* **支持基本的拖动,声音、亮度调节。** -* **支持边播边缓存,使用了[AndroidVideoCache](https://github.com/danikula/AndroidVideoCache)。** -* **支持视频本身自带rotation的旋转。** -* **增加了重力旋转与手动旋转的同步支持。** -* **支持列表播放。** -* **直接添加控件为封面。** -* **全屏和播放等的动画效果。** -* **列表的全屏效果优化,多种配置模式。** -* **列表的小窗口播放,可拖动。** -* **网络视频加载速度。** -* **锁定/解锁全屏点击功能。** -* **支持快播和慢播。** -* **调整显示比例:默认、16:9、4:3。** -* **暂停时前后台切换不黑屏** -* **调整不同清晰度的支持。** -* **支持IJKPlayer和EXOPlayer切换。** -* **进度条小窗口预览(测试)。** -* **Https支持。** -* **支持播放时旋转画面角度(0,90,180,270)。** -* **连续播放一个列表的视频。** -* **支持全屏与非全屏两套布局切换** -* **弹幕支持** -* **镜像旋转** - +![](https://github.com/CarGuo/GSYVideoPlayer/blob/master/img/home_logo.png) + +## 基于[IJKPlayer](https://github.com/Bilibili/ijkplayer)(兼容系统MediaPlayer与EXOPlayer2),实现了多功能的视频播放器。 (请仔细阅读下方各项说明,大多数问题可在下方找到解答)。 + +> ## [如果克隆太慢,可尝试从码云地址下载](https://gitee.com/CarGuo/GSYVideoPlayer) + +类型 | 功能 +-------- | --- +**缓存**|**边播边缓存,使用了[AndroidVideoCache](https://github.com/danikula/AndroidVideoCache);ExoPlayer使用SimpleCache。** +**协议**|**h263\4\5、Https、concat、rtsp、hls、rtmp、crypto、mpeg等等。[(ijk模式格式支持)](https://github.com/CarGuo/GSYVideoPlayer/blob/master/doc/DECODERS.md)** +**滤镜**|**简单滤镜(马赛克、黑白、色彩过滤、高斯、模糊、模糊等等20多种)、动画、(水印、画面多重播放等)。** +**帧图**|**视频第一帧、视频帧截图功能,视频生成gif功能。** +**播放**|**列表播放、列表连续播放、重力旋转与手动旋转、视频本身rotation旋转属性、快播和慢播、网络视频加载速度。** +**画面**|**调整显示比例:默认、16:9、4:3、填充;播放时旋转画面角度(0,90,180,270);镜像旋转。** +**内核**|**IJKPlayer、EXOPlayer、MediaPlayer切换、自定义内核** +**布局**|**全屏与非全屏两套布局切换、没有任何操作控件的纯播放支持、弹幕功能、继承自定义任何布局。** +**播放**|**单例播放、多个同时播放、视频列表滑动自动播放、列表切换详情页面无缝播放。** +**窗口**|**小窗口、多窗体下(包括桌面)的小窗口播放。** +**广告**|**片头广告、跳过广告支持、中间插入广告功能。** +**字幕**|**[exo2模式下支持自定增加外挂字幕](https://github.com/CarGuo/GSYVideoPlayer/tree/master/app/src/main/java/com/example/gsyvideoplayer/exosubtitle)。** +**更多**|**暂停前后台切换不黑屏;调整不同清晰度的支持;无缝切换支持;锁定/解锁全屏点击功能;进度条小窗口预览(测试)。** +**自定义**|**可自定义渲染层、自定义管理层、自定义播放层(控制层)、自定义缓存层。** + +[ ![Download](https://api.bintray.com/packages/carguo/GSYVideoPlayer/gsyVideoPlayer-java/images/download.svg) ](https://bintray.com/carguo/GSYVideoPlayer/gsyVideoPlayer-java/_latestVersion) [![](https://jitpack.io/v/CarGuo/GSYVideoPlayer.svg)](https://jitpack.io/#CarGuo/GSYVideoPlayer) [ ![Download](https://api.bintray.com/packages/carguo/GSYVideoPlayer/gsyVideoPlayer/images/download.svg) ](https://bintray.com/carguo/GSYVideoPlayer/gsyVideoPlayer/_latestVersion) [![Build Status](https://travis-ci.org/CarGuo/GSYVideoPlayer.svg?branch=master)](https://travis-ci.org/CarGuo/GSYVideoPlayer) -## 使用依赖(支持jcenter和jitpack) +[]() +[![GitHub stars](https://img.shields.io/github/stars/CarGuo/GSYVideoPlayer.svg)](https://github.com/CarGuo/GSYVideoPlayer/stargazers) +[![GitHub forks](https://img.shields.io/github/forks/CarGuo/GSYVideoPlayer.svg)](https://github.com/CarGuo/GSYVideoPlayer/network) +[![GitHub issues](https://img.shields.io/github/issues/CarGuo/GSYVideoPlayer.svg)](https://github.com/CarGuo/GSYVideoPlayer/issues) +[![GitHub license](https://img.shields.io/github/license/CarGuo/GSYVideoPlayer.svg)](https://github.com/CarGuo/GSYVideoPlayer/blob/master/LICENSE) + +### [-----------------微信赞赏链接-----------------](https://github.com/CarGuo/GSYVideoPlayer/blob/master/img/thanks.jpg) + +### [--------------Demo APK 下载地址---------------](https://github.com/CarGuo/GSYVideoPlayer/releases) + +![](http://img.cdn.guoshuyu.cn/WeChat-Code) + +## 一、使用依赖 + +#### 7.0版本使用了anndroidx,support版本请看6.x.x,请查看:[--- 版本更新说明 - 入口](https://github.com/CarGuo/GSYVideoPlayer/blob/master/doc/UPDATE_VERSION.md)。 + +### 1、JCenter 引入方法(推荐) -### 1、JCenter 引入方法 +**你可以选择下面三种的其中一种,在module下的build.gradle添加。** -#### 直接在module下的build.gradle添加 +#### A、直接引入 ``` -compile 'com.shuyu:GSYVideoPlayer:1.6.9' +//完整版引入 +implementation 'com.shuyu:GSYVideoPlayer:7.1.2' ``` --------------------------------------------------------------------------------- +#### B、添加java和你想要的so支持: -### 2、JitPack引入方法 -#### 在project下的build.gradle添加 ``` -allprojects { - repositories { - ... - maven { url 'https://jitpack.io' } - } -} +implementation 'com.shuyu:gsyVideoPlayer-java:7.1.2' + +//是否需要ExoPlayer模式 +implementation 'com.shuyu:GSYVideoPlayer-exo2:7.1.2' + +//根据你的需求ijk模式的so +implementation 'com.shuyu:gsyVideoPlayer-armv5:7.1.2' +implementation 'com.shuyu:gsyVideoPlayer-armv7a:7.1.2' +implementation 'com.shuyu:gsyVideoPlayer-arm64:7.1.2' +implementation 'com.shuyu:gsyVideoPlayer-x64:7.1.2' +implementation 'com.shuyu:gsyVideoPlayer-x86:7.1.2' + ``` -#### 在module下的build.gradle添加 +#### C、支持其他格式协议的(mpeg,rtsp, concat、crypto协议) +A、B普通版本支持263/264/265等,对于mpeg编码会有声音无画面情况。 +C 引入的so支持mpeg编码和其他补充协议,但是so包相对变大。 + ``` -dependencies { - compile 'com.github.CarGuo:GSYVideoPlayer:v1.6.9' -} +implementation 'com.shuyu:gsyVideoPlayer-java:7.1.2' + +//是否需要ExoPlayer模式 +implementation 'com.shuyu:GSYVideoPlayer-exo2:7.1.2' + +//更多ijk的编码支持 +implementation 'com.shuyu:gsyVideoPlayer-ex_so:7.1.2' + +``` + +#### 代码中的全局切换支持(更多请参看下方文档和demo) + ``` --------------------------------------------------------------------------------- +//EXOPlayer内核,支持格式更多 +PlayerFactory.setPlayManager(Exo2PlayerManager.class); +//系统内核模式 +PlayerFactory.setPlayManager(SystemPlayerManager.class); +//ijk内核,默认模式 +PlayerFactory.setPlayManager(IjkPlayerManager.class); + + +//exo缓存模式,支持m3u8,只支持exo +CacheFactory.setCacheManager(ExoPlayerCacheManager.class); +//代理缓存模式,支持所有模式,不支持m3u8等,默认 +CacheFactory.setCacheManager(ProxyCacheManager.class); + + + +//切换渲染模式 +GSYVideoType.setShowType(GSYVideoType.SCREEN_MATCH_FULL); +//默认显示比例 +GSYVideoType.SCREEN_TYPE_DEFAULT = 0; +//16:9 +GSYVideoType.SCREEN_TYPE_16_9 = 1; +//4:3 +GSYVideoType.SCREEN_TYPE_4_3 = 2; +//全屏裁减显示,为了显示正常 CoverImageView 建议使用FrameLayout作为父布局 +GSYVideoType.SCREEN_TYPE_FULL = 4; +//全屏拉伸显示,使用这个属性时,surface_container建议使用FrameLayout +GSYVideoType.SCREEN_MATCH_FULL = -4; +/*** + * SCREEN_TYPE_CUSTOM 下自定义显示比例 + * @param screenScaleRatio 高宽比,如 16:9 + */ +public static void setScreenScaleRatio(float screenScaleRatio) + + +//切换绘制模式 +GSYVideoType.setRenderType(GSYVideoType.SUFRACE); +GSYVideoType.setRenderType(GSYVideoType.GLSURFACE); +GSYVideoType.setRenderType(GSYVideoType.TEXTURE); + -* ### 下方文档以及问题集锦,你想要知道的大部分都在里面。 +//ijk关闭log +IjkPlayerManager.setLogLevel(IjkMediaPlayer.IJK_LOG_SILENT); -* ### 有问题请先下面问题集锦中查阅(如依赖不成功,播放不成功等等)。 -* ### QQ群,有兴趣的可以进来(群里平时可能比较吵):174815284 。 +//exoplayer自定义MediaSource +ExoSourceManager.setExoMediaSourceInterceptListener(new ExoMediaSourceInterceptListener() { + @Override + public MediaSource getMediaSource(String dataSource, boolean preview, boolean cacheEnable, boolean isLooping, File cacheDir) { + //可自定义MediaSource + return null; + } +}); --------------------------------------------------------------------------------- +``` + +### [--- 更多依赖方式请点击 - ](https://github.com/CarGuo/GSYVideoPlayer/blob/master/doc/DEPENDENCIES.md) + +## 二、其他推荐 + +### * QQ群,有兴趣的欢迎(平时吹水吐槽多,因为人数饱和,目前开启付费入群):174815284 。 +### * [Flutter Github客户端](https://github.com/CarGuo/gsy_github_app_flutter) 、[React Native Github客户端](https://github.com/CarGuo/GSYGithubAPP) 、 [Weex Github客户端](https://github.com/CarGuo/GSYGithubAPPWeex) 、 [原生 Kotlin Github客户端](https://github.com/CarGuo/GSYGithubAPPKotlin) +### * [RxFFmpeg Android 的音视频编辑工具](https://github.com/microshow/RxFFmpeg) +### * [RickText](https://github.com/CarGuo/RickText) +### * [LazyRecyclerAdapter](https://github.com/CarGuo/LazyRecyclerAdapter) -## 文档Wiki -### [--- 使用说明、接口文档 - 入口](https://github.com/CarGuo/GSYVideoPlayer/wiki) +## 三、文档Wiki -## 其他 +文档 | 传送门 +-------- | --- +**使用说明**|***[--- 简单使用,快速上手文档](https://github.com/CarGuo/GSYVideoPlayer/blob/master/doc/USE.md)*** +**项目解析说明**|***[--- 项目解析说明、包含项目架构和解析](https://github.com/CarGuo/GSYVideoPlayer/blob/master/doc/GSYVIDEO_PLAYER_PROJECT_INFO.md)*** +接口文档入口|**[--- 使用说明、接口文档 - 入口](https://github.com/CarGuo/GSYVideoPlayer/wiki)** +**问题集锦入口**|***[--- 问题集锦 - 入口(大部分你遇到的问题都在这里解决) ](https://github.com/CarGuo/GSYVideoPlayer/blob/master/doc/QUESTION.md)*** +编码格式|**[--- IJK so文件配置格式说明](https://github.com/CarGuo/GSYVideoPlayer/blob/master/doc/DECODERS.md)** +编译自定义SO|**[--- IJKPlayer编译自定义SO - 入口](http://www.jianshu.com/p/bd289e25d272)** +版本更新说明|**[--- 版本更新说明 - 入口](https://github.com/CarGuo/GSYVideoPlayer/blob/master/doc/UPDATE_VERSION.md)** -### [--- 版本更新说明 - 入口](https://github.com/CarGuo/GSYVideoPlayer/blob/master/UPDATE_VERSION.md) -### [--- 问题集锦 - 入口 ](https://github.com/CarGuo/GSYVideoPlayer/blob/master/QUESTION.md) -### [--- IJKPlayer问题 - 入口](http://www.jianshu.com/p/220b00d00deb)  -### [--- IJKPlayer编译自定义SO - 入口](http://www.jianshu.com/p/bd289e25d272)  -### [--- 简书详解 (项目的基础)- 入口](http://www.jianshu.com/p/9fe377dd9750) -### [--- 项目支持视频格式(如果遇上黑屏,没声音)](https://github.com/CarGuo/GSYVideoPlayer/blob/master/DECODERS.md) -  -## 运行效果 +![框架图](https://raw.githubusercontent.com/CarGuo/GSYVideoPlayer/master/img/StructureChart2.jpg) -* ### 1、打开一个播放 - +## 四、运行效果 -* ### 2、列表/详情模式 +* ### 1、打开一个播放(旋转、镜像、填充) + + +* ### 2、列表/详情模式(动画、旋转、小窗体)
- - - + + +
* ### 3、弹幕 - + + +* ### 4、滤镜和GL动画 + +* ### 6、背景铺满模糊播放 -* ### 4、进度条小窗口预览 - + +* ### 7、进度条小窗口预览 + -## 近期版本 +## 五、近期版本 -### 1.6.9(2017-07-08) >>>> [片头广告功能推迟到1.7.0] -* 修改setup的设置参数。 -* 升级修改所有回调接口,回调接口中返回当前播放器。 -* 修正播放本地文件错误,会错删文件问题。 -* 兼容Appbar中使用,感谢[@loveRose](https://github.com/loveRose) -* 非全屏播放器可获取全屏播放器对象。 +### 7.1.2(2019-12-02) + +* fix #2436 增加 exo 的 http timeout ``` -/** - * 获取全屏播放器对象 - * - * @return GSYVideoPlayer 如果没有则返回空。 - */ -public GSYVideoPlayer getFullWindowPlayer() +ExoSourceManager + + public static void setHttpReadTimeout(int httpReadTimeout) + + public static void setHttpConnectTimeout(int httpConnectTimeout) + +``` +* 优化视频尺寸显示计算 +* 增加 exo 支持 raw 文件播放 + +``` +String url = RawResourceDataSource.buildRawResourceUri(R.raw.test).toString(); +``` +* 增加模拟下载共用缓存例子 DetailDownloadPlayer +* 适配 android 10 的全屏 +* fix #2382、#2411、#2343、#2379、#2350、#2328 +* 增加设置自定义显示比例的支持 + +``` +GSYVideoType.setScreenScaleRatio +``` +* 增加外挂字幕例子 [exo2模式下支持自定增加外挂字幕](https://github.com/CarGuo/GSYVideoPlayer/tree/master/app/src/main/java/com/example/gsyvideoplayer/exosubtitle) + + +### 7.1.1(2019-10-12) + +* fix #2244、#2252(resolveFullVideoShow 不执行情况)、#2279、#2280 +* fix #2303(去除 TimerTask)、#2306(某些机型退到后台返回不显示) +* 增加 setNeedAutoAdaptation +``` + /** + * 是否需要适配在竖屏横屏时,由于刘海屏或者打孔屏占据空间,导致标题显示被遮盖的问题 + * + * @param needAutoAdaptation 默认false + */ + public void setNeedAutoAdaptation(boolean needAutoAdaptation) +``` + +### 7.1.0(2019-09-01) + +* update ExoPlayer to 2.10.4 +* 添加沉浸式支持 +* 增加 IPlayerInitSuccessListener 播放器初始化成果回调 +``` +GSYVideoManager + .instance() + .setPlayerInitSuccessListener(new IPlayerInitSuccessListener() { + ///播放器初始化成果回调,可用于播放前的自定义设置 + @Override + public void onPlayerInitSuccess(IMediaPlayer player, GSYModel model) { + if (player instanceof IjkExo2MediaPlayer) { + ((IjkExo2MediaPlayer) player).setTrackSelector(new DefaultTrackSelector()); + ((IjkExo2MediaPlayer) player).setLoadControl(new DefaultLoadControl()); + } + } + }); ``` +* fix #2142 +* 增加硬解码不花屏幕 [RecyclerView3Activity](https://github.com/CarGuo/GSYVideoPlayer/blob/master/app/src/main/java/com/example/gsyvideoplayer/RecyclerView3Activity.java) -### 更多版本请查阅:[版本更新说明](https://github.com/CarGuo/GSYVideoPlayer/blob/master/UPDATE_VERSION.md) +### 非 androidx 版本为 6.0.3 以下版本。更多兼容版本请查阅版本更新。 -------------------- +### 更多版本请查阅:[版本更新说明](https://github.com/CarGuo/GSYVideoPlayer/blob/master/doc/UPDATE_VERSION.md) -## 关于Issues +## 六、关于Issues ``` 提问题前可先查阅上方文档和说明,请在Demo中复现问题。 @@ -143,13 +283,43 @@ public GSYVideoPlayer getFullWindowPlayer() 4、补充问题的机型,android版本。 ``` -## 混淆 +## 七、混淆 ``` +-keep class com.shuyu.gsyvideoplayer.video.** { *; } +-dontwarn com.shuyu.gsyvideoplayer.video.** +-keep class com.shuyu.gsyvideoplayer.video.base.** { *; } +-dontwarn com.shuyu.gsyvideoplayer.video.base.** +-keep class com.shuyu.gsyvideoplayer.utils.** { *; } +-dontwarn com.shuyu.gsyvideoplayer.utils.** -keep class tv.danmaku.ijk.** { *; } -dontwarn tv.danmaku.ijk.** --keep class com.shuyu.gsyvideoplayer.** { *; } --dontwarn com.shuyu.gsyvideoplayer.** + +-keep public class * extends android.view.View{ + *** get*(); + void set*(***); + public (android.content.Context); + public (android.content.Context, android.util.AttributeSet); + public (android.content.Context, android.util.AttributeSet, int); +} +``` + +## 温馨提示 + +#### [如果克隆太慢,可尝试从码云地址下载](https://gitee.com/CarGuo/GSYVideoPlayer) + +``` +关于自定义和出现问题的请先看问题集锦、demo、issue。 + +多了解一些音视频的基础常识,对容器,音视频编码,ffmpeg先做一些了解,以及mediacodec等的不同。 +尽量少出现为什么别的能播的问题哟。 + +播放器的可自定义还是挺高的,定制请参考demo,多看源码。现在的功能有些多,demo也在不断的更新。 + +一些新功能和项目结构也在不断的调整。 + +欢迎提出问题,谢谢。 + ``` ## 依赖大小参考 @@ -157,9 +327,17 @@ public GSYVideoPlayer getFullWindowPlayer() ![](https://ooo.0o0.ooo/2017/06/15/5941f343a39f5.png) +## 非常感谢您的支持 + +#### 撸码不易,如果对你有所帮助,欢迎您的赞赏 + +![](https://github.com/CarGuo/GSYVideoPlayer/blob/master/img/thanks.jpg) + + ## License ``` 请参看IJKPlayer和AndroidVideoCache相关协议。 -项目最开始是从jiecao过来的,只是后来方向不同,所以慢慢的也异化了。 +项目最开始是从jiecao过来的,改着改着直接重构了。 +偶尔有一变量和方法名可能还有点jiaozi的影子,但是基本是一个新项目。 ``` diff --git a/app/build.gradle b/app/build.gradle index 6eb40c7fa..f23bfd3b2 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,5 +1,5 @@ apply plugin: 'com.android.application' -apply plugin: 'com.neenbedankt.android-apt' + android { @@ -10,13 +10,17 @@ android { defaultConfig { applicationId "com.example.gsyvideoplayer" + multiDexEnabled true + minSdkVersion globalConfiguration.androidMinSdkVersion targetSdkVersion globalConfiguration.androidTargetSdkVersion - versionCode 3 - versionName "1.4.9" - + versionCode 6 + versionName "5.0.0" + javaCompileOptions { + annotationProcessorOptions.includeCompileClasspath = true + } ndk { //设置支持的SO库架构 abiFilters 'armeabi', 'armeabi-v7a', 'x86' @@ -24,10 +28,21 @@ android { } + + signingConfigs { + releaseConfig { + storeFile file("../debug.jks") + storePassword "123456" + keyAlias "debug" + keyPassword "123456" + } + } + buildTypes { release { minifyEnabled false + signingConfig signingConfigs.releaseConfig proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } @@ -42,21 +57,58 @@ android { jniLibs.srcDirs = ['libs'] } } + + compileOptions { targetCompatibility JavaVersion.VERSION_1_8 } + + lintOptions { + abortOnError false + } + } dependencies { - compile fileTree(dir: 'libs', include: ['*.jar']) + implementation fileTree(dir: 'libs', include: ['*.jar']) def viewDependencies = rootProject.ext.viewDependencies def androidDependencies = rootProject.ext.androidDependencies def dataDependencies = rootProject.ext.dataDependencies - apt viewDependencies.apt_butterKnife - compile viewDependencies.butterKnife - compile androidDependencies.recyclerView - compile viewDependencies.DanmakuFlameMaster - compile viewDependencies.ndkbitmap_armv7a - compile viewDependencies.ndkbitmap_armv5 - compile viewDependencies.ndkbitmap_x86 - compile androidDependencies.support_v4 - //debugCompile dataDependencies.leakcanary - compile project(':gsyVideoPlayer') + annotationProcessor viewDependencies.apt_butterKnife + implementation viewDependencies.butterKnife + implementation androidDependencies.recyclerView + implementation viewDependencies.DanmakuFlameMaster + implementation viewDependencies.ndkbitmap_armv7a + implementation viewDependencies.ndkbitmap_armv5 + implementation viewDependencies.ndkbitmap_x86 + implementation viewDependencies.glide + implementation viewDependencies.glideProcessor + implementation androidDependencies.support_v4 + implementation androidDependencies.design + + implementation project(':gsyVideoPlayer') + + implementation dataDependencies.okhttpUtil + implementation dataDependencies.okhttp + + implementation(dataDependencies.permissionsdispatcher) { + // if you don't use android.app.Fragment you can exclude support for them + exclude module: "support-v13" + } + annotationProcessor dataDependencies.permissionsdispatcherProcessor + + //jcenter + //implementation "com.shuyu:GSYVideoPlayer:$gsyVideoVersion" + + //implementation "com.shuyu:gsyVideoPlayer-java:$gsyVideoVersion' + + //implementation "com.shuyu:gsyVideoPlayer-armv5:$gsyVideoVersion" + //implementation "com.shuyu:gsyVideoPlayer-armv7a:$gsyVideoVersion" + //implementation "com.shuyu:gsyVideoPlayer-arm64:$gsyVideoVersion" + //implementation "com.shuyu:gsyVideoPlayer-x64:$gsyVideoVersion" + //implementation "com.shuyu:gsyVideoPlayer-x86:$gsyVideoVersion" + + //implementation "com.shuyu:gsyVideoPlayer-ex_so:$gsyVideoVersion" + + + debugImplementation dataDependencies.leakcanary + releaseImplementation dataDependencies.leakcanary_no_op + } diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro index 39b7c73b0..31101cb63 100644 --- a/app/proguard-rules.pro +++ b/app/proguard-rules.pro @@ -40,12 +40,6 @@ native ; } - --keepclassmembers class fqcn.of.javascript.interface.for.webview { - public *; -} - - -keepclassmembers class * implements java.io.Serializable { static final long serialVersionUID; private static final java.io.ObjectStreamField[] serialPersistentFields; @@ -77,7 +71,53 @@ public static ** valueOf(java.lang.String); } --keep class com.shuyu.gsyvideoplayer.** { *; } --dontwarn com.shuyu.gsyvideoplayer.** +# 对于带有回调函数的onXXEvent、**On*Listener的,不能被混淆 +-keepclassmembers class * { + void *(**On*Event); + void *(**On*Listener); +} + +-keepclassmembers class * extends android.webkit.webViewClient { + public void *(android.webkit.WebView, java.lang.String, android.graphics.Bitmap); + public boolean *(android.webkit.WebView, java.lang.String); +} +-keepclassmembers class * extends android.webkit.webViewClient { + public void *(android.webkit.webView, jav.lang.String); +} + + +# ButterKnife +-keep class butterknife.** { *; } +-dontwarn butterknife.internal.** +-keep class **$$ViewBinder { *; } +-keepclasseswithmembernames class * { + @butterknife.* ; +} +-keepclasseswithmembernames class * { + @butterknife.* ; +} + +# Glide +-keep public class * implements com.bumptech.glide.module.GlideModule +-keep public enum com.bumptech.glide.load.resource.bitmap.ImageHeaderParser$** { + **[] $VALUES; + public *; +} + + +# 保留我们自定义控件(继承自View)不被混淆 +-keep public class * extends android.view.View{ + *** get*(); + void set*(***); + public (android.content.Context); + public (android.content.Context, android.util.AttributeSet); + public (android.content.Context, android.util.AttributeSet, int); +} +-keep class com.shuyu.gsyvideoplayer.video.** { *; } +-dontwarn com.shuyu.gsyvideoplayer.video.** +-keep class com.shuyu.gsyvideoplayer.video.base.** { *; } +-dontwarn com.shuyu.gsyvideoplayer.video.base.** +-keep class com.shuyu.gsyvideoplayer.utils.** { *; } +-dontwarn com.shuyu.gsyvideoplayer.utils.** -keep class tv.danmaku.ijk.** { *; } -dontwarn tv.danmaku.ijk.** \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 66a5dc411..992ee6009 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -1,5 +1,6 @@ @@ -8,7 +9,7 @@ -   + @@ -30,55 +33,202 @@ - + + + + + + + android:theme="@style/AppTheme.NoActionBar" /> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/java/com/example/gsyvideoplayer/AudioDetailPlayer.java b/app/src/main/java/com/example/gsyvideoplayer/AudioDetailPlayer.java new file mode 100644 index 000000000..14537b469 --- /dev/null +++ b/app/src/main/java/com/example/gsyvideoplayer/AudioDetailPlayer.java @@ -0,0 +1,230 @@ +package com.example.gsyvideoplayer; + +import android.content.res.Configuration; +import android.os.Bundle; +import android.view.View; +import android.widget.ImageView; +import android.widget.RelativeLayout; + +import androidx.appcompat.app.AppCompatActivity; +import androidx.core.widget.NestedScrollView; + +import com.example.gsyvideoplayer.video.AudioSampleVideo; +import com.example.gsyvideoplayer.video.LandLayoutVideo; +import com.google.android.exoplayer2.SeekParameters; +import com.shuyu.gsyvideoplayer.GSYVideoManager; +import com.shuyu.gsyvideoplayer.builder.GSYVideoOptionBuilder; +import com.shuyu.gsyvideoplayer.listener.GSYSampleCallBack; +import com.shuyu.gsyvideoplayer.listener.GSYVideoProgressListener; +import com.shuyu.gsyvideoplayer.listener.LockClickListener; +import com.shuyu.gsyvideoplayer.utils.Debuger; +import com.shuyu.gsyvideoplayer.utils.OrientationUtils; +import com.shuyu.gsyvideoplayer.video.base.GSYVideoPlayer; + +import java.util.HashMap; +import java.util.Map; + +import butterknife.BindView; +import butterknife.ButterKnife; +import tv.danmaku.ijk.media.exo2.Exo2PlayerManager; + + +public class AudioDetailPlayer extends AppCompatActivity { + + @BindView(R.id.post_detail_nested_scroll) + NestedScrollView postDetailNestedScroll; + + //推荐使用StandardGSYVideoPlayer,功能一致 + //CustomGSYVideoPlayer部分功能处于试验阶段 + @BindView(R.id.detail_player) + AudioSampleVideo detailPlayer; + + @BindView(R.id.activity_detail_player) + RelativeLayout activityDetailPlayer; + + private boolean isPlay; + private boolean isPause; + + private OrientationUtils orientationUtils; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_detail_audio_player); + ButterKnife.bind(this); + + String url = getUrl(); + + resolveNormalVideoUI(); + + //外部辅助的旋转,帮助全屏 + orientationUtils = new OrientationUtils(this, detailPlayer); + //初始化不打开外部的旋转 + orientationUtils.setEnable(false); + + detailPlayer.loadCoverImage("", R.drawable.lololo); + + Map header = new HashMap<>(); + header.put("ee", "33"); + header.put("allowCrossProtocolRedirects", "true"); + GSYVideoOptionBuilder gsyVideoOption = new GSYVideoOptionBuilder(); + gsyVideoOption + .setIsTouchWiget(true) + .setRotateViewAuto(false) + .setLockLand(false) + .setAutoFullWithSize(false) + .setShowFullAnimation(false) + .setNeedLockFull(true) + .setUrl(url) + .setMapHeadData(header) + .setCacheWithPlay(false) + .setVideoTitle("测试视频") + .setVideoAllCallBack(new GSYSampleCallBack() { + @Override + public void onPrepared(String url, Object... objects) { + Debuger.printfError("***** onPrepared **** " + objects[0]); + Debuger.printfError("***** onPrepared **** " + objects[1]); + super.onPrepared(url, objects); + //开始播放了才能旋转和全屏 + orientationUtils.setEnable(true); + isPlay = true; + + //设置 seek 的临近帧。 + if (detailPlayer.getGSYVideoManager().getPlayer() instanceof Exo2PlayerManager) { + ((Exo2PlayerManager) detailPlayer.getGSYVideoManager().getPlayer()).setSeekParameter(SeekParameters.NEXT_SYNC); + Debuger.printfError("***** setSeekParameter **** "); + } + } + + @Override + public void onEnterFullscreen(String url, Object... objects) { + super.onEnterFullscreen(url, objects); + Debuger.printfError("***** onEnterFullscreen **** " + objects[0]);//title + Debuger.printfError("***** onEnterFullscreen **** " + objects[1]);//当前全屏player + } + + @Override + public void onAutoComplete(String url, Object... objects) { + super.onAutoComplete(url, objects); + } + + @Override + public void onClickStartError(String url, Object... objects) { + super.onClickStartError(url, objects); + } + + @Override + public void onQuitFullscreen(String url, Object... objects) { + super.onQuitFullscreen(url, objects); + Debuger.printfError("***** onQuitFullscreen **** " + objects[0]);//title + Debuger.printfError("***** onQuitFullscreen **** " + objects[1]);//当前非全屏player + if (orientationUtils != null) { + orientationUtils.backToProtVideo(); + } + } + }) + .setLockClickListener(new LockClickListener() { + @Override + public void onClick(View view, boolean lock) { + if (orientationUtils != null) { + //配合下方的onConfigurationChanged + orientationUtils.setEnable(!lock); + } + } + }) + .setGSYVideoProgressListener(new GSYVideoProgressListener() { + @Override + public void onProgress(int progress, int secProgress, int currentPosition, int duration) { + Debuger.printfLog(" progress " + progress + " secProgress " + secProgress + " currentPosition " + currentPosition + " duration " + duration); + } + }) + .build(detailPlayer); + + detailPlayer.getFullscreenButton().setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + //直接横屏 + orientationUtils.resolveByClick(); + + //第一个true是否需要隐藏actionbar,第二个true是否需要隐藏statusbar + detailPlayer.startWindowFullscreen(AudioDetailPlayer.this, true, true); + } + }); + } + + @Override + public void onBackPressed() { + + if (orientationUtils != null) { + orientationUtils.backToProtVideo(); + } + + if (GSYVideoManager.backFromWindowFull(this)) { + return; + } + super.onBackPressed(); + } + + + @Override + protected void onPause() { + getCurPlay().onVideoPause(); + super.onPause(); + isPause = true; + } + + @Override + protected void onResume() { + getCurPlay().onVideoResume(false); + super.onResume(); + isPause = false; + } + + @Override + protected void onDestroy() { + super.onDestroy(); + if (isPlay) { + getCurPlay().release(); + } + //GSYPreViewManager.instance().releaseMediaPlayer(); + if (orientationUtils != null) + orientationUtils.releaseListener(); + } + + + @Override + public void onConfigurationChanged(Configuration newConfig) { + super.onConfigurationChanged(newConfig); + //如果旋转了就全屏 + if (isPlay && !isPause) { + detailPlayer.onConfigurationChanged(this, newConfig, orientationUtils, true, true); + } + } + + + private void resolveNormalVideoUI() { + //增加title + detailPlayer.getTitleTextView().setVisibility(View.GONE); + detailPlayer.getBackButton().setVisibility(View.GONE); + } + + private GSYVideoPlayer getCurPlay() { + if (detailPlayer.getFullWindowPlayer() != null) { + return detailPlayer.getFullWindowPlayer(); + } + return detailPlayer; + } + + + private String getUrl() { + + String url = "android.resource://" + getPackageName() + "/" + R.raw.test3; + //注意,用ijk模式播放raw视频,这个必须打开 + GSYVideoManager.instance().enableRawPlay(getApplicationContext()); + + ///exo raw 支持 + //String url = RawResourceDataSource.buildRawResourceUri(R.raw.test).toString(); + + return url; + } +} diff --git a/app/src/main/java/com/example/gsyvideoplayer/AutoPlayRecyclerViewActivity.java b/app/src/main/java/com/example/gsyvideoplayer/AutoPlayRecyclerViewActivity.java new file mode 100644 index 000000000..9b8b4438d --- /dev/null +++ b/app/src/main/java/com/example/gsyvideoplayer/AutoPlayRecyclerViewActivity.java @@ -0,0 +1,144 @@ +package com.example.gsyvideoplayer; + + +import android.content.pm.ActivityInfo; +import android.content.res.Configuration; +import android.os.Build; +import android.os.Bundle; +import android.transition.Explode; +import android.view.Window; + +import com.example.gsyvideoplayer.adapter.RecyclerBaseAdapter; +import com.example.gsyvideoplayer.adapter.RecyclerNormalAdapter; +import com.example.gsyvideoplayer.model.VideoModel; +import com.example.gsyvideoplayer.utils.ScrollCalculatorHelper; +import com.shuyu.gsyvideoplayer.GSYVideoManager; +import com.shuyu.gsyvideoplayer.utils.CommonUtil; +import java.util.ArrayList; +import java.util.List; + +import androidx.appcompat.app.AppCompatActivity; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; +import butterknife.BindView; +import butterknife.ButterKnife; + +/** + * 类似微博视频,滑动到具体位置自动播放 + */ +public class AutoPlayRecyclerViewActivity extends AppCompatActivity { + + + @BindView(R.id.list_item_recycler) + RecyclerView videoList; + + LinearLayoutManager linearLayoutManager; + + RecyclerBaseAdapter recyclerBaseAdapter; + + List dataList = new ArrayList<>(); + + boolean mFull = false; + + ScrollCalculatorHelper scrollCalculatorHelper; + + @Override + protected void onCreate(Bundle savedInstanceState) { + // 设置一个exit transition + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + getWindow().requestFeature(Window.FEATURE_CONTENT_TRANSITIONS); + getWindow().setEnterTransition(new Explode()); + getWindow().setExitTransition(new Explode()); + } + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_recycler_view); + ButterKnife.bind(this); + + resolveData(); + + + //限定范围为屏幕一半的上下偏移180 + int playTop = CommonUtil.getScreenHeight(this) / 2 - CommonUtil.dip2px(this, 180); + int playBottom = CommonUtil.getScreenHeight(this) / 2 + CommonUtil.dip2px(this, 180); + //自定播放帮助类 + scrollCalculatorHelper = new ScrollCalculatorHelper(R.id.video_item_player, playTop, playBottom); + + final RecyclerNormalAdapter recyclerNormalAdapter = new RecyclerNormalAdapter(this, dataList); + linearLayoutManager = new LinearLayoutManager(this); + videoList.setLayoutManager(linearLayoutManager); + videoList.setAdapter(recyclerNormalAdapter); + + videoList.addOnScrollListener(new RecyclerView.OnScrollListener() { + + int firstVisibleItem, lastVisibleItem; + + @Override + public void onScrollStateChanged(RecyclerView recyclerView, int newState) { + super.onScrollStateChanged(recyclerView, newState); + scrollCalculatorHelper.onScrollStateChanged(recyclerView, newState); + } + + @Override + public void onScrolled(RecyclerView recyclerView, int dx, int dy) { + super.onScrolled(recyclerView, dx, dy); + firstVisibleItem = linearLayoutManager.findFirstVisibleItemPosition(); + lastVisibleItem = linearLayoutManager.findLastVisibleItemPosition(); + + //这是滑动自动播放的代码 + if (!mFull) { + scrollCalculatorHelper.onScroll(recyclerView, firstVisibleItem, lastVisibleItem, lastVisibleItem - firstVisibleItem); + } + } + }); + + } + + @Override + public void onConfigurationChanged(Configuration newConfig) { + super.onConfigurationChanged(newConfig); + //如果旋转了就全屏 + if (newConfig.orientation != ActivityInfo.SCREEN_ORIENTATION_USER) { + mFull = false; + } else { + mFull = true; + } + + } + + @Override + public void onBackPressed() { + if (GSYVideoManager.backFromWindowFull(this)) { + return; + } + super.onBackPressed(); + } + + @Override + protected void onPause() { + super.onPause(); + GSYVideoManager.onPause(); + } + + @Override + protected void onResume() { + super.onResume(); + GSYVideoManager.onResume(); + } + + @Override + protected void onDestroy() { + super.onDestroy(); + GSYVideoManager.releaseAllVideos(); + } + + + private void resolveData() { + for (int i = 0; i < 19; i++) { + VideoModel videoModel = new VideoModel(); + dataList.add(videoModel); + } + if (recyclerBaseAdapter != null) + recyclerBaseAdapter.notifyDataSetChanged(); + } + +} diff --git a/app/src/main/java/com/example/gsyvideoplayer/DanmkuVideoActivity.java b/app/src/main/java/com/example/gsyvideoplayer/DanmkuVideoActivity.java index 07a91513c..342e08089 100644 --- a/app/src/main/java/com/example/gsyvideoplayer/DanmkuVideoActivity.java +++ b/app/src/main/java/com/example/gsyvideoplayer/DanmkuVideoActivity.java @@ -1,24 +1,29 @@ package com.example.gsyvideoplayer; -import android.content.pm.ActivityInfo; import android.content.res.Configuration; import android.os.Bundle; -import android.support.v4.widget.NestedScrollView; -import android.support.v7.app.AppCompatActivity; + +import androidx.core.widget.NestedScrollView; +import androidx.appcompat.app.AppCompatActivity; +import android.text.TextUtils; import android.view.View; import android.widget.ImageView; import android.widget.RelativeLayout; -import com.example.gsyvideoplayer.listener.SampleListener; import com.example.gsyvideoplayer.video.DanmakuVideoPlayer; -import com.shuyu.gsyvideoplayer.GSYPreViewManager; -import com.shuyu.gsyvideoplayer.GSYVideoPlayer; +import com.shuyu.gsyvideoplayer.GSYVideoManager; +import com.shuyu.gsyvideoplayer.listener.GSYSampleCallBack; +import com.shuyu.gsyvideoplayer.video.base.GSYVideoPlayer; import com.shuyu.gsyvideoplayer.listener.LockClickListener; import com.shuyu.gsyvideoplayer.utils.OrientationUtils; -import com.shuyu.gsyvideoplayer.video.StandardGSYVideoPlayer; +import com.zhy.http.okhttp.OkHttpUtils; +import com.zhy.http.okhttp.callback.FileCallBack; + +import java.io.File; import butterknife.BindView; import butterknife.ButterKnife; +import okhttp3.Call; /** * Created by guoshuyu on 2017/2/19. @@ -39,6 +44,7 @@ public class DanmkuVideoActivity extends AppCompatActivity { private boolean isPlay; private boolean isPause; + private boolean isDestory; private OrientationUtils orientationUtils; @@ -54,7 +60,8 @@ protected void onCreate(Bundle savedInstanceState) { danmakuVideoPlayer.setShrinkImageRes(R.drawable.custom_shrink); danmakuVideoPlayer.setEnlargeImageRes(R.drawable.custom_enlarge); - String url = "http://baobab.wdjcdn.com/14564977406580.mp4"; + //String url = "https://res.exexm.com/cw_145225549855002"; + String url = "http://9890.vod.myqcloud.com/9890_4e292f9a3dd011e6b4078980237cc3d3.f20.mp4"; //String url = "https://res.exexm.com/cw_145225549855002"; danmakuVideoPlayer.setUp(url, true, null, "测试视频"); @@ -90,13 +97,14 @@ public void onClick(View v) { } }); - danmakuVideoPlayer.setStandardVideoAllCallBack(new SampleListener() { + danmakuVideoPlayer.setVideoAllCallBack(new GSYSampleCallBack() { @Override public void onPrepared(String url, Object... objects) { super.onPrepared(url, objects); //开始播放了才能旋转和全屏 orientationUtils.setEnable(true); isPlay = true; + getDanmu(); } @Override @@ -137,7 +145,7 @@ public void onBackPressed() { orientationUtils.backToProtVideo(); } - if (StandardGSYVideoPlayer.backFromWindowFull(this)) { + if (GSYVideoManager.backFromWindowFull(this)) { return; } super.onBackPressed(); @@ -146,12 +154,14 @@ public void onBackPressed() { @Override protected void onPause() { + getCurPlay().onVideoPause(); super.onPause(); isPause = true; } @Override protected void onResume() { + getCurPlay().onVideoResume(); super.onResume(); isPause = false; } @@ -159,38 +169,59 @@ protected void onResume() { @Override protected void onDestroy() { super.onDestroy(); - GSYVideoPlayer.releaseAllVideos(); + if (isPlay) { + getCurPlay().release(); + } + //GSYPreViewManager.instance().releaseMediaPlayer(); if (orientationUtils != null) orientationUtils.releaseListener(); + + isDestory = true; } + @Override public void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); //如果旋转了就全屏 if (isPlay && !isPause) { - if (newConfig.orientation == ActivityInfo.SCREEN_ORIENTATION_USER) { - if (!danmakuVideoPlayer.isIfCurrentIsFullscreen()) { - danmakuVideoPlayer.startWindowFullscreen(DanmkuVideoActivity.this, true, true); - } - } else { - //新版本isIfCurrentIsFullscreen的标志位内部提前设置了,所以不会和手动点击冲突 - if (danmakuVideoPlayer.isIfCurrentIsFullscreen()) { - StandardGSYVideoPlayer.backFromWindowFull(this); - } - if (orientationUtils != null) { - orientationUtils.setEnable(true); - } - } + danmakuVideoPlayer.onConfigurationChanged(this, newConfig, orientationUtils, true, true); } } + private void getDanmu() { + //下载demo然后设置 + OkHttpUtils.get().url(TextUtils.concat("http://xingyuyou.com/Public/app/barragefile/","608","barrage.txt").toString()) + .build() + .execute(new FileCallBack(getApplication().getCacheDir().getAbsolutePath(), "barrage.txt")// + { + @Override + public void onError(Call call, Exception e, int id) { + } + + @Override + public void onResponse(File response, int id) { + if (!isDestory) { + ((DanmakuVideoPlayer) danmakuVideoPlayer.getCurrentPlayer()).setDanmaKuStream(response); + } + + } + + }); + } + private void resolveNormalVideoUI() { //增加title danmakuVideoPlayer.getTitleTextView().setVisibility(View.GONE); - danmakuVideoPlayer.getTitleTextView().setText("测试视频"); danmakuVideoPlayer.getBackButton().setVisibility(View.GONE); } + private GSYVideoPlayer getCurPlay() { + if (danmakuVideoPlayer.getFullWindowPlayer() != null) { + return danmakuVideoPlayer.getFullWindowPlayer(); + } + return danmakuVideoPlayer; + } + } \ No newline at end of file diff --git a/app/src/main/java/com/example/gsyvideoplayer/DetailADPlayer.java b/app/src/main/java/com/example/gsyvideoplayer/DetailADPlayer.java new file mode 100644 index 000000000..444e5be22 --- /dev/null +++ b/app/src/main/java/com/example/gsyvideoplayer/DetailADPlayer.java @@ -0,0 +1,128 @@ +package com.example.gsyvideoplayer; + +import android.os.Bundle; +import androidx.core.widget.NestedScrollView; +import android.view.View; +import android.widget.ImageView; +import android.widget.RelativeLayout; + + +import com.shuyu.gsyvideoplayer.GSYBaseActivityDetail; +import com.shuyu.gsyvideoplayer.builder.GSYVideoOptionBuilder; +import com.shuyu.gsyvideoplayer.listener.LockClickListener; +import com.shuyu.gsyvideoplayer.video.GSYSampleADVideoPlayer; +import com.shuyu.gsyvideoplayer.video.ListGSYVideoPlayer; +import com.shuyu.gsyvideoplayer.video.base.GSYVideoPlayer; + +import java.util.ArrayList; + +import butterknife.BindView; +import butterknife.ButterKnife; + + +public class DetailADPlayer extends GSYBaseActivityDetail { + + + @BindView(R.id.post_detail_nested_scroll) + NestedScrollView postDetailNestedScroll; + @BindView(R.id.ad_player) + GSYSampleADVideoPlayer detailPlayer; + @BindView(R.id.activity_detail_player) + RelativeLayout activityDetailPlayer; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_detail_ad_player); + ButterKnife.bind(this); + + //普通模式 + initVideo(); + + ArrayList urls = new ArrayList<>(); + //广告1 + urls.add(new GSYSampleADVideoPlayer.GSYADVideoModel("http://7xjmzj.com1.z0.glb.clouddn.com/20171026175005_JObCxCE2.mp4", + "", GSYSampleADVideoPlayer.GSYADVideoModel.TYPE_AD)); + //正式内容1 + urls.add(new GSYSampleADVideoPlayer.GSYADVideoModel("http://9890.vod.myqcloud.com/9890_4e292f9a3dd011e6b4078980237cc3d3.f20.mp4", + "正文1标题", GSYSampleADVideoPlayer.GSYADVideoModel.TYPE_NORMAL)); + //广告2 + urls.add(new GSYSampleADVideoPlayer.GSYADVideoModel("http://7xjmzj.com1.z0.glb.clouddn.com/20171026175005_JObCxCE2.mp4", + "", GSYSampleADVideoPlayer.GSYADVideoModel.TYPE_AD, true)); + //正式内容2 + urls.add(new GSYSampleADVideoPlayer.GSYADVideoModel("http://9890.vod.myqcloud.com/9890_4e292f9a3dd011e6b4078980237cc3d3.f30.mp4", + "正文2标题", GSYSampleADVideoPlayer.GSYADVideoModel.TYPE_NORMAL)); + + detailPlayer.setAdUp(urls, true, 0); + + //增加封面 + ImageView imageView = new ImageView(this); + imageView.setScaleType(ImageView.ScaleType.CENTER_CROP); + imageView.setImageResource(R.mipmap.xxx1); + detailPlayer.setThumbImageView(imageView); + + resolveNormalVideoUI(); + + detailPlayer.setIsTouchWiget(true); + //关闭自动旋转 + detailPlayer.setRotateViewAuto(false); + detailPlayer.setLockLand(false); + detailPlayer.setShowFullAnimation(false); + detailPlayer.setNeedLockFull(true); + + detailPlayer.setVideoAllCallBack(this); + + detailPlayer.setLockClickListener(new LockClickListener() { + @Override + public void onClick(View view, boolean lock) { + if (orientationUtils != null) { + //配合下方的onConfigurationChanged + orientationUtils.setEnable(!lock); + } + } + }); + + } + + @Override + public ListGSYVideoPlayer getGSYVideoPlayer() { + return detailPlayer; + } + + @Override + public GSYVideoOptionBuilder getGSYVideoOptionBuilder() { + //不需要builder的 + return null; + } + + @Override + public void clickForFullScreen() { + + } + + /** + * 是否启动旋转横屏,true表示启动 + * + * @return true + */ + @Override + public boolean getDetailOrientationRotateAuto() { + return true; + } + + @Override + public void onEnterFullscreen(String url, Object... objects) { + super.onEnterFullscreen(url, objects); + //隐藏调全屏对象的返回按键 + GSYVideoPlayer gsyVideoPlayer = (GSYVideoPlayer) objects[1]; + gsyVideoPlayer.getBackButton().setVisibility(View.GONE); + } + + + private void resolveNormalVideoUI() { + //增加title + detailPlayer.getTitleTextView().setVisibility(View.VISIBLE); + detailPlayer.getBackButton().setVisibility(View.VISIBLE); + } +} + diff --git a/app/src/main/java/com/example/gsyvideoplayer/DetailADPlayer2.java b/app/src/main/java/com/example/gsyvideoplayer/DetailADPlayer2.java new file mode 100644 index 000000000..757f9dea8 --- /dev/null +++ b/app/src/main/java/com/example/gsyvideoplayer/DetailADPlayer2.java @@ -0,0 +1,154 @@ +package com.example.gsyvideoplayer; + +import android.os.Bundle; +import android.view.View; +import android.widget.ImageView; + +import com.shuyu.gsyvideoplayer.GSYBaseADActivityDetail; +import com.shuyu.gsyvideoplayer.builder.GSYVideoOptionBuilder; +import com.shuyu.gsyvideoplayer.listener.GSYVideoProgressListener; +import com.shuyu.gsyvideoplayer.listener.LockClickListener; +import com.shuyu.gsyvideoplayer.video.NormalGSYVideoPlayer; +import com.shuyu.gsyvideoplayer.video.GSYADVideoPlayer; +import com.shuyu.gsyvideoplayer.video.base.GSYVideoPlayer; + +/** + * 带广告播放,支持中间插入广告模式 + */ +public class DetailADPlayer2 extends GSYBaseADActivityDetail { + + private NormalGSYVideoPlayer detailPlayer; + + private GSYADVideoPlayer adPlayer; + + private String urlAd = "http://7xjmzj.com1.z0.glb.clouddn.com/20171026175005_JObCxCE2.mp4"; + + private String urlAd2 = "http://7xjmzj.com1.z0.glb.clouddn.com/20171026175005_JObCxCE2.mp4"; + + private String url = "http://9890.vod.myqcloud.com/9890_4e292f9a3dd011e6b4078980237cc3d3.f20.mp4"; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_detail_ad_player2); + + detailPlayer = (NormalGSYVideoPlayer) findViewById(R.id.detail_player); + adPlayer = (GSYADVideoPlayer) findViewById(R.id.ad_player); + + //普通模式 + resolveNormalVideoUI(); + + initVideoBuilderMode(); + + detailPlayer.setLockClickListener(new LockClickListener() { + @Override + public void onClick(View view, boolean lock) { + if (orientationUtils != null) { + //配合下方的onConfigurationChanged + orientationUtils.setEnable(!lock); + } + } + }); + detailPlayer.setStartAfterPrepared(false); + detailPlayer.setReleaseWhenLossAudio(false); + + detailPlayer.setGSYVideoProgressListener(new GSYVideoProgressListener() { + private int preSecond = 0; + @Override + public void onProgress(int progress, int secProgress, int currentPosition, int duration) { + //在5秒的时候弹出中间广告 + int currentSecond = currentPosition / 1000; + if (currentSecond == 5 && currentSecond != preSecond) { + detailPlayer.getCurrentPlayer().onVideoPause(); + getGSYADVideoOptionBuilder().setUrl(urlAd2).build(adPlayer); + startAdPlay(); + } + preSecond = currentSecond; + } + }); + + } + + @Override + public NormalGSYVideoPlayer getGSYVideoPlayer() { + return detailPlayer; + } + + @Override + public GSYADVideoPlayer getGSYADVideoPlayer() { + return adPlayer; + } + + @Override + public GSYVideoOptionBuilder getGSYVideoOptionBuilder() { + //不需要builder的 + ImageView imageView = new ImageView(this); + imageView.setScaleType(ImageView.ScaleType.CENTER_CROP); + imageView.setImageResource(R.mipmap.xxx1); + return getCommonBuilder() + .setUrl(url) + .setThumbImageView(imageView); + } + + @Override + public GSYVideoOptionBuilder getGSYADVideoOptionBuilder() { + return getCommonBuilder() + .setUrl(urlAd); + } + + @Override + public void clickForFullScreen() { + + } + + /** + * 需要片头广告 + * 如果返回 false ,setStartAfterPrepared 需要设置为 ture + */ + @Override + public boolean isNeedAdOnStart() { + return true; + } + + /** + * 是否启动旋转横屏,true表示启动 + * + * @return true + */ + @Override + public boolean getDetailOrientationRotateAuto() { + return true; + } + + @Override + public void onEnterFullscreen(String url, Object... objects) { + super.onEnterFullscreen(url, objects); + //隐藏调全屏对象的返回按键 + GSYVideoPlayer gsyVideoPlayer = (GSYVideoPlayer) objects[1]; + gsyVideoPlayer.getBackButton().setVisibility(View.GONE); + } + + /** + * 公用的视频配置 + */ + private GSYVideoOptionBuilder getCommonBuilder() { + return new GSYVideoOptionBuilder() + .setCacheWithPlay(true) + .setVideoTitle(" ") + .setFullHideActionBar(true) + .setFullHideStatusBar(true) + .setIsTouchWiget(true) + .setRotateViewAuto(false) + .setLockLand(false) + .setShowFullAnimation(false)//打开动画 + .setNeedLockFull(true) + .setSeekRatio(1); + } + + private void resolveNormalVideoUI() { + //增加title + detailPlayer.getTitleTextView().setVisibility(View.VISIBLE); + detailPlayer.getBackButton().setVisibility(View.VISIBLE); + } +} + diff --git a/app/src/main/java/com/example/gsyvideoplayer/DetailControlActivity.java b/app/src/main/java/com/example/gsyvideoplayer/DetailControlActivity.java new file mode 100644 index 000000000..680dbd77c --- /dev/null +++ b/app/src/main/java/com/example/gsyvideoplayer/DetailControlActivity.java @@ -0,0 +1,401 @@ +package com.example.gsyvideoplayer; + +import android.Manifest; +import android.app.AlertDialog; +import android.content.DialogInterface; +import android.content.res.Configuration; +import android.graphics.Bitmap; +import android.os.Bundle; +import androidx.annotation.NonNull; +import androidx.core.widget.NestedScrollView; +import android.view.View; +import android.widget.Button; +import android.widget.ImageView; +import android.widget.RelativeLayout; +import android.widget.Toast; + +import com.bumptech.glide.Glide; +import com.bumptech.glide.request.RequestOptions; +import com.example.gsyvideoplayer.utils.CommonUtil; +import com.example.gsyvideoplayer.utils.JumpUtils; +import com.example.gsyvideoplayer.video.SampleControlVideo; +import com.shuyu.gsyvideoplayer.GSYBaseActivityDetail; +import com.shuyu.gsyvideoplayer.builder.GSYVideoOptionBuilder; +import com.shuyu.gsyvideoplayer.listener.GSYVideoGifSaveListener; +import com.shuyu.gsyvideoplayer.listener.GSYVideoShotListener; +import com.shuyu.gsyvideoplayer.listener.LockClickListener; +import com.shuyu.gsyvideoplayer.utils.Debuger; +import com.shuyu.gsyvideoplayer.utils.FileUtils; +import com.shuyu.gsyvideoplayer.utils.GifCreateHelper; +import com.shuyu.gsyvideoplayer.video.StandardGSYVideoPlayer; + +import java.io.File; +import java.io.FileNotFoundException; + +import butterknife.BindView; +import butterknife.ButterKnife; +import permissions.dispatcher.NeedsPermission; +import permissions.dispatcher.OnNeverAskAgain; +import permissions.dispatcher.OnPermissionDenied; +import permissions.dispatcher.OnShowRationale; +import permissions.dispatcher.PermissionRequest; +import permissions.dispatcher.RuntimePermissions; + +/** + * sampleVideo支持全屏与非全屏切换的清晰度,旋转,镜像等功能. + * Activity可以继承GSYBaseActivityDetail实现详情模式的页面 + * 或者参考DetailPlayer、DetailListPlayer实现 + *

+ * Created by guoshuyu on 2017/6/18. + */ +@RuntimePermissions +public class DetailControlActivity extends GSYBaseActivityDetail { + + @BindView(R.id.post_detail_nested_scroll) + NestedScrollView postDetailNestedScroll; + + @BindView(R.id.detail_player) + SampleControlVideo detailPlayer; + + @BindView(R.id.activity_detail_player) + RelativeLayout activityDetailPlayer; + + @BindView(R.id.change_speed) + Button changeSpeed; + + + @BindView(R.id.jump) + Button jump; + + @BindView(R.id.shot) + Button shot; + + @BindView(R.id.start_gif) + Button startGif; + + @BindView(R.id.stop_gif) + Button stopGif; + + @BindView(R.id.loadingView) + View loadingView; + + private String url = "http://9890.vod.myqcloud.com/9890_4e292f9a3dd011e6b4078980237cc3d3.f20.mp4"; + //private String url = "http://livecdn1.news.cn/Live_MajorEvent01Phone/manifest.m3u8"; + //private String url = "https://ruigongkao.oss-cn-shenzhen.aliyuncs.com/transcode/video/source/video/8908d124aa839d0d3fa9593855ef5957.m3u8"; + //private String url2 = "http://ruigongkao.oss-cn-shenzhen.aliyuncs.com/transcode/video/source/video/3aca1a0db8db9418dcbc765848c8903e.m3u8"; + + + private GifCreateHelper mGifCreateHelper; + + private float speed = 1; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_detail_control); + ButterKnife.bind(this); + + resolveNormalVideoUI(); + + initVideoBuilderMode(); + + initGifHelper(); + + detailPlayer.setLockClickListener(new LockClickListener() { + @Override + public void onClick(View view, boolean lock) { + //if (orientationUtils != null) { + //配合下方的onConfigurationChanged + //orientationUtils.setEnable(!lock); + //} + } + }); + + changeSpeed.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + resolveTypeUI(); + } + }); + + /*VideoOptionModel videoOptionModel = + new VideoOptionModel(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "dns_cache_clear", 1); + List list = new ArrayList<>(); + list.add(videoOptionModel); + videoOptionModel = + new VideoOptionModel(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "rtsp_transport", "tcp"); + list.add(videoOptionModel); + GSYVideoManager.instance().setOptionModelList(list);*/ + + jump.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + JumpUtils.gotoControl(DetailControlActivity.this); + //startActivity(new Intent(DetailControlActivity.this, MainActivity.class)); + } + }); + + shot.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + DetailControlActivityPermissionsDispatcher.shotImageWithPermissionCheck(DetailControlActivity.this, v); + } + }); + + + startGif.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + DetailControlActivityPermissionsDispatcher.startGifWithPermissionCheck(DetailControlActivity.this); + } + }); + + stopGif.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + stopGif(); + } + }); + + loadingView.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + //do nothing + } + }); + } + + @Override + public StandardGSYVideoPlayer getGSYVideoPlayer() { + return detailPlayer; + } + + @Override + public GSYVideoOptionBuilder getGSYVideoOptionBuilder() { + //内置封面可参考SampleCoverVideo + ImageView imageView = new ImageView(this); + loadCover(imageView, url); + return new GSYVideoOptionBuilder() + .setThumbImageView(imageView) + .setUrl(url) + .setCacheWithPlay(true) + .setVideoTitle(" ") + .setIsTouchWiget(true) + .setRotateViewAuto(false) + .setLockLand(false) + .setShowFullAnimation(true)//打开动画 + .setNeedLockFull(true) + .setSeekRatio(1); + } + + @Override + public void clickForFullScreen() { + + } + + @Override + protected void onDestroy() { + super.onDestroy(); + mGifCreateHelper.cancelTask(); + } + + + /*******************************竖屏全屏开始************************************/ + + @Override + public void initVideo() { + super.initVideo(); + //重载后实现点击,不横屏 + if (getGSYVideoPlayer().getFullscreenButton() != null) { + getGSYVideoPlayer().getFullscreenButton().setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + //第一个true是否需要隐藏actionbar,第二个true是否需要隐藏statusbar + getGSYVideoPlayer().startWindowFullscreen(DetailControlActivity.this, true, true); + } + }); + } + } + + + /** + * 是否启动旋转横屏,true表示启动 + * + * @return true + */ + @Override + public boolean getDetailOrientationRotateAuto() { + return false; + } + + //重载后关闭重力旋转 + @Override + public void onConfigurationChanged(Configuration newConfig) { + super.onConfigurationChanged(newConfig); + orientationUtils.setEnable(false); + } + + //重载后不做任何事情,实现竖屏全屏 + @Override + public void onQuitFullscreen(String url, Object... objects) { + super.onQuitFullscreen(url, objects); + } + + /*******************************竖屏全屏结束************************************/ + + private void initGifHelper() { + mGifCreateHelper = new GifCreateHelper(detailPlayer, new GSYVideoGifSaveListener() { + @Override + public void result(boolean success, File file) { + detailPlayer.post(new Runnable() { + @Override + public void run() { + loadingView.setVisibility(View.GONE); + Toast.makeText(detailPlayer.getContext(), "创建成功", Toast.LENGTH_LONG).show(); + } + }); + } + + @Override + public void process(int curPosition, int total) { + Debuger.printfError(" current " + curPosition + " total " + total); + } + }); + } + + + /** + * 开始gif截图 + */ + @NeedsPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) + void startGif() { + //开始缓存各个帧 + mGifCreateHelper.startGif(new File(FileUtils.getPath())); + + } + + /** + * 生成gif + */ + void stopGif() { + loadingView.setVisibility(View.VISIBLE); + mGifCreateHelper.stopGif(new File(FileUtils.getPath(), "GSY-Z-" + System.currentTimeMillis() + ".gif")); + } + + @OnShowRationale(Manifest.permission.WRITE_EXTERNAL_STORAGE) + void showRationaleForCamera(final PermissionRequest request) { + new AlertDialog.Builder(this) + .setMessage("快给我权限") + .setPositiveButton("允许", new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + request.proceed(); + } + }) + .setNegativeButton("拒绝", new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + request.cancel(); + } + }) + .show(); + } + + @OnPermissionDenied(Manifest.permission.WRITE_EXTERNAL_STORAGE) + void showDeniedForCamera() { + Toast.makeText(this, "没有权限啊", Toast.LENGTH_SHORT).show(); + } + + @OnNeverAskAgain(Manifest.permission.CAMERA) + void showNeverAskForCamera() { + Toast.makeText(this, "再次授权", Toast.LENGTH_SHORT).show(); + } + + /** + * 视频截图 + * 这里没有做读写本地sd卡的权限处理,记得实际使用要加上 + */ + @NeedsPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) + void shotImage(final View v) { + //获取截图 + detailPlayer.taskShotPic(new GSYVideoShotListener() { + @Override + public void getBitmap(Bitmap bitmap) { + if (bitmap != null) { + try { + CommonUtil.saveBitmap(bitmap); + } catch (FileNotFoundException e) { + showToast("save fail "); + e.printStackTrace(); + return; + } + showToast("save success "); + } else { + showToast("get bitmap fail "); + } + } + }); + + } + + + private void loadCover(ImageView imageView, String url) { + + imageView.setScaleType(ImageView.ScaleType.CENTER_CROP); + imageView.setImageResource(R.mipmap.xxx1); + + Glide.with(this.getApplicationContext()) + .setDefaultRequestOptions( + new RequestOptions() + .frame(3000000) + .centerCrop() + .error(R.mipmap.xxx2) + .placeholder(R.mipmap.xxx1)) + .load(url) + .into(imageView); + } + + private void resolveNormalVideoUI() { + //增加title + detailPlayer.getTitleTextView().setVisibility(View.GONE); + detailPlayer.getBackButton().setVisibility(View.GONE); + } + + /** + * 显示比例 + * 注意,GSYVideoType.setShowType是全局静态生效,除非重启APP。 + */ + private void resolveTypeUI() { + if (speed == 1) { + speed = 1.5f; + } else if (speed == 1.5f) { + speed = 2f; + } else if (speed == 2) { + speed = 0.5f; + } else if (speed == 0.5f) { + speed = 0.25f; + } else if (speed == 0.25f) { + speed = 1; + } + changeSpeed.setText("播放速度:" + speed); + detailPlayer.setSpeedPlaying(speed, true); + } + + + private void showToast(final String tip) { + detailPlayer.post(new Runnable() { + @Override + public void run() { + Toast.makeText(DetailControlActivity.this, tip, Toast.LENGTH_LONG).show(); + } + }); + } + + @Override + public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { + super.onRequestPermissionsResult(requestCode, permissions, grantResults); + // NOTE: delegate the permission handling to generated method + DetailControlActivityPermissionsDispatcher.onRequestPermissionsResult(this, requestCode, grantResults); + } + +} diff --git a/app/src/main/java/com/example/gsyvideoplayer/DetailDownloadPlayer.java b/app/src/main/java/com/example/gsyvideoplayer/DetailDownloadPlayer.java new file mode 100644 index 000000000..f8cb91729 --- /dev/null +++ b/app/src/main/java/com/example/gsyvideoplayer/DetailDownloadPlayer.java @@ -0,0 +1,293 @@ +package com.example.gsyvideoplayer; + +import android.content.res.Configuration; +import android.os.Bundle; +import android.view.View; +import android.widget.Button; +import android.widget.ImageView; +import android.widget.RelativeLayout; + +import androidx.appcompat.app.AppCompatActivity; +import androidx.core.widget.NestedScrollView; + +import com.danikula.videocache.HttpProxyCacheServer; +import com.example.gsyvideoplayer.utils.MemoryCallBack; +import com.example.gsyvideoplayer.video.LandLayoutVideo; +import com.google.android.exoplayer2.SeekParameters; +import com.shuyu.gsyvideoplayer.GSYVideoManager; +import com.shuyu.gsyvideoplayer.builder.GSYVideoOptionBuilder; +import com.shuyu.gsyvideoplayer.cache.ProxyCacheManager; +import com.shuyu.gsyvideoplayer.listener.GSYSampleCallBack; +import com.shuyu.gsyvideoplayer.listener.GSYVideoProgressListener; +import com.shuyu.gsyvideoplayer.listener.LockClickListener; +import com.shuyu.gsyvideoplayer.utils.Debuger; +import com.shuyu.gsyvideoplayer.utils.OrientationUtils; +import com.shuyu.gsyvideoplayer.video.base.GSYVideoPlayer; +import com.zhy.http.okhttp.OkHttpUtils; +import com.zhy.http.okhttp.request.RequestCall; + +import java.io.File; +import java.util.HashMap; +import java.util.Map; + +import butterknife.BindView; +import butterknife.ButterKnife; +import okhttp3.Call; +import tv.danmaku.ijk.media.exo2.Exo2PlayerManager; + + +public class DetailDownloadPlayer extends AppCompatActivity { + + @BindView(R.id.post_detail_nested_scroll) + NestedScrollView postDetailNestedScroll; + + @BindView(R.id.detail_player) + LandLayoutVideo detailPlayer; + + + @BindView(R.id.start_down) + Button startDownLoad; + + @BindView(R.id.stop_down) + Button stopDownLoad; + + + @BindView(R.id.activity_detail_player) + RelativeLayout activityDetailPlayer; + + private boolean isPlay; + private boolean isPause; + + private OrientationUtils orientationUtils; + + private HttpProxyCacheServer proxyCacheServer; + private RequestCall requestCall; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_detail_download_player); + ButterKnife.bind(this); + + String url = getUrl(); + + //增加封面 + ImageView imageView = new ImageView(this); + imageView.setScaleType(ImageView.ScaleType.CENTER_CROP); + imageView.setImageResource(R.mipmap.xxx1); + + resolveNormalVideoUI(); + + //外部辅助的旋转,帮助全屏 + orientationUtils = new OrientationUtils(this, detailPlayer); + //初始化不打开外部的旋转 + orientationUtils.setEnable(false); + + Map header = new HashMap<>(); + header.put("ee", "33"); + header.put("allowCrossProtocolRedirects", "true"); + GSYVideoOptionBuilder gsyVideoOption = new GSYVideoOptionBuilder(); + gsyVideoOption.setThumbImageView(imageView) + .setIsTouchWiget(true) + .setRotateViewAuto(false) + .setLockLand(false) + .setAutoFullWithSize(false) + .setShowFullAnimation(false) + .setNeedLockFull(true) + .setUrl(url) + .setMapHeadData(header) + .setCacheWithPlay(true) + .setVideoTitle("测试视频") + .setVideoAllCallBack(new GSYSampleCallBack() { + @Override + public void onPrepared(String url, Object... objects) { + Debuger.printfError("***** onPrepared **** " + objects[0]); + Debuger.printfError("***** onPrepared **** " + objects[1]); + super.onPrepared(url, objects); + //开始播放了才能旋转和全屏 + orientationUtils.setEnable(true); + isPlay = true; + + //设置 seek 的临近帧。 + if (detailPlayer.getGSYVideoManager().getPlayer() instanceof Exo2PlayerManager) { + ((Exo2PlayerManager) detailPlayer.getGSYVideoManager().getPlayer()).setSeekParameter(SeekParameters.NEXT_SYNC); + Debuger.printfError("***** setSeekParameter **** "); + } + } + + @Override + public void onEnterFullscreen(String url, Object... objects) { + super.onEnterFullscreen(url, objects); + Debuger.printfError("***** onEnterFullscreen **** " + objects[0]);//title + Debuger.printfError("***** onEnterFullscreen **** " + objects[1]);//当前全屏player + } + + @Override + public void onAutoComplete(String url, Object... objects) { + super.onAutoComplete(url, objects); + } + + @Override + public void onClickStartError(String url, Object... objects) { + super.onClickStartError(url, objects); + } + + @Override + public void onQuitFullscreen(String url, Object... objects) { + super.onQuitFullscreen(url, objects); + Debuger.printfError("***** onQuitFullscreen **** " + objects[0]);//title + Debuger.printfError("***** onQuitFullscreen **** " + objects[1]);//当前非全屏player + if (orientationUtils != null) { + orientationUtils.backToProtVideo(); + } + } + }) + .setLockClickListener(new LockClickListener() { + @Override + public void onClick(View view, boolean lock) { + if (orientationUtils != null) { + //配合下方的onConfigurationChanged + orientationUtils.setEnable(!lock); + } + } + }) + .setGSYVideoProgressListener(new GSYVideoProgressListener() { + @Override + public void onProgress(int progress, int secProgress, int currentPosition, int duration) { + Debuger.printfLog(" progress " + progress + " secProgress " + secProgress + " currentPosition " + currentPosition + " duration " + duration); + } + }) + .build(detailPlayer); + + detailPlayer.getFullscreenButton().setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + //直接横屏 + orientationUtils.resolveByClick(); + + //第一个true是否需要隐藏actionbar,第二个true是否需要隐藏statusbar + detailPlayer.startWindowFullscreen(DetailDownloadPlayer.this, true, true); + } + }); + + + startDownLoad.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + String url = proxyCacheServer.getProxyUrl(getUrl()); + startDownload(url); + + } + }); + + stopDownLoad.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + stopDownload(); + } + }); + + proxyCacheServer = ProxyCacheManager.instance().newProxy(getApplicationContext()); + + + } + + @Override + public void onBackPressed() { + + if (orientationUtils != null) { + orientationUtils.backToProtVideo(); + } + + if (GSYVideoManager.backFromWindowFull(this)) { + return; + } + super.onBackPressed(); + } + + + @Override + protected void onPause() { + getCurPlay().onVideoPause(); + super.onPause(); + isPause = true; + } + + @Override + protected void onResume() { + getCurPlay().onVideoResume(false); + super.onResume(); + isPause = false; + } + + @Override + protected void onDestroy() { + super.onDestroy(); + if (isPlay) { + getCurPlay().release(); + } + stopDownload(); + if (orientationUtils != null) + orientationUtils.releaseListener(); + } + + + @Override + public void onConfigurationChanged(Configuration newConfig) { + super.onConfigurationChanged(newConfig); + //如果旋转了就全屏 + if (isPlay && !isPause) { + detailPlayer.onConfigurationChanged(this, newConfig, orientationUtils, true, true); + } + } + + + private void resolveNormalVideoUI() { + //增加title + detailPlayer.getTitleTextView().setVisibility(View.GONE); + detailPlayer.getBackButton().setVisibility(View.GONE); + } + + private GSYVideoPlayer getCurPlay() { + if (detailPlayer.getFullWindowPlayer() != null) { + return detailPlayer.getFullWindowPlayer(); + } + return detailPlayer; + } + + + private String getUrl() { + + String url = "http://9890.vod.myqcloud.com/9890_4e292f9a3dd011e6b4078980237cc3d3.f20.mp4"; + + return url; + } + + private void startDownload(String url) { + Debuger.printfError("###### url " + url); + //下载demo然后设置 + requestCall = OkHttpUtils.get().url(url) + .build(); + requestCall.execute(new MemoryCallBack() { + @Override + public void onError(Call call, Exception e, int id) { + } + + @Override + public void onResponse(Boolean response, int id) { + stopDownload(); + } + + }); + } + + private void stopDownload() { + if (requestCall != null) { + requestCall.cancel(); + requestCall = null; + } + if (proxyCacheServer != null) { + proxyCacheServer.shutdown(); + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/gsyvideoplayer/DetailFilterActivity.java b/app/src/main/java/com/example/gsyvideoplayer/DetailFilterActivity.java new file mode 100644 index 000000000..2ff7f18cf --- /dev/null +++ b/app/src/main/java/com/example/gsyvideoplayer/DetailFilterActivity.java @@ -0,0 +1,622 @@ +package com.example.gsyvideoplayer; + +import android.Manifest; +import android.app.AlertDialog; +import android.content.DialogInterface; +import android.graphics.Bitmap; +import android.graphics.Color; +import android.opengl.Matrix; +import android.os.Bundle; +import androidx.annotation.NonNull; +import androidx.core.widget.NestedScrollView; +import android.util.TypedValue; +import android.view.View; +import android.widget.Button; +import android.widget.ImageView; +import android.widget.RelativeLayout; +import android.widget.Toast; + +import com.bumptech.glide.Glide; +import com.bumptech.glide.request.RequestOptions; +import com.example.gsyvideoplayer.effect.BitmapIconEffect; +import com.example.gsyvideoplayer.effect.GSYVideoGLViewCustomRender; +import com.example.gsyvideoplayer.effect.PixelationEffect; +import com.example.gsyvideoplayer.utils.CommonUtil; +import com.example.gsyvideoplayer.video.SampleControlVideo; +import com.shuyu.gsyvideoplayer.GSYBaseActivityDetail; +import com.shuyu.gsyvideoplayer.listener.GSYVideoGifSaveListener; +import com.shuyu.gsyvideoplayer.render.view.GSYVideoGLView; +import com.shuyu.gsyvideoplayer.builder.GSYVideoOptionBuilder; +import com.shuyu.gsyvideoplayer.render.effect.AutoFixEffect; +import com.shuyu.gsyvideoplayer.render.effect.BarrelBlurEffect; +import com.shuyu.gsyvideoplayer.render.effect.BlackAndWhiteEffect; +import com.shuyu.gsyvideoplayer.render.effect.BrightnessEffect; +import com.shuyu.gsyvideoplayer.render.effect.ContrastEffect; +import com.shuyu.gsyvideoplayer.render.effect.CrossProcessEffect; +import com.shuyu.gsyvideoplayer.render.effect.DocumentaryEffect; +import com.shuyu.gsyvideoplayer.render.effect.DuotoneEffect; +import com.shuyu.gsyvideoplayer.render.effect.FillLightEffect; +import com.shuyu.gsyvideoplayer.render.effect.GammaEffect; +import com.shuyu.gsyvideoplayer.render.effect.GaussianBlurEffect; +import com.shuyu.gsyvideoplayer.render.effect.GrainEffect; +import com.shuyu.gsyvideoplayer.render.effect.HueEffect; +import com.shuyu.gsyvideoplayer.render.effect.InvertColorsEffect; +import com.shuyu.gsyvideoplayer.render.effect.LamoishEffect; +import com.shuyu.gsyvideoplayer.render.effect.NoEffect; +import com.shuyu.gsyvideoplayer.render.effect.OverlayEffect; +import com.shuyu.gsyvideoplayer.render.effect.PosterizeEffect; +import com.shuyu.gsyvideoplayer.render.effect.SampleBlurEffect; +import com.shuyu.gsyvideoplayer.render.effect.SaturationEffect; +import com.shuyu.gsyvideoplayer.render.effect.SepiaEffect; +import com.shuyu.gsyvideoplayer.render.effect.SharpnessEffect; +import com.shuyu.gsyvideoplayer.render.effect.TemperatureEffect; +import com.shuyu.gsyvideoplayer.render.effect.TintEffect; +import com.shuyu.gsyvideoplayer.render.effect.VignetteEffect; +import com.shuyu.gsyvideoplayer.listener.GSYVideoShotListener; +import com.shuyu.gsyvideoplayer.listener.LockClickListener; +import com.shuyu.gsyvideoplayer.utils.Debuger; +import com.shuyu.gsyvideoplayer.utils.FileUtils; +import com.shuyu.gsyvideoplayer.utils.GSYVideoType; +import com.shuyu.gsyvideoplayer.utils.GifCreateHelper; +import com.shuyu.gsyvideoplayer.video.StandardGSYVideoPlayer; + +import java.io.File; +import java.io.FileNotFoundException; +import java.util.Timer; +import java.util.TimerTask; + +import butterknife.BindView; +import butterknife.ButterKnife; +import permissions.dispatcher.NeedsPermission; +import permissions.dispatcher.OnNeverAskAgain; +import permissions.dispatcher.OnPermissionDenied; +import permissions.dispatcher.OnShowRationale; +import permissions.dispatcher.PermissionRequest; +import permissions.dispatcher.RuntimePermissions; + +/** + * 滤镜 + * Activity可以继承GSYBaseActivityDetail实现详情模式的页面 + * 或者参考DetailPlayer、DetailListPlayer实现 + * Created by guoshuyu on 2017/6/18. + */ +@RuntimePermissions +public class DetailFilterActivity extends GSYBaseActivityDetail { + + @BindView(R.id.post_detail_nested_scroll) + NestedScrollView postDetailNestedScroll; + + @BindView(R.id.detail_player) + SampleControlVideo detailPlayer; + + @BindView(R.id.activity_detail_player) + RelativeLayout activityDetailPlayer; + + @BindView(R.id.change_filter) + Button changeFilter; + + + @BindView(R.id.jump) + Button jump; + + @BindView(R.id.change_anima) + Button anima; + + @BindView(R.id.start_gif) + Button startGif; + + @BindView(R.id.stop_gif) + Button stopGif; + + @BindView(R.id.loadingView) + View loadingView; + + private int type = 0; + + private int backupRendType; + + private float deep = 0.8f; + + private String url = "https://res.exexm.com/cw_145225549855002"; + //private String url = "http://9890.vod.myqcloud.com/9890_4e292f9a3dd011e6b4078980237cc3d3.f20.mp4"; + + private Timer timer = new Timer(); + + private TaskLocal mTimerTask; + + private TaskLocal2 mTimerTask2; + + private GSYVideoGLViewCustomRender mGSYVideoGLViewCustomRender; + + private BitmapIconEffect mCustomBitmapIconEffect; + + private int percentage = 1; + + private int percentageType = 1; + + private boolean moveBitmap = false; + + private GifCreateHelper mGifCreateHelper; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_detail_filter); + ButterKnife.bind(this); + + backupRendType = GSYVideoType.getRenderType(); + + //设置为GL播放模式,才能支持滤镜,注意此设置是全局的 + GSYVideoType.setRenderType(GSYVideoType.GLSURFACE); + + resolveNormalVideoUI(); + + initVideoBuilderMode(); + + initGifHelper(); + + detailPlayer.setLockClickListener(new LockClickListener() { + @Override + public void onClick(View view, boolean lock) { + if (orientationUtils != null) { + //配合下方的onConfigurationChanged + orientationUtils.setEnable(!lock); + } + } + }); + + + //自定义render需要在播放器开始播放之前,播放过程中不允许切换render + + //水印图效果 + /*Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher); + mGSYVideoGLViewCustomRender = new GSYVideoGLViewCustomRender(); + mCustomBitmapIconEffect = new BitmapIconEffect(bitmap, dp2px(50), dp2px(50), 0.6f); + mGSYVideoGLViewCustomRender.setBitmapEffect(mCustomBitmapIconEffect); + detailPlayer.setCustomGLRenderer(mGSYVideoGLViewCustomRender); + detailPlayer.setGLRenderMode(GSYVideoGLView.MODE_RENDER_SIZE);*/ + + //多窗口播放效果 + //detailPlayer.setEffectFilter(new GammaEffect(0.8f)); + //detailPlayer.setCustomGLRenderer(new GSYVideoGLViewCustomRender2()); + + //图片穿孔透视播放 + //detailPlayer.setCustomGLRenderer(new GSYVideoGLViewCustomRender3()); + + //高斯拉伸视频铺满背景,替换黑色,前台正常比例播放 + //detailPlayer.setEffectFilter(new GaussianBlurEffect(6.0f, GaussianBlurEffect.TYPEXY)); + //detailPlayer.setCustomGLRenderer(new GSYVideoGLViewCustomRender4()); + //detailPlayer.setGLRenderMode(GSYVideoGLView.MODE_RENDER_SIZE); + + changeFilter.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + resolveTypeUI(); + } + }); + + //使用GL播放的话,用这种方式可以解决退出全屏黑屏的问题 + detailPlayer.setBackFromFullScreenListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + DetailFilterActivity.this.onBackPressed(); + } + }); + + + jump.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + //shotImage(v); + //JumpUtils.gotoControl(DetailFilterActivity.this); + //startActivity(new Intent(DetailControlActivity.this, MainActivity.class)); + + DetailFilterActivityPermissionsDispatcher.shotImageWithPermissionCheck(DetailFilterActivity.this, v); + } + }); + + anima.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + //画面旋转 + cancelTask(); + mTimerTask = new TaskLocal(); + timer.schedule(mTimerTask, 0, 50); + percentageType++; + if (percentageType > 4) { + percentageType = 1; + } + //水印图动起来 + //cancelTask2(); + //mTimerTask2 = new TaskLocal2(); + //timer.schedule(mTimerTask2, 0, 400); + + //moveBitmap = !moveBitmap; + } + }); + + + startGif.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + DetailFilterActivityPermissionsDispatcher.startGifWithPermissionCheck(DetailFilterActivity.this); + } + }); + + stopGif.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + stopGif(); + } + }); + + loadingView.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + //do nothing + } + }); + } + + @Override + public StandardGSYVideoPlayer getGSYVideoPlayer() { + return detailPlayer; + } + + @Override + public GSYVideoOptionBuilder getGSYVideoOptionBuilder() { + //内置封面可参考SampleCoverVideo + ImageView imageView = new ImageView(this); + loadCover(imageView, url); + return new GSYVideoOptionBuilder() + .setThumbImageView(imageView) + .setUrl(url) + .setCacheWithPlay(true) + .setVideoTitle(" ") + .setIsTouchWiget(true) + .setRotateViewAuto(false) + .setLockLand(false) + .setShowFullAnimation(false) + .setNeedLockFull(true) + .setSeekRatio(1); + } + + @Override + public void clickForFullScreen() { + + } + + /** + * 是否启动旋转横屏,true表示启动 + * @return true + */ + @Override + public boolean getDetailOrientationRotateAuto() { + return true; + } + + @Override + protected void onDestroy() { + super.onDestroy(); + //恢复到原本的绘制模式 + GSYVideoType.setRenderType(backupRendType); + cancelTask(); + } + + /** + * 视频截图 + */ + @NeedsPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) + void shotImage(final View v) { + //获取截图 + detailPlayer.taskShotPic(new GSYVideoShotListener() { + @Override + public void getBitmap(Bitmap bitmap) { + if (bitmap != null) { + try { + CommonUtil.saveBitmap(bitmap); + } catch (FileNotFoundException e) { + showToast("save fail "); + e.printStackTrace(); + return; + } + showToast("save success "); + } else { + showToast("get bitmap fail "); + } + } + }); + + } + + + + private void initGifHelper() { + mGifCreateHelper = new GifCreateHelper(detailPlayer, new GSYVideoGifSaveListener() { + @Override + public void result(boolean success, File file) { + detailPlayer.post(new Runnable() { + @Override + public void run() { + loadingView.setVisibility(View.GONE); + Toast.makeText(detailPlayer.getContext(), "创建成功", Toast.LENGTH_LONG).show(); + } + }); + } + + @Override + public void process(int curPosition, int total) { + Debuger.printfError(" current " + curPosition + " total " + total); + } + }); + } + + + /** + * 开始gif截图 + */ + @NeedsPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) + void startGif() { + //开始缓存各个帧 + mGifCreateHelper.startGif(new File(FileUtils.getPath())); + + } + + /** + * 生成gif + */ + private void stopGif() { + loadingView.setVisibility(View.VISIBLE); + mGifCreateHelper.stopGif(new File(FileUtils.getPath(), "GSY-Z-" + System.currentTimeMillis() + ".gif")); + } + + /** + * 加载第三秒的帧数作为封面 + */ + private void loadCover(ImageView imageView, String url) { + + imageView.setScaleType(ImageView.ScaleType.CENTER_CROP); + imageView.setImageResource(R.mipmap.xxx1); + + Glide.with(this.getApplicationContext()) + .setDefaultRequestOptions( + new RequestOptions() + .frame(3000000) + .centerCrop() + .error(R.mipmap.xxx2) + .placeholder(R.mipmap.xxx1)) + .load(url) + .into(imageView); + } + + private void resolveNormalVideoUI() { + //增加title + detailPlayer.getTitleTextView().setVisibility(View.GONE); + detailPlayer.getBackButton().setVisibility(View.GONE); + } + + /** + * 切换滤镜 + */ + private void resolveTypeUI() { + GSYVideoGLView.ShaderInterface effect = new NoEffect(); + switch (type) { + case 0: + effect = new AutoFixEffect(deep); + break; + case 1: + effect = new PixelationEffect(); + break; + case 2: + effect = new BlackAndWhiteEffect(); + break; + case 3: + effect = new ContrastEffect(deep); + break; + case 4: + effect = new CrossProcessEffect(); + break; + case 5: + effect = new DocumentaryEffect(); + break; + case 6: + effect = new DuotoneEffect(Color.BLUE, Color.YELLOW); + break; + case 7: + effect = new FillLightEffect(deep); + break; + case 8: + effect = new GammaEffect(deep); + break; + case 9: + effect = new GrainEffect(deep); + break; + case 10: + effect = new GrainEffect(deep); + break; + case 11: + effect = new HueEffect(deep); + break; + case 12: + effect = new InvertColorsEffect(); + break; + case 13: + effect = new LamoishEffect(); + break; + case 14: + effect = new PosterizeEffect(); + break; + case 15: + effect = new BarrelBlurEffect(); + break; + case 16: + effect = new SaturationEffect(deep); + break; + case 17: + effect = new SepiaEffect(); + break; + case 18: + effect = new SharpnessEffect(deep); + break; + case 19: + effect = new TemperatureEffect(deep); + break; + case 20: + effect = new TintEffect(Color.GREEN); + break; + case 21: + effect = new VignetteEffect(deep); + break; + case 22: + effect = new NoEffect(); + break; + case 23: + effect = new OverlayEffect(); + break; + case 24: + effect = new SampleBlurEffect(4.0f); + break; + case 25: + effect = new GaussianBlurEffect(6.0f, GaussianBlurEffect.TYPEXY); + break; + case 26: + effect = new BrightnessEffect(deep); + break; + } + detailPlayer.setEffectFilter(effect); + type++; + if (type > 25) { + type = 0; + } + } + + + private void cancelTask2() { + if (mTimerTask2 != null) { + mTimerTask2.cancel(); + mTimerTask2 = null; + } + } + + private void cancelTask() { + if (mTimerTask != null) { + mTimerTask.cancel(); + mTimerTask = null; + } + } + + + /** + * 水印图动起来,播放前开始会崩溃哟 + */ + private class TaskLocal2 extends TimerTask { + @Override + public void run() { + float[] transform = new float[16]; + //旋转到正常角度 + Matrix.setRotateM(transform, 0, 180f, 0.0f, 0, 1.0f); + //调整大小比例 + Matrix.scaleM(transform, 0, mCustomBitmapIconEffect.getScaleW(), mCustomBitmapIconEffect.getScaleH(), 1); + if (moveBitmap) { + //调整位置 + Matrix.translateM(transform, 0, mCustomBitmapIconEffect.getPositionX(), mCustomBitmapIconEffect.getPositionY(), 0f); + } else { + float maxX = mCustomBitmapIconEffect.getMaxPositionX(); + float minX = mCustomBitmapIconEffect.getMinPositionX(); + float maxY = mCustomBitmapIconEffect.getMaxPositionY(); + float minY = mCustomBitmapIconEffect.getMinPositionY(); + float x = (float) Math.random() * (maxX - minX) + minX; + float y = (float) Math.random() * (maxY - minY) + minY; + //调整位置 + Matrix.translateM(transform, 0, x, y, 0f); + mGSYVideoGLViewCustomRender.setCurrentMVPMatrix(transform); + } + } + } + + + /** + * 设置GLRender的VertexShader的transformMatrix + * 注意,这是android.opengl.Matrix + */ + private class TaskLocal extends TimerTask { + @Override + public void run() { + float[] transform = new float[16]; + switch (percentageType) { + case 1: + //给予x变化 + Matrix.setRotateM(transform, 0, 360 * percentage / 100, 1.0f, 0, 0.0f); + break; + case 2: + //给予y变化 + Matrix.setRotateM(transform, 0, 360 * percentage / 100, 0.0f, 1.0f, 0.0f); + break; + case 3: + //给予z变化 + Matrix.setRotateM(transform, 0, 360 * percentage / 100, 0.0f, 0, 1.0f); + break; + case 4: + Matrix.setRotateM(transform, 0, 360, 0.0f, 0, 1.0f); + break; + } + //设置渲染transform + detailPlayer.setMatrixGL(transform); + percentage++; + if (percentage > 100) { + percentage = 1; + } + } + } + + private int dp2px(int dp) { + return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, + getResources().getDisplayMetrics()); + } + + private void showToast(final String tip) { + detailPlayer.post(new Runnable() { + @Override + public void run() { + Toast.makeText(DetailFilterActivity.this, tip, Toast.LENGTH_LONG).show(); + } + }); + } + + + @OnShowRationale(Manifest.permission.WRITE_EXTERNAL_STORAGE) + void showRationaleForCamera(final PermissionRequest request) { + new AlertDialog.Builder(this) + .setMessage("快给我权限") + .setPositiveButton("允许", new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + request.proceed(); + } + }) + .setNegativeButton("拒绝", new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + request.cancel(); + } + }) + .show(); + } + + @OnPermissionDenied(Manifest.permission.WRITE_EXTERNAL_STORAGE) + void showDeniedForCamera() { + Toast.makeText(this, "没有权限啊", Toast.LENGTH_SHORT).show(); + } + + @OnNeverAskAgain(Manifest.permission.CAMERA) + void showNeverAskForCamera() { + Toast.makeText(this, "再次授权", Toast.LENGTH_SHORT).show(); + } + + @Override + public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { + super.onRequestPermissionsResult(requestCode, permissions, grantResults); + // NOTE: delegate the permission handling to generated method + DetailFilterActivityPermissionsDispatcher.onRequestPermissionsResult(this, requestCode, grantResults); + } +} diff --git a/app/src/main/java/com/example/gsyvideoplayer/DetailListPlayer.java b/app/src/main/java/com/example/gsyvideoplayer/DetailListPlayer.java index 93a44c76e..fc793019a 100644 --- a/app/src/main/java/com/example/gsyvideoplayer/DetailListPlayer.java +++ b/app/src/main/java/com/example/gsyvideoplayer/DetailListPlayer.java @@ -1,23 +1,17 @@ package com.example.gsyvideoplayer; -import android.content.pm.ActivityInfo; -import android.content.res.Configuration; import android.os.Bundle; -import android.support.annotation.Nullable; -import android.support.v4.widget.NestedScrollView; -import android.support.v7.app.AppCompatActivity; +import androidx.core.widget.NestedScrollView; import android.view.View; import android.widget.ImageView; import android.widget.RelativeLayout; -import com.example.gsyvideoplayer.listener.SampleListener; -import com.shuyu.gsyvideoplayer.GSYPreViewManager; -import com.shuyu.gsyvideoplayer.GSYVideoPlayer; +import com.shuyu.gsyvideoplayer.GSYBaseActivityDetail; +import com.shuyu.gsyvideoplayer.builder.GSYVideoOptionBuilder; +import com.shuyu.gsyvideoplayer.video.base.GSYVideoPlayer; import com.shuyu.gsyvideoplayer.listener.LockClickListener; import com.shuyu.gsyvideoplayer.model.GSYVideoModel; -import com.shuyu.gsyvideoplayer.utils.OrientationUtils; import com.shuyu.gsyvideoplayer.video.ListGSYVideoPlayer; -import com.shuyu.gsyvideoplayer.video.StandardGSYVideoPlayer; import java.util.ArrayList; import java.util.List; @@ -26,10 +20,12 @@ import butterknife.ButterKnife; /** + * Activity可以继承GSYBaseActivityDetail实现详情模式的页面 + * 或者参考DetailPlayer、DetailListPlayer实现 * Created by shuyu on 2016/12/20. */ -public class DetailListPlayer extends AppCompatActivity { +public class DetailListPlayer extends GSYBaseActivityDetail { @BindView(R.id.post_detail_nested_scroll) @@ -38,11 +34,8 @@ public class DetailListPlayer extends AppCompatActivity { ListGSYVideoPlayer detailPlayer; @BindView(R.id.activity_detail_player) RelativeLayout activityDetailPlayer; - - private boolean isPlay; - private boolean isPause; - - private OrientationUtils orientationUtils; + @BindView(R.id.next) + View next; @Override protected void onCreate(Bundle savedInstanceState) { @@ -50,11 +43,14 @@ protected void onCreate(Bundle savedInstanceState) { setContentView(R.layout.activity_deatil_list_player); ButterKnife.bind(this); + //普通模式 + initVideo(); + //String url = "http://baobab.wd jcdn.com/14564977406580.mp4"; List urls = new ArrayList<>(); - urls.add(new GSYVideoModel("http://baobab.wdjcdn.com/14564977406580.mp4", "标题1")); + urls.add(new GSYVideoModel("http://7xjmzj.com1.z0.glb.clouddn.com/20171026175005_JObCxCE2.mp4", "标题1")); urls.add(new GSYVideoModel("http://9890.vod.myqcloud.com/9890_4e292f9a3dd011e6b4078980237cc3d3.f20.mp4", "标题2")); - urls.add(new GSYVideoModel("http://baobab.wdjcdn.com/14564977406580.mp4", "标题3")); + urls.add(new GSYVideoModel("https://res.exexm.com/cw_145225549855002", "标题3")); urls.add(new GSYVideoModel("http://9890.vod.myqcloud.com/9890_4e292f9a3dd011e6b4078980237cc3d3.f20.mp4", "标题4")); detailPlayer.setUp(urls, true, 0); @@ -66,56 +62,15 @@ protected void onCreate(Bundle savedInstanceState) { resolveNormalVideoUI(); - //外部辅助的旋转,帮助全屏 - orientationUtils = new OrientationUtils(this, detailPlayer); - //初始化不打开外部的旋转 - orientationUtils.setEnable(false); - detailPlayer.setIsTouchWiget(true); //关闭自动旋转 detailPlayer.setRotateViewAuto(false); detailPlayer.setLockLand(false); detailPlayer.setShowFullAnimation(false); - detailPlayer.setNeedLockFull(true); - - detailPlayer.getFullscreenButton().setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - //直接横屏 - orientationUtils.resolveByClick(); - - //第一个true是否需要隐藏actionbar,第二个true是否需要隐藏statusbar - detailPlayer.startWindowFullscreen(DetailListPlayer.this, true, true); - } - }); + //detailPlayer.setNeedLockFull(true); + detailPlayer.setAutoFullWithSize(true); - detailPlayer.setStandardVideoAllCallBack(new SampleListener() { - @Override - public void onPrepared(String url, Object... objects) { - super.onPrepared(url, objects); - //开始播放了才能旋转和全屏 - orientationUtils.setEnable(true); - isPlay = true; - } - - @Override - public void onAutoComplete(String url, Object... objects) { - super.onAutoComplete(url, objects); - } - - @Override - public void onClickStartError(String url, Object... objects) { - super.onClickStartError(url, objects); - } - - @Override - public void onQuitFullscreen(String url, Object... objects) { - super.onQuitFullscreen(url, objects); - if (orientationUtils != null) { - orientationUtils.backToProtVideo(); - } - } - }); + detailPlayer.setVideoAllCallBack(this); detailPlayer.setLockClickListener(new LockClickListener() { @Override @@ -127,69 +82,62 @@ public void onClick(View view, boolean lock) { } }); + next.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + ((ListGSYVideoPlayer) detailPlayer.getCurrentPlayer()).playNext(); + } + }); + } @Override - public void onBackPressed() { - - if (orientationUtils != null) { - orientationUtils.backToProtVideo(); - } - - if (StandardGSYVideoPlayer.backFromWindowFull(this)) { - return; - } - super.onBackPressed(); + public ListGSYVideoPlayer getGSYVideoPlayer() { + return detailPlayer; } - @Override - protected void onPause() { - super.onPause(); - isPause = true; + public GSYVideoOptionBuilder getGSYVideoOptionBuilder() { + //不需要builder的 + return null; } @Override - protected void onResume() { - super.onResume(); - isPause = false; + public void clickForFullScreen() { + } + /** + * 是否启动旋转横屏,true表示启动 + * + * @return true + */ @Override - protected void onDestroy() { - super.onDestroy(); - GSYVideoPlayer.releaseAllVideos(); - if (orientationUtils != null) - orientationUtils.releaseListener(); + public boolean getDetailOrientationRotateAuto() { + return true; } @Override - public void onConfigurationChanged(Configuration newConfig) { - super.onConfigurationChanged(newConfig); - //如果旋转了就全屏 - if (isPlay && !isPause) { - if (newConfig.orientation == ActivityInfo.SCREEN_ORIENTATION_USER) { - if (!detailPlayer.isIfCurrentIsFullscreen()) { - detailPlayer.startWindowFullscreen(DetailListPlayer.this, true, true); - } - } else { - //新版本isIfCurrentIsFullscreen的标志位内部提前设置了,所以不会和手动点击冲突 - if (detailPlayer.isIfCurrentIsFullscreen()) { - StandardGSYVideoPlayer.backFromWindowFull(this); - } - if (orientationUtils != null) { - orientationUtils.setEnable(true); - } - } - } + public void onEnterFullscreen(String url, Object... objects) { + super.onEnterFullscreen(url, objects); + //隐藏调全屏对象的返回按键 + GSYVideoPlayer gsyVideoPlayer = (GSYVideoPlayer) objects[1]; + gsyVideoPlayer.getBackButton().setVisibility(View.GONE); } private void resolveNormalVideoUI() { //增加title - detailPlayer.getTitleTextView().setVisibility(View.GONE); - detailPlayer.getTitleTextView().setText("测试视频"); - detailPlayer.getBackButton().setVisibility(View.GONE); + detailPlayer.getTitleTextView().setVisibility(View.VISIBLE); + detailPlayer.getBackButton().setVisibility(View.VISIBLE); } + private GSYVideoPlayer getCurPlay() { + if (detailPlayer.getFullWindowPlayer() != null) { + return detailPlayer.getFullWindowPlayer(); + } + return detailPlayer; + } + + } diff --git a/app/src/main/java/com/example/gsyvideoplayer/DetailMoreTypeActivity.java b/app/src/main/java/com/example/gsyvideoplayer/DetailMoreTypeActivity.java index 7002e1165..4226a7e8c 100644 --- a/app/src/main/java/com/example/gsyvideoplayer/DetailMoreTypeActivity.java +++ b/app/src/main/java/com/example/gsyvideoplayer/DetailMoreTypeActivity.java @@ -1,23 +1,26 @@ package com.example.gsyvideoplayer; -import android.content.pm.ActivityInfo; import android.content.res.Configuration; +import android.media.MediaMetadataRetriever; import android.os.Bundle; -import android.support.v4.widget.NestedScrollView; -import android.support.v7.app.AppCompatActivity; +import androidx.core.widget.NestedScrollView; +import androidx.appcompat.app.AppCompatActivity; import android.view.View; import android.widget.ImageView; import android.widget.RelativeLayout; -import com.example.gsyvideoplayer.listener.SampleListener; +import com.bumptech.glide.Glide; +import com.bumptech.glide.request.RequestOptions; import com.example.gsyvideoplayer.model.SwitchVideoModel; import com.example.gsyvideoplayer.video.SampleVideo; -import com.shuyu.gsyvideoplayer.GSYVideoPlayer; +import com.shuyu.gsyvideoplayer.GSYVideoManager; +import com.shuyu.gsyvideoplayer.listener.GSYSampleCallBack; +import com.shuyu.gsyvideoplayer.video.base.GSYVideoPlayer; import com.shuyu.gsyvideoplayer.listener.LockClickListener; import com.shuyu.gsyvideoplayer.utils.OrientationUtils; -import com.shuyu.gsyvideoplayer.video.StandardGSYVideoPlayer; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; import butterknife.BindView; @@ -25,10 +28,10 @@ /** * Created by guoshuyu on 2017/6/18. - * ampleVideo支持全屏与非全屏切换的清晰度,旋转,镜像等功能. + * sampleVideo支持全屏与非全屏切换的清晰度,旋转,镜像等功能. */ -public class DetailMoreTypeActivity extends AppCompatActivity{ +public class DetailMoreTypeActivity extends AppCompatActivity { @BindView(R.id.post_detail_nested_scroll) NestedScrollView postDetailNestedScroll; @@ -42,9 +45,14 @@ public class DetailMoreTypeActivity extends AppCompatActivity{ private boolean isPlay; private boolean isPause; + private boolean isRelease; private OrientationUtils orientationUtils; + private MediaMetadataRetriever mCoverMedia; + + private ImageView coverImageView; + @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -52,7 +60,7 @@ protected void onCreate(Bundle savedInstanceState) { ButterKnife.bind(this); String source1 = "http://9890.vod.myqcloud.com/9890_4e292f9a3dd011e6b4078980237cc3d3.f20.mp4"; - //String source1 = "http://baobab.wdjcdn.com/14564977406580.mp4"; + //String source1 = "https://res.exexm.com/cw_145225549855002"; String name = "普通"; SwitchVideoModel switchVideoModel = new SwitchVideoModel(name, source1); @@ -67,10 +75,10 @@ protected void onCreate(Bundle savedInstanceState) { detailPlayer.setUp(list, true, ""); //增加封面 - ImageView imageView = new ImageView(this); - imageView.setScaleType(ImageView.ScaleType.CENTER_CROP); - imageView.setImageResource(R.mipmap.xxx1); - detailPlayer.setThumbImageView(imageView); + coverImageView = new ImageView(this); + coverImageView.setScaleType(ImageView.ScaleType.CENTER_CROP); + //coverImageView.setImageResource(R.mipmap.xxx1); + detailPlayer.setThumbImageView(coverImageView); resolveNormalVideoUI(); @@ -84,27 +92,31 @@ protected void onCreate(Bundle savedInstanceState) { //关闭自动旋转 detailPlayer.setRotateViewAuto(false); detailPlayer.setLockLand(false); - detailPlayer.setShowFullAnimation(false); + + //打开 实现竖屏全屏动画 + detailPlayer.setShowFullAnimation(true); + detailPlayer.setNeedLockFull(true); detailPlayer.setSeekRatio(1); //detailPlayer.setOpenPreView(false); detailPlayer.getFullscreenButton().setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { - //直接横屏 - orientationUtils.resolveByClick(); + //屏蔽,实现竖屏全屏 + //orientationUtils.resolveByClick(); //第一个true是否需要隐藏actionbar,第二个true是否需要隐藏statusbar detailPlayer.startWindowFullscreen(DetailMoreTypeActivity.this, true, true); } }); - detailPlayer.setStandardVideoAllCallBack(new SampleListener() { + detailPlayer.setVideoAllCallBack(new GSYSampleCallBack() { @Override public void onPrepared(String url, Object... objects) { super.onPrepared(url, objects); //开始播放了才能旋转和全屏 - orientationUtils.setEnable(true); + //orientationUtils.setEnable(true); + orientationUtils.setEnable(false); isPlay = true; } @@ -121,22 +133,25 @@ public void onClickStartError(String url, Object... objects) { @Override public void onQuitFullscreen(String url, Object... objects) { super.onQuitFullscreen(url, objects); - if (orientationUtils != null) { - orientationUtils.backToProtVideo(); - } + //屏蔽,实现竖屏全屏 + //if (orientationUtils != null) { + //orientationUtils.backToProtVideo(); + //} } }); detailPlayer.setLockClickListener(new LockClickListener() { @Override public void onClick(View view, boolean lock) { - if (orientationUtils != null) { + //屏蔽,实现竖屏全屏 + //if (orientationUtils != null) { //配合下方的onConfigurationChanged - orientationUtils.setEnable(!lock); - } + //orientationUtils.setEnable(!lock); + //} } }); + loadFirstFrameCover(source1); } @Override @@ -146,7 +161,7 @@ public void onBackPressed() { orientationUtils.backToProtVideo(); } - if (StandardGSYVideoPlayer.backFromWindowFull(this)) { + if (GSYVideoManager.backFromWindowFull(this)) { return; } super.onBackPressed(); @@ -155,12 +170,14 @@ public void onBackPressed() { @Override protected void onPause() { + getCurPlay().onVideoPause(); super.onPause(); isPause = true; } @Override protected void onResume() { + getCurPlay().onVideoResume(); super.onResume(); isPause = false; } @@ -168,10 +185,17 @@ protected void onResume() { @Override protected void onDestroy() { super.onDestroy(); - GSYVideoPlayer.releaseAllVideos(); + isRelease = true; + if (isPlay) { + getCurPlay().release(); + } //GSYPreViewManager.instance().releaseMediaPlayer(); if (orientationUtils != null) orientationUtils.releaseListener(); + if (mCoverMedia != null) { + mCoverMedia.release(); + mCoverMedia = null; + } } @Override @@ -179,27 +203,79 @@ public void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); //如果旋转了就全屏 if (isPlay && !isPause) { - if (newConfig.orientation == ActivityInfo.SCREEN_ORIENTATION_USER) { - if (!detailPlayer.isIfCurrentIsFullscreen()) { - detailPlayer.startWindowFullscreen(DetailMoreTypeActivity.this, true, true); - } - } else { - //新版本isIfCurrentIsFullscreen的标志位内部提前设置了,所以不会和手动点击冲突 - if (detailPlayer.isIfCurrentIsFullscreen()) { - StandardGSYVideoPlayer.backFromWindowFull(this); - } - if (orientationUtils != null) { - orientationUtils.setEnable(true); - } - } + detailPlayer.onConfigurationChanged(this, newConfig, orientationUtils, true, true); } + //竖屏全屏 + orientationUtils.setEnable(false); + } + + + + private GSYVideoPlayer getCurPlay() { + if (detailPlayer.getFullWindowPlayer() != null) { + return detailPlayer.getFullWindowPlayer(); + } + return detailPlayer; } private void resolveNormalVideoUI() { //增加title detailPlayer.getTitleTextView().setVisibility(View.GONE); - detailPlayer.getTitleTextView().setText("测试视频"); detailPlayer.getBackButton().setVisibility(View.GONE); } + + + /** + * 这里只是演示,并不建议直接这么做 + * MediaMetadataRetriever最好做一个独立的管理器 + * 使用缓存 + * 注意资源的开销和异步等 + * + * @param url + */ + public void loadFirstFrameCover(String url) { + + //原始方法 + /*final MediaMetadataRetriever mediaMetadataRetriever = getMediaMetadataRetriever(url); + //获取帧图片 + if (getMediaMetadataRetriever(url) != null) { + new Thread(new Runnable() { + @Override + public void run() { + final Bitmap bitmap = mediaMetadataRetriever + .getFrameAtTime(1000, MediaMetadataRetriever.OPTION_CLOSEST); + runOnUiThread(new Runnable() { + @Override + public void run() { + if (bitmap != null && !isRelease) { + Debuger.printfLog("time " + System.currentTimeMillis()); + //显示 + coverImageView.setImageBitmap(bitmap); + } + } + }); + } + }).start(); + }*/ + + //可以参考Glide,内部也是封装了MediaMetadataRetriever + Glide.with(this.getApplicationContext()) + .setDefaultRequestOptions( + new RequestOptions() + .frame(1000000) + .centerCrop() + .error(R.mipmap.xxx2) + .placeholder(R.mipmap.xxx1)) + .load(url) + .into(coverImageView); + } + + public MediaMetadataRetriever getMediaMetadataRetriever(String url) { + if (mCoverMedia == null) { + mCoverMedia = new MediaMetadataRetriever(); + } + mCoverMedia.setDataSource(url, new HashMap()); + return mCoverMedia; + } } diff --git a/app/src/main/java/com/example/gsyvideoplayer/DetailNormalActivityPlayer.java b/app/src/main/java/com/example/gsyvideoplayer/DetailNormalActivityPlayer.java new file mode 100644 index 000000000..9bcb00a69 --- /dev/null +++ b/app/src/main/java/com/example/gsyvideoplayer/DetailNormalActivityPlayer.java @@ -0,0 +1,228 @@ +package com.example.gsyvideoplayer; + +import android.app.Activity; +import android.content.res.Configuration; +import android.os.Bundle; +import android.view.View; +import android.widget.ImageView; +import android.widget.RelativeLayout; + +import androidx.appcompat.app.AppCompatActivity; +import androidx.core.widget.NestedScrollView; + +import com.example.gsyvideoplayer.video.LandLayoutVideo; +import com.google.android.exoplayer2.SeekParameters; +import com.shuyu.gsyvideoplayer.GSYVideoManager; +import com.shuyu.gsyvideoplayer.builder.GSYVideoOptionBuilder; +import com.shuyu.gsyvideoplayer.listener.GSYSampleCallBack; +import com.shuyu.gsyvideoplayer.listener.GSYVideoProgressListener; +import com.shuyu.gsyvideoplayer.listener.LockClickListener; +import com.shuyu.gsyvideoplayer.utils.Debuger; +import com.shuyu.gsyvideoplayer.utils.OrientationUtils; +import com.shuyu.gsyvideoplayer.video.base.GSYVideoPlayer; + +import java.util.HashMap; +import java.util.Map; + +import butterknife.BindView; +import butterknife.ButterKnife; +import tv.danmaku.ijk.media.exo2.Exo2PlayerManager; + + +public class DetailNormalActivityPlayer extends Activity { + + @BindView(R.id.post_detail_nested_scroll) + NestedScrollView postDetailNestedScroll; + + //推荐使用StandardGSYVideoPlayer,功能一致 + //CustomGSYVideoPlayer部分功能处于试验阶段 + @BindView(R.id.detail_player) + LandLayoutVideo detailPlayer; + + @BindView(R.id.activity_detail_player) + RelativeLayout activityDetailPlayer; + + private boolean isPlay; + private boolean isPause; + + private OrientationUtils orientationUtils; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_detail_player); + ButterKnife.bind(this); + + String url = getUrl(); + + //增加封面 + ImageView imageView = new ImageView(this); + imageView.setScaleType(ImageView.ScaleType.CENTER_CROP); + imageView.setImageResource(R.mipmap.xxx1); + + resolveNormalVideoUI(); + + //外部辅助的旋转,帮助全屏 + orientationUtils = new OrientationUtils(this, detailPlayer); + //初始化不打开外部的旋转 + orientationUtils.setEnable(false); + + Map header = new HashMap<>(); + header.put("ee", "33"); + header.put("allowCrossProtocolRedirects", "true"); + GSYVideoOptionBuilder gsyVideoOption = new GSYVideoOptionBuilder(); + gsyVideoOption.setThumbImageView(imageView) + .setIsTouchWiget(true) + .setRotateViewAuto(false) + .setLockLand(false) + .setAutoFullWithSize(false) + .setShowFullAnimation(false) + .setNeedLockFull(true) + .setUrl(url) + .setMapHeadData(header) + .setCacheWithPlay(false) + .setVideoTitle("测试视频") + .setVideoAllCallBack(new GSYSampleCallBack() { + @Override + public void onPrepared(String url, Object... objects) { + Debuger.printfError("***** onPrepared **** " + objects[0]); + Debuger.printfError("***** onPrepared **** " + objects[1]); + super.onPrepared(url, objects); + //开始播放了才能旋转和全屏 + orientationUtils.setEnable(true); + isPlay = true; + + //设置 seek 的临近帧。 + if(detailPlayer.getGSYVideoManager().getPlayer() instanceof Exo2PlayerManager) { + ((Exo2PlayerManager) detailPlayer.getGSYVideoManager().getPlayer()).setSeekParameter(SeekParameters.NEXT_SYNC); + Debuger.printfError("***** setSeekParameter **** "); + } + } + + @Override + public void onEnterFullscreen(String url, Object... objects) { + super.onEnterFullscreen(url, objects); + Debuger.printfError("***** onEnterFullscreen **** " + objects[0]);//title + Debuger.printfError("***** onEnterFullscreen **** " + objects[1]);//当前全屏player + } + + @Override + public void onAutoComplete(String url, Object... objects) { + super.onAutoComplete(url, objects); + } + + @Override + public void onClickStartError(String url, Object... objects) { + super.onClickStartError(url, objects); + } + + @Override + public void onQuitFullscreen(String url, Object... objects) { + super.onQuitFullscreen(url, objects); + Debuger.printfError("***** onQuitFullscreen **** " + objects[0]);//title + Debuger.printfError("***** onQuitFullscreen **** " + objects[1]);//当前非全屏player + if (orientationUtils != null) { + orientationUtils.backToProtVideo(); + } + } + }) + .setLockClickListener(new LockClickListener() { + @Override + public void onClick(View view, boolean lock) { + if (orientationUtils != null) { + //配合下方的onConfigurationChanged + orientationUtils.setEnable(!lock); + } + } + }) + .setGSYVideoProgressListener(new GSYVideoProgressListener() { + @Override + public void onProgress(int progress, int secProgress, int currentPosition, int duration) { + Debuger.printfLog(" progress " + progress + " secProgress " + secProgress + " currentPosition " + currentPosition + " duration " + duration); + } + }) + .build(detailPlayer); + + detailPlayer.getFullscreenButton().setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + //直接横屏 + orientationUtils.resolveByClick(); + + //第一个true是否需要隐藏actionbar,第二个true是否需要隐藏statusbar + detailPlayer.startWindowFullscreen(DetailNormalActivityPlayer.this, false, true); + } + }); + } + + @Override + public void onBackPressed() { + + if (orientationUtils != null) { + orientationUtils.backToProtVideo(); + } + + if (GSYVideoManager.backFromWindowFull(this)) { + return; + } + super.onBackPressed(); + } + + + @Override + protected void onPause() { + getCurPlay().onVideoPause(); + super.onPause(); + isPause = true; + } + + @Override + protected void onResume() { + getCurPlay().onVideoResume(false); + super.onResume(); + isPause = false; + } + + @Override + protected void onDestroy() { + super.onDestroy(); + if (isPlay) { + getCurPlay().release(); + } + //GSYPreViewManager.instance().releaseMediaPlayer(); + if (orientationUtils != null) + orientationUtils.releaseListener(); + } + + + + @Override + public void onConfigurationChanged(Configuration newConfig) { + super.onConfigurationChanged(newConfig); + //如果旋转了就全屏 + if (isPlay && !isPause) { + detailPlayer.onConfigurationChanged(this, newConfig, orientationUtils, true, true); + } + } + + + private void resolveNormalVideoUI() { + //增加title + detailPlayer.getTitleTextView().setVisibility(View.GONE); + detailPlayer.getBackButton().setVisibility(View.GONE); + } + + private GSYVideoPlayer getCurPlay() { + if (detailPlayer.getFullWindowPlayer() != null) { + return detailPlayer.getFullWindowPlayer(); + } + return detailPlayer; + } + + + + + private String getUrl() { + return "http://9890.vod.myqcloud.com/9890_4e292f9a3dd011e6b4078980237cc3d3.f20.mp4"; + } +} diff --git a/app/src/main/java/com/example/gsyvideoplayer/DetailPlayer.java b/app/src/main/java/com/example/gsyvideoplayer/DetailPlayer.java index fb0670299..977408ead 100644 --- a/app/src/main/java/com/example/gsyvideoplayer/DetailPlayer.java +++ b/app/src/main/java/com/example/gsyvideoplayer/DetailPlayer.java @@ -1,33 +1,37 @@ package com.example.gsyvideoplayer; -import android.content.pm.ActivityInfo; import android.content.res.Configuration; import android.os.Bundle; -import android.support.v4.widget.NestedScrollView; -import android.support.v7.app.AppCompatActivity; + +import androidx.core.widget.NestedScrollView; +import androidx.appcompat.app.AppCompatActivity; import android.view.View; import android.widget.ImageView; import android.widget.RelativeLayout; -import com.example.gsyvideoplayer.listener.SampleListener; import com.example.gsyvideoplayer.video.LandLayoutVideo; -import com.shuyu.gsyvideoplayer.GSYPreViewManager; +import com.google.android.exoplayer2.SeekParameters; +import com.google.android.exoplayer2.upstream.RawResourceDataSource; import com.shuyu.gsyvideoplayer.GSYVideoManager; -import com.shuyu.gsyvideoplayer.GSYVideoPlayer; +import com.shuyu.gsyvideoplayer.listener.GSYSampleCallBack; +import com.shuyu.gsyvideoplayer.listener.GSYVideoProgressListener; +import com.shuyu.gsyvideoplayer.model.VideoOptionModel; +import com.shuyu.gsyvideoplayer.video.base.GSYVideoPlayer; -import com.shuyu.gsyvideoplayer.builder.GSYVideoBuilder; +import com.shuyu.gsyvideoplayer.builder.GSYVideoOptionBuilder; import com.shuyu.gsyvideoplayer.listener.LockClickListener; -import com.shuyu.gsyvideoplayer.model.VideoOptionModel; import com.shuyu.gsyvideoplayer.utils.Debuger; import com.shuyu.gsyvideoplayer.utils.OrientationUtils; -import com.shuyu.gsyvideoplayer.video.StandardGSYVideoPlayer; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; +import java.util.Map; import butterknife.BindView; import butterknife.ButterKnife; +import tv.danmaku.ijk.media.exo2.Exo2PlayerManager; import tv.danmaku.ijk.media.player.IjkMediaPlayer; @@ -55,27 +59,30 @@ protected void onCreate(Bundle savedInstanceState) { setContentView(R.layout.activity_detail_player); ButterKnife.bind(this); - String url = "http://baobab.wdjcdn.com/14564977406580.mp4"; + String url = getUrl(); - //String url = "http://hcjs2ra2rytd8v8np1q.exp.bcevod.com/mda-hegtjx8n5e8jt9zv/mda-hegtjx8n5e8jt9zv.m3u8"; - //String url = "http://7xse1z.com1.z0.glb.clouddn.com/1491813192"; - //String url = "http://ocgk7i2aj.bkt.clouddn.com/17651ac2-693c-47e9-b2d2-b731571bad37"; - //String url = "http://111.198.24.133:83/yyy_login_server/pic/YB059284/97778276040859/1.mp4"; - //String url = "http://vr.tudou.com/v2proxy/v?sid=95001&id=496378919&st=3&pw="; - //String url = "http://pl-ali.youku.com/playlist/m3u8?type=mp4&ts=1490185963&keyframe=0&vid=XMjYxOTQ1Mzg2MA==&ep=ciadGkiFU8cF4SvajD8bYyuwJiYHXJZ3rHbN%2FrYDAcZuH%2BrC6DPcqJ21TPs%3D&sid=04901859548541247bba8&token=0524&ctype=12&ev=1&oip=976319194"; - //String url = "http://hls.ciguang.tv/hdtv/video.m3u8"; - //String url = "https://res.exexm.com/cw_145225549855002"; //detailPlayer.setUp(url, false, null, "测试视频"); //detailPlayer.setLooping(true); //detailPlayer.setShowPauseCover(false); - /*VideoOptionModel videoOptionModel = - new VideoOptionModel(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "framedrop", 5); - List list = new ArrayList<>(); - list.add(videoOptionModel); - GSYVideoManager.instance().setOptionModelList(list);*/ + + //如果视频帧数太高导致卡画面不同步 + //VideoOptionModel videoOptionModel = new VideoOptionModel(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "framedrop", 30); + //如果视频seek之后从头播放 + //VideoOptionModel videoOptionModel = new VideoOptionModel(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "enable-accurate-seek", 1); + //list list = new ArrayList<>(); + //list.add(videoOptionModel); + //GSYVideoManager.instance().setOptionModelList(list); //GSYVideoManager.instance().setTimeOut(4000, true); +// VideoOptionModel videoOptionModel = +// new VideoOptionModel(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "dns_cache_clear", 1); +// List list = new ArrayList<>(); +// list.add(videoOptionModel); +// VideoOptionModel videoOptionModel2 = new VideoOptionModel(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "dns_cache_timeout", -1); +// list.add(videoOptionModel2); +// GSYVideoManager.instance().setOptionModelList(list); + //增加封面 ImageView imageView = new ImageView(this); imageView.setScaleType(ImageView.ScaleType.CENTER_CROP); @@ -90,18 +97,27 @@ protected void onCreate(Bundle savedInstanceState) { //初始化不打开外部的旋转 orientationUtils.setEnable(false); - GSYVideoBuilder gsyVideoBuilder = new GSYVideoBuilder(); - gsyVideoBuilder.setThumbImageView(imageView) + /**仅仅横屏旋转,不变直*/ + //orientationUtils.setOnlyRotateLand(true); + + Map header = new HashMap<>(); + header.put("ee", "33"); + header.put("allowCrossProtocolRedirects", "true"); + GSYVideoOptionBuilder gsyVideoOption = new GSYVideoOptionBuilder(); + gsyVideoOption.setThumbImageView(imageView) .setIsTouchWiget(true) .setRotateViewAuto(false) + //仅仅横屏旋转,不变直 + //.setOnlyRotateLand(true) .setLockLand(false) + .setAutoFullWithSize(false) .setShowFullAnimation(false) .setNeedLockFull(true) - .setSeekRatio(1) .setUrl(url) + .setMapHeadData(header) .setCacheWithPlay(false) .setVideoTitle("测试视频") - .setStandardVideoAllCallBack(new SampleListener() { + .setVideoAllCallBack(new GSYSampleCallBack() { @Override public void onPrepared(String url, Object... objects) { Debuger.printfError("***** onPrepared **** " + objects[0]); @@ -110,6 +126,12 @@ public void onPrepared(String url, Object... objects) { //开始播放了才能旋转和全屏 orientationUtils.setEnable(true); isPlay = true; + + //设置 seek 的临近帧。 + if(detailPlayer.getGSYVideoManager().getPlayer() instanceof Exo2PlayerManager) { + ((Exo2PlayerManager) detailPlayer.getGSYVideoManager().getPlayer()).setSeekParameter(SeekParameters.NEXT_SYNC); + Debuger.printfError("***** setSeekParameter **** "); + } } @Override @@ -147,7 +169,14 @@ public void onClick(View view, boolean lock) { orientationUtils.setEnable(!lock); } } - }).build(detailPlayer); + }) + .setGSYVideoProgressListener(new GSYVideoProgressListener() { + @Override + public void onProgress(int progress, int secProgress, int currentPosition, int duration) { + Debuger.printfLog(" progress " + progress + " secProgress " + secProgress + " currentPosition " + currentPosition + " duration " + duration); + } + }) + .build(detailPlayer); detailPlayer.getFullscreenButton().setOnClickListener(new View.OnClickListener() { @Override @@ -159,7 +188,6 @@ public void onClick(View v) { detailPlayer.startWindowFullscreen(DetailPlayer.this, true, true); } }); - } @Override @@ -169,7 +197,7 @@ public void onBackPressed() { orientationUtils.backToProtVideo(); } - if (StandardGSYVideoPlayer.backFromWindowFull(this)) { + if (GSYVideoManager.backFromWindowFull(this)) { return; } super.onBackPressed(); @@ -178,12 +206,14 @@ public void onBackPressed() { @Override protected void onPause() { + getCurPlay().onVideoPause(); super.onPause(); isPause = true; } @Override protected void onResume() { + getCurPlay().onVideoResume(false); super.onResume(); isPause = false; } @@ -191,30 +221,22 @@ protected void onResume() { @Override protected void onDestroy() { super.onDestroy(); - GSYVideoPlayer.releaseAllVideos(); + if (isPlay) { + getCurPlay().release(); + } //GSYPreViewManager.instance().releaseMediaPlayer(); if (orientationUtils != null) orientationUtils.releaseListener(); } + + @Override public void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); //如果旋转了就全屏 if (isPlay && !isPause) { - if (newConfig.orientation == ActivityInfo.SCREEN_ORIENTATION_USER) { - if (!detailPlayer.isIfCurrentIsFullscreen()) { - detailPlayer.startWindowFullscreen(DetailPlayer.this, true, true); - } - } else { - //新版本isIfCurrentIsFullscreen的标志位内部提前设置了,所以不会和手动点击冲突 - if (detailPlayer.isIfCurrentIsFullscreen()) { - StandardGSYVideoPlayer.backFromWindowFull(this); - } - if (orientationUtils != null) { - orientationUtils.setEnable(true); - } - } + detailPlayer.onConfigurationChanged(this, newConfig, orientationUtils, true, true); } } @@ -222,8 +244,70 @@ public void onConfigurationChanged(Configuration newConfig) { private void resolveNormalVideoUI() { //增加title detailPlayer.getTitleTextView().setVisibility(View.GONE); - detailPlayer.getTitleTextView().setText("测试视频"); detailPlayer.getBackButton().setVisibility(View.GONE); } + private GSYVideoPlayer getCurPlay() { + if (detailPlayer.getFullWindowPlayer() != null) { + return detailPlayer.getFullWindowPlayer(); + } + return detailPlayer; + } + + + + + private String getUrl() { + + //String url = "android.resource://" + getPackageName() + "/" + R.raw.test; + //注意,用ijk模式播放raw视频,这个必须打开 + //GSYVideoManager.instance().enableRawPlay(getApplicationContext()); + + ///exo raw 支持 + //String url = RawResourceDataSource.buildRawResourceUri(R.raw.test).toString(); + + //断网自动重新链接,url前接上ijkhttphook: + //String url = "ijkhttphook:https://res.exexm.com/cw_145225549855002"; + + //String url = "http://9890.vod.myqcloud.com/9890_4e292f9a3dd011e6b4078980237cc3d3.f20.mp4"; + //String url = "http://9890.vod.myqcloud.com/9890_4e292f9a3dd011e6b4078980237cc3d3.f20.mp4"; + //String url = "http://7xjmzj.com1.z0.glb.clouddn.com/20171026175005_JObCxCE2.mp4"; + //String url = "http://hjq-1257036536.cos.ap-shanghai.myqcloud.com/m3u8/m1/out2.m3u8"; + //String url = "http://223.110.243.138/PLTV/2510088/224/3221227177/index.m3u8"; + //String url = "http://qiniu.carmmi.com/image/132451525666042.mp4"; + //String url = "http://ucp.wn.sunmath.cn/file-upload/gYQJHxK9iNQKJeWyS/V80418-103803.mp4?rc_uid=7sCFCGoaF2iTc9vH9&rc_token=prJK-xGutKmy2LDQO-OZASjob0o1u_s3e5SgMHmgjtn"; + //String url = "http://7xse1z.com1.z0.glb.clouddn.com/1491813192"; + //String url = "file://"+ Environment.getExternalStorageDirectory().getPath() + "Download/132451525666042.mp4"; + + //String url = "http://ipsimg-huabei2.speiyou.cn/010/video/other/20180427/40288b156241ec6301624243bdf7021e/40288b156290270d0162a3e7eb2e0726/1524814477/movie.mp4"; + //String url = "http://ipsimg-huabei2.speiyou.cn/010/video/other/20180424/40288b156290270d0162a3db8cdd033e/40288b156290270d0162a3e8207f074f/e787a64c-f2d0-48fe-896d-246af05f111a.mp4"; + + //String url = "http://video.7k.cn/app_video/20171202/6c8cf3ea/v.m3u8.mp4"; + //String url = "http://devimages.apple.com.edgekey.net/streaming/examples/bipbop_4x3/bipbop_4x3_variant.m3u8"; + String url = "http://9890.vod.myqcloud.com/9890_4e292f9a3dd011e6b4078980237cc3d3.f20.mp4"; + //String url = "https://cdn61.ytbbs.tv/cn/tv/55550/55550-1/play.m3u8?md5=v4sI4lWlo4XojzeAjgBGaQ&expires=1521204012&token=55550"; + //String url = "http://1253492636.vod2.myqcloud.com/2e5fc148vodgzp1253492636/d08af82d4564972819086152830/plHZZoSkje0A.mp4"; + + //String url = "rtsp://ajj:12345678@218.21.217.122:65523/h264/ch40/sub/av_stream"; + //String url = "rtsp://ajj:ajj12345678@218.21.217.122:65522/h264/ch15/sub/av_stream";//String url = "rtsp://cloud.easydarwin.org:554/stream0.sdp"; + //String url = "http://s.swao.cn/o_1c4gm8o1nniu1had13bk1t0l1rq64m.mov"; + //String url = "http://api.ciguang.tv/avideo/play?num=02-041-0491&type=flv&v=1&client=android"; + //String url = "http://video.7k.cn/app_video/20171213/276d8195/v.m3u8.mp4"; + //String url = "http://103.233.191.21/riak/riak-bucket/6469ac502e813a4c1df7c99f364e70c1.mp4"; + //String url = "http://7xjmzj.com1.z0.glb.clouddn.com/20171026175005_JObCxCE2.mp4"; + //String url = "https://media6.smartstudy.com/ae/07/3997/2/dest.m3u8"; + //String url = "http://cdn.tiaobatiaoba.com/Upload/square/2017-11-02/1509585140_1279.mp4"; + + //String url = "http://hcjs2ra2rytd8v8np1q.exp.bcevod.com/mda-hegtjx8n5e8jt9zv/mda-hegtjx8n5e8jt9zv.m3u8"; + //String url = "http://7xse1z.com1.z0.glb.clouddn.com/1491813192"; + //String url = "http://ocgk7i2aj.bkt.clouddn.com/17651ac2-693c-47e9-b2d2-b731571bad37"; + //String url = "http://111.198.24.133:83/yyy_login_server/pic/YB059284/97778276040859/1.mp4"; + //String url = "http://vr.tudou.com/v2proxy/v?sid=95001&id=496378919&st=3&pw="; + //String url = "http://pl-ali.youku.com/playlist/m3u8?type=mp4&ts=1490185963&keyframe=0&vid=XMjYxOTQ1Mzg2MA==&ep=ciadGkiFU8cF4SvajD8bYyuwJiYHXJZ3rHbN%2FrYDAcZuH%2BrC6DPcqJ21TPs%3D&sid=04901859548541247bba8&token=0524&ctype=12&ev=1&oip=976319194"; + //String url = "http://hls.ciguang.tv/hdtv/video.m3u8"; + //String url = "https://res.exexm.com/cw_145225549855002"; + //String url = "http://storage.gzstv.net/uploads/media/huangmeiyan/jr05-09.mp4";//mepg + //String url = "https://zh-files.oss-cn-qingdao.aliyuncs.com/20170808223928mJ1P3n57.mp4";//90度 + return url; + } } diff --git a/app/src/main/java/com/example/gsyvideoplayer/EmptyActivity.java b/app/src/main/java/com/example/gsyvideoplayer/EmptyActivity.java new file mode 100644 index 000000000..8847c7415 --- /dev/null +++ b/app/src/main/java/com/example/gsyvideoplayer/EmptyActivity.java @@ -0,0 +1,28 @@ +package com.example.gsyvideoplayer; + +import android.content.Intent; +import android.os.Bundle; +import androidx.appcompat.app.AppCompatActivity; +import android.widget.Button; + +import butterknife.BindView; +import butterknife.ButterKnife; +import butterknife.OnClick; + +public class EmptyActivity extends AppCompatActivity { + + @BindView(R.id.jump_other) + Button jumpOther; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_empty); + ButterKnife.bind(this); + } + + @OnClick(R.id.jump_other) + public void onViewClicked() { + startActivity(new Intent(this, EmptyActivity.class)); + } +} diff --git a/app/src/main/java/com/example/gsyvideoplayer/FragmentVideoActivity.java b/app/src/main/java/com/example/gsyvideoplayer/FragmentVideoActivity.java index 106d89cb2..7cecb7372 100644 --- a/app/src/main/java/com/example/gsyvideoplayer/FragmentVideoActivity.java +++ b/app/src/main/java/com/example/gsyvideoplayer/FragmentVideoActivity.java @@ -1,8 +1,8 @@ package com.example.gsyvideoplayer; import android.os.Build; -import android.support.v4.app.FragmentTransaction; -import android.support.v7.app.AppCompatActivity; +import androidx.fragment.app.FragmentTransaction; +import androidx.appcompat.app.AppCompatActivity; import android.os.Bundle; import android.transition.Explode; import android.view.Window; diff --git a/app/src/main/java/com/example/gsyvideoplayer/GSYApplication.java b/app/src/main/java/com/example/gsyvideoplayer/GSYApplication.java index 03992995d..4d62c9607 100644 --- a/app/src/main/java/com/example/gsyvideoplayer/GSYApplication.java +++ b/app/src/main/java/com/example/gsyvideoplayer/GSYApplication.java @@ -1,30 +1,91 @@ package com.example.gsyvideoplayer; -import android.app.Application; +import androidx.multidex.MultiDexApplication; +import tv.danmaku.ijk.media.exo2.Exo2PlayerManager; +import tv.danmaku.ijk.media.exo2.IjkExo2MediaPlayer; +import tv.danmaku.ijk.media.player.IMediaPlayer; + +import com.google.android.exoplayer2.DefaultLoadControl; +import com.google.android.exoplayer2.trackselection.DefaultTrackSelector; import com.shuyu.gsyvideoplayer.GSYVideoManager; +import com.shuyu.gsyvideoplayer.model.GSYModel; +import com.shuyu.gsyvideoplayer.player.BasePlayerManager; +import com.shuyu.gsyvideoplayer.player.IPlayerInitSuccessListener; +import com.shuyu.gsyvideoplayer.player.PlayerFactory; +import com.shuyu.gsyvideoplayer.utils.Debuger; import com.shuyu.gsyvideoplayer.utils.GSYVideoType; +import com.squareup.leakcanary.LeakCanary; -//import com.squareup.leakcanary.LeakCanary; +import static com.google.android.exoplayer2.util.Util.inferContentType; /** - * Created by shuyu on 2016/11/11. + Created by shuyu on 2016/11/11. */ -public class GSYApplication extends Application { +public class GSYApplication extends MultiDexApplication { @Override public void onCreate() { super.onCreate(); - //if (LeakCanary.isInAnalyzerProcess(this)) { + if (LeakCanary.isInAnalyzerProcess(this)) { // This process is dedicated to LeakCanary for heap analysis. // You should not init your app in this process. - //return; - //} - //LeakCanary.install(this); + return; + } + LeakCanary.install(this); + //GSYVideoType.enableMediaCodec(); - //GSYVideoManager.instance().setVideoType(this, GSYVideoType.IJKEXOPLAYER); + //GSYVideoType.enableMediaCodecTexture(); + + //PlayerFactory.setPlayManager(Exo2PlayerManager.class);//EXO模式 + //ExoSourceManager.setSkipSSLChain(true); + + + //PlayerFactory.setPlayManager(SystemPlayerManager.class);//系统模式 + //PlayerFactory.setPlayManager(IjkPlayerManager.class);//ijk模式 + + //CacheFactory.setCacheManager(ExoPlayerCacheManager.class);//exo缓存模式,支持m3u8,只支持exo + //CacheFactory.setCacheManager(ProxyCacheManager.class);//代理缓存模式,支持所有模式,不支持m3u8等 + //GSYVideoType.setShowType(GSYVideoType.SCREEN_MATCH_FULL); //GSYVideoType.setShowType(GSYVideoType.SCREEN_TYPE_FULL); + //GSYVideoType.setShowType(GSYVideoType.SCREEN_MATCH_FULL); + + //GSYVideoType.setShowType(GSYVideoType.SCREEN_TYPE_CUSTOM); + //GSYVideoType.setScreenScaleRatio(9.0f/16); + + //GSYVideoType.setRenderType(GSYVideoType.SUFRACE); + //GSYVideoType.setRenderType(GSYVideoType.GLSURFACE); + + //IjkPlayerManager.setLogLevel(IjkMediaPlayer.IJK_LOG_SILENT); + + /*ExoSourceManager.setExoMediaSourceInterceptListener(new ExoMediaSourceInterceptListener() { + @Override + public MediaSource getMediaSource(String dataSource, boolean preview, boolean cacheEnable, boolean isLooping, File cacheDir) { + Uri contentUri = Uri.parse(dataSource); + int contentType = inferContentType(dataSource); + switch (contentType) { + case C.TYPE_HLS: + return new HlsMediaSource.Factory(CustomSourceTag.getDataSourceFactory(GSYApplication.this.getApplicationContext(), preview)).createMediaSource(contentUri); + } + return null; + } + });*/ + + /*GSYVideoManager.instance().setPlayerInitSuccessListener(new IPlayerInitSuccessListener() { + ///播放器初始化成果回调 + @Override + public void onPlayerInitSuccess(IMediaPlayer player, GSYModel model) { + if (player instanceof IjkExo2MediaPlayer) { + ((IjkExo2MediaPlayer) player).setTrackSelector(new DefaultTrackSelector()); + ((IjkExo2MediaPlayer) player).setLoadControl(new DefaultLoadControl()); + } + } + });*/ + + } + + } diff --git a/app/src/main/java/com/example/gsyvideoplayer/InputUrlDetailActivity.java b/app/src/main/java/com/example/gsyvideoplayer/InputUrlDetailActivity.java index 4ab75aa33..f486ca0f3 100644 --- a/app/src/main/java/com/example/gsyvideoplayer/InputUrlDetailActivity.java +++ b/app/src/main/java/com/example/gsyvideoplayer/InputUrlDetailActivity.java @@ -1,29 +1,24 @@ package com.example.gsyvideoplayer; -import android.app.Activity; import android.content.DialogInterface; -import android.content.pm.ActivityInfo; import android.content.res.Configuration; import android.os.Bundle; -import android.support.v4.widget.NestedScrollView; -import android.support.v7.app.AppCompatActivity; +import androidx.core.widget.NestedScrollView; +import androidx.appcompat.app.AppCompatActivity; import android.view.View; import android.widget.Button; import android.widget.ImageView; import android.widget.RelativeLayout; -import com.example.gsyvideoplayer.listener.SampleListener; -import com.example.gsyvideoplayer.model.SwitchVideoModel; +import com.bumptech.glide.Glide; import com.example.gsyvideoplayer.video.LandLayoutVideo; -import com.example.gsyvideoplayer.video.SampleVideo; import com.example.gsyvideoplayer.view.CustomInputDialog; -import com.shuyu.gsyvideoplayer.GSYVideoPlayer; +import com.shuyu.gsyvideoplayer.GSYVideoManager; +import com.shuyu.gsyvideoplayer.builder.GSYVideoOptionBuilder; +import com.shuyu.gsyvideoplayer.listener.GSYSampleCallBack; +import com.shuyu.gsyvideoplayer.video.base.GSYVideoPlayer; import com.shuyu.gsyvideoplayer.listener.LockClickListener; import com.shuyu.gsyvideoplayer.utils.OrientationUtils; -import com.shuyu.gsyvideoplayer.video.StandardGSYVideoPlayer; - -import java.util.ArrayList; -import java.util.List; import butterknife.BindView; import butterknife.ButterKnife; @@ -52,21 +47,20 @@ public class InputUrlDetailActivity extends AppCompatActivity { private OrientationUtils orientationUtils; + private GSYVideoOptionBuilder gsyVideoOptionBuilder; + @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_input_url_detail); ButterKnife.bind(this); - url = "http://baobab.wdjcdn.com/14564977406580.mp4"; - detailPlayer.setUp(url, cache, null, "测试视频"); - + url = "https://res.exexm.com/cw_145225549855002"; //增加封面 ImageView imageView = new ImageView(this); imageView.setScaleType(ImageView.ScaleType.CENTER_CROP); imageView.setImageResource(R.mipmap.xxx1); - detailPlayer.setThumbImageView(imageView); resolveNormalVideoUI(); @@ -75,15 +69,36 @@ protected void onCreate(Bundle savedInstanceState) { //初始化不打开外部的旋转 orientationUtils.setEnable(false); - detailPlayer.setIsTouchWiget(true); - //detailPlayer.setIsTouchWigetFull(false); - //关闭自动旋转 - detailPlayer.setRotateViewAuto(false); - detailPlayer.setLockLand(false); - detailPlayer.setShowFullAnimation(false); - detailPlayer.setNeedLockFull(true); - detailPlayer.setSeekRatio(1); - //detailPlayer.setOpenPreView(false); + gsyVideoOptionBuilder = new GSYVideoOptionBuilder() + .setThumbImageView(imageView) + .setIsTouchWiget(true) + .setRotateViewAuto(false) + .setLockLand(false) + .setShowFullAnimation(false) + .setNeedLockFull(true) + .setSeekRatio(1) + .setUrl(url) + .setCacheWithPlay(cache) + .setVideoTitle("测试视频") + .setVideoAllCallBack(new GSYSampleCallBack() { + @Override + public void onPrepared(String url, Object... objects) { + super.onPrepared(url, objects); + //开始播放了才能旋转和全屏 + orientationUtils.setEnable(true); + isPlay = true; + } + + @Override + public void onQuitFullscreen(String url, Object... objects) { + super.onQuitFullscreen(url, objects); + if (orientationUtils != null) { + orientationUtils.backToProtVideo(); + } + } + }); + gsyVideoOptionBuilder.build(detailPlayer); + detailPlayer.getFullscreenButton().setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { @@ -95,34 +110,6 @@ public void onClick(View v) { } }); - detailPlayer.setStandardVideoAllCallBack(new SampleListener() { - @Override - public void onPrepared(String url, Object... objects) { - super.onPrepared(url, objects); - //开始播放了才能旋转和全屏 - orientationUtils.setEnable(true); - isPlay = true; - } - - @Override - public void onAutoComplete(String url, Object... objects) { - super.onAutoComplete(url, objects); - } - - @Override - public void onClickStartError(String url, Object... objects) { - super.onClickStartError(url, objects); - } - - @Override - public void onQuitFullscreen(String url, Object... objects) { - super.onQuitFullscreen(url, objects); - if (orientationUtils != null) { - orientationUtils.backToProtVideo(); - } - } - }); - detailPlayer.setLockClickListener(new LockClickListener() { @Override public void onClick(View view, boolean lock) { @@ -140,6 +127,18 @@ public void onClick(View v) { } }); + + + detailPlayer.postDelayed(new Runnable() { + @Override + public void run() { + ImageView testImage = findViewById(R.id.test_image_view); + Glide.with(InputUrlDetailActivity.this.getApplicationContext()) + .load("https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1525708180755&di=078af5aedf4b44268425be46bf25e407&imgtype=0&src=http%3A%2F%2Fc.hiphotos.baidu.com%2Fzhidao%2Fpic%2Fitem%2F203fb80e7bec54e78494e3a3bb389b504fc26a17.jpg") + .into(testImage); + } + }, 5000); + } @Override @@ -149,7 +148,7 @@ public void onBackPressed() { orientationUtils.backToProtVideo(); } - if (StandardGSYVideoPlayer.backFromWindowFull(this)) { + if (GSYVideoManager.backFromWindowFull(this)) { return; } super.onBackPressed(); @@ -158,12 +157,14 @@ public void onBackPressed() { @Override protected void onPause() { + getCurPlay().onVideoPause(); super.onPause(); isPause = true; } @Override protected void onResume() { + getCurPlay().onVideoResume(); super.onResume(); isPause = false; } @@ -171,7 +172,9 @@ protected void onResume() { @Override protected void onDestroy() { super.onDestroy(); - GSYVideoPlayer.releaseAllVideos(); + if (isPlay) { + getCurPlay().release(); + } //GSYPreViewManager.instance().releaseMediaPlayer(); if (orientationUtils != null) orientationUtils.releaseListener(); @@ -182,25 +185,24 @@ public void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); //如果旋转了就全屏 if (isPlay && !isPause) { - if (newConfig.orientation == ActivityInfo.SCREEN_ORIENTATION_USER) { - if (!detailPlayer.isIfCurrentIsFullscreen()) { - detailPlayer.startWindowFullscreen(InputUrlDetailActivity.this, true, true); - } - } else { - //新版本isIfCurrentIsFullscreen的标志位内部提前设置了,所以不会和手动点击冲突 - if (detailPlayer.isIfCurrentIsFullscreen()) { - StandardGSYVideoPlayer.backFromWindowFull(this); - } - if (orientationUtils != null) { - orientationUtils.setEnable(true); - } - } + detailPlayer.onConfigurationChanged(this, newConfig, orientationUtils, true, true); + } + } + + private GSYVideoPlayer getCurPlay() { + if (detailPlayer.getFullWindowPlayer() != null) { + return detailPlayer.getFullWindowPlayer(); } + return detailPlayer; } private void playVideo() { detailPlayer.release(); - detailPlayer.setUp(url, cache, null, "测试视频"); + gsyVideoOptionBuilder.setUrl(url) + .setCacheWithPlay(cache) + .setVideoTitle("测试视频") + .build(detailPlayer); + gsyVideoOptionBuilder.build(detailPlayer); detailPlayer.postDelayed(new Runnable() { @Override public void run() { @@ -212,7 +214,6 @@ public void run() { private void resolveNormalVideoUI() { //增加title detailPlayer.getTitleTextView().setVisibility(View.GONE); - detailPlayer.getTitleTextView().setText("测试视频"); detailPlayer.getBackButton().setVisibility(View.GONE); } diff --git a/app/src/main/java/com/example/gsyvideoplayer/ListADVideoActivity.java b/app/src/main/java/com/example/gsyvideoplayer/ListADVideoActivity.java new file mode 100644 index 000000000..3d990606a --- /dev/null +++ b/app/src/main/java/com/example/gsyvideoplayer/ListADVideoActivity.java @@ -0,0 +1,324 @@ +package com.example.gsyvideoplayer; + +import android.content.Context; +import android.os.Build; +import android.os.Bundle; +import androidx.appcompat.app.AppCompatActivity; +import android.transition.Explode; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.view.Window; +import android.widget.AbsListView; +import android.widget.BaseAdapter; +import android.widget.ListView; + +import com.example.gsyvideoplayer.model.VideoModel; +import com.example.gsyvideoplayer.video.SampleCoverVideo; +import com.shuyu.gsyvideoplayer.GSYVideoADManager; +import com.shuyu.gsyvideoplayer.GSYVideoManager; +import com.shuyu.gsyvideoplayer.listener.GSYSampleCallBack; +import com.shuyu.gsyvideoplayer.video.GSYADVideoPlayer; +import com.shuyu.gsyvideoplayer.video.StandardGSYVideoPlayer; + +import java.util.ArrayList; +import java.util.List; + +import butterknife.BindView; +import butterknife.ButterKnife; + +/** + * 带广告播放列表,支持中间插入广告模式 + */ +public class ListADVideoActivity extends AppCompatActivity { + + @BindView(R.id.video_list) + ListView videoList; + + ListADNormalAdapter listADNormalAdapter; + + + @Override + protected void onCreate(Bundle savedInstanceState) { + // 设置一个exit transition + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + getWindow().requestFeature(Window.FEATURE_CONTENT_TRANSITIONS); + getWindow().setEnterTransition(new Explode()); + getWindow().setExitTransition(new Explode()); + } + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_list_video); + ButterKnife.bind(this); + + listADNormalAdapter = new ListADNormalAdapter(this); + videoList.setAdapter(listADNormalAdapter); + + videoList.setOnScrollListener(new AbsListView.OnScrollListener() { + @Override + public void onScrollStateChanged(AbsListView view, int scrollState) { + } + + @Override + public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { + int lastVisibleItem = firstVisibleItem + visibleItemCount; + //大于0说明有播放 + if (GSYVideoManager.instance().getPlayPosition() >= 0) { + //当前播放的位置 + int position = GSYVideoManager.instance().getPlayPosition(); + //对应的播放列表TAG + if (GSYVideoManager.instance().getPlayTag().equals(ListADNormalAdapter.TAG) + && (position < firstVisibleItem || position > lastVisibleItem)) { + //如果滑出去了上面和下面就是否,和今日头条一样 + //释放广告和视频 + if (GSYVideoADManager.instance().listener() != null) { + GSYVideoADManager.instance().listener().onAutoCompletion(); + } + GSYVideoADManager.releaseAllVideos(); + GSYVideoManager.releaseAllVideos(); + listADNormalAdapter.notifyDataSetChanged(); + } + } + } + }); + + } + + @Override + public void onBackPressed() { + if (GSYVideoADManager.backFromWindowFull(this)) { + return; + } + if (GSYVideoManager.backFromWindowFull(this)) { + return; + } + super.onBackPressed(); + } + + @Override + protected void onPause() { + super.onPause(); + GSYVideoManager.onPause(); + GSYVideoADManager.onPause(); + } + + @Override + protected void onResume() { + super.onResume(); + GSYVideoManager.onResume(); + GSYVideoADManager.onResume(); + } + + @Override + protected void onDestroy() { + super.onDestroy(); + GSYVideoManager.releaseAllVideos(); + GSYVideoADManager.releaseAllVideos(); + } + + + public class ListADNormalAdapter extends BaseAdapter { + + public static final String TAG = "ListADNormalAdapter"; + + private List list = new ArrayList<>(); + private LayoutInflater inflater = null; + private Context context; + + public ListADNormalAdapter(Context context) { + super(); + this.context = context; + inflater = LayoutInflater.from(context); + for (int i = 0; i < 40; i++) { + list.add(new VideoModel()); + } + + } + + @Override + public int getCount() { + return list.size(); + } + + @Override + public Object getItem(int position) { + return null; + } + + @Override + public long getItemId(int position) { + return 0; + } + + @Override + public View getView(final int position, View convertView, ViewGroup parent) { + final ViewHolder holder; + if (convertView == null) { + holder = new ViewHolder(); + convertView = inflater.inflate(R.layout.list_video_item_ad, null); + holder.gsyVideoPlayer = (SampleCoverVideo) convertView.findViewById(R.id.video_item_player); + holder.adVideoPlayer = (GSYADVideoPlayer) convertView.findViewById(R.id.video_ad_player); + convertView.setTag(holder); + } else { + holder = (ViewHolder) convertView.getTag(); + } + + + final String url = "http://9890.vod.myqcloud.com/9890_4e292f9a3dd011e6b4078980237cc3d3.f20.mp4"; + final String urlAD = "http://7xjmzj.com1.z0.glb.clouddn.com/20171026175005_JObCxCE2.mp4"; + + //多个播放时必须在setUpLazy、setUp和getGSYVideoManager()等前面设置 + holder.gsyVideoPlayer.setPlayTag(TAG); + holder.gsyVideoPlayer.setPlayPosition(position); + + boolean isPlaying = holder.gsyVideoPlayer.getCurrentPlayer().isInPlayingState(); + + if (!isPlaying) { + holder.gsyVideoPlayer.setUpLazy(url, false, null, null, "这是title"); + } + + boolean isADPlaying = holder.adVideoPlayer.getCurrentPlayer().isInPlayingState(); + if (!isADPlaying) { + holder.adVideoPlayer.setUpLazy(urlAD, false, null, null, "这是title"); + } + + + //增加title + holder.gsyVideoPlayer.getTitleTextView().setVisibility(View.GONE); + + //设置返回键 + holder.gsyVideoPlayer.getBackButton().setVisibility(View.GONE); + + + //设置全屏按键功能 + holder.gsyVideoPlayer.getFullscreenButton().setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + resolveFullBtn(holder.gsyVideoPlayer); + } + }); + holder.adVideoPlayer.getFullscreenButton().setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + resolveFullBtn(holder.adVideoPlayer); + } + }); + holder.gsyVideoPlayer.setRotateViewAuto(false); + holder.adVideoPlayer.setRotateViewAuto(false); + holder.gsyVideoPlayer.setLockLand(true); + holder.adVideoPlayer.setLockLand(true); + holder.gsyVideoPlayer.setReleaseWhenLossAudio(false); + holder.adVideoPlayer.setReleaseWhenLossAudio(false); + holder.gsyVideoPlayer.setShowFullAnimation(false); + holder.adVideoPlayer.setShowFullAnimation(false); + holder.gsyVideoPlayer.setIsTouchWiget(false); + holder.adVideoPlayer.setIsTouchWiget(false); + + holder.gsyVideoPlayer.setNeedLockFull(true); + + if (position % 2 == 0) { + holder.gsyVideoPlayer.loadCoverImage(url, R.mipmap.xxx1); + } else { + holder.gsyVideoPlayer.loadCoverImage(url, R.mipmap.xxx2); + } + + holder.gsyVideoPlayer.setVideoAllCallBack(new GSYSampleCallBack() { + + + @Override + public void onPrepared(String url, Object... objects) { + super.onPrepared(url, objects); + if (isNeedAdOnStart()) + startAdPlay(holder.adVideoPlayer, holder.gsyVideoPlayer); + } + + @Override + public void onQuitFullscreen(String url, Object... objects) { + super.onQuitFullscreen(url, objects); + } + + @Override + public void onEnterFullscreen(String url, Object... objects) { + super.onEnterFullscreen(url, objects); + holder.gsyVideoPlayer.getCurrentPlayer().getTitleTextView().setText((String) objects[0]); + } + + @Override + public void onAutoComplete(String url, Object... objects) { + super.onAutoComplete(url, objects); + } + }); + + holder.adVideoPlayer.setVideoAllCallBack(new GSYSampleCallBack() { + + + @Override + public void onAutoComplete(String url, Object... objects) { + //广告结束,释放 + holder.adVideoPlayer.getCurrentPlayer().release(); + holder.adVideoPlayer.onVideoReset(); + holder.adVideoPlayer.setVisibility(View.GONE); + + //开始播放原视频,根据是否处于全屏状态判断 + int playPosition = holder.gsyVideoPlayer.getGSYVideoManager().getPlayPosition(); + if (position == playPosition) { + holder.gsyVideoPlayer.getCurrentPlayer().startAfterPrepared(); + } + + if (holder.adVideoPlayer.getCurrentPlayer().isIfCurrentIsFullscreen()) { + holder.adVideoPlayer.removeFullWindowViewOnly(); + if (!holder.gsyVideoPlayer.getCurrentPlayer().isIfCurrentIsFullscreen()) { + resolveFullBtn(holder.gsyVideoPlayer); + holder.gsyVideoPlayer.setSaveBeforeFullSystemUiVisibility(holder.adVideoPlayer.getSaveBeforeFullSystemUiVisibility()); + } + } + } + + @Override + public void onQuitFullscreen(String url, Object... objects) { + //退出全屏逻辑 + if (holder.gsyVideoPlayer.isIfCurrentIsFullscreen()) { + holder.gsyVideoPlayer.onBackFullscreen(); + } + } + + }); + + + return convertView; + } + + /** + * 全屏幕按键处理 + */ + private void resolveFullBtn(final StandardGSYVideoPlayer standardGSYVideoPlayer) { + standardGSYVideoPlayer.startWindowFullscreen(context, false, true); + } + + + /** + * 显示播放广告 + */ + public void startAdPlay(GSYADVideoPlayer gsyadVideoPlayer, StandardGSYVideoPlayer normalPlayer) { + gsyadVideoPlayer.setVisibility(View.VISIBLE); + gsyadVideoPlayer.startPlayLogic(); + if (normalPlayer.getCurrentPlayer().isIfCurrentIsFullscreen()) { + resolveFullBtn(gsyadVideoPlayer); + gsyadVideoPlayer.setSaveBeforeFullSystemUiVisibility(normalPlayer.getSaveBeforeFullSystemUiVisibility()); + } + } + + class ViewHolder { + SampleCoverVideo gsyVideoPlayer; + GSYADVideoPlayer adVideoPlayer; + } + } + + + /** + * 需要片头广告 + */ + public boolean isNeedAdOnStart() { + return true; + } + +} diff --git a/app/src/main/java/com/example/gsyvideoplayer/ListADVideoActivity2.java b/app/src/main/java/com/example/gsyvideoplayer/ListADVideoActivity2.java new file mode 100644 index 000000000..d81564166 --- /dev/null +++ b/app/src/main/java/com/example/gsyvideoplayer/ListADVideoActivity2.java @@ -0,0 +1,334 @@ +package com.example.gsyvideoplayer; + +import android.content.Context; +import android.os.Build; +import android.os.Bundle; +import androidx.appcompat.app.AppCompatActivity; +import android.transition.Explode; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.view.Window; +import android.widget.AbsListView; +import android.widget.BaseAdapter; +import android.widget.ListView; + +import com.example.gsyvideoplayer.model.VideoModel; +import com.example.gsyvideoplayer.video.RequestListADVideoPlayer; +import com.example.gsyvideoplayer.video.SampleCoverVideo; +import com.shuyu.gsyvideoplayer.GSYVideoADManager; +import com.shuyu.gsyvideoplayer.GSYVideoManager; +import com.shuyu.gsyvideoplayer.listener.GSYSampleCallBack; +import com.shuyu.gsyvideoplayer.video.GSYADVideoPlayer; +import com.shuyu.gsyvideoplayer.video.StandardGSYVideoPlayer; + +import java.util.ArrayList; +import java.util.List; + +import butterknife.BindView; +import butterknife.ButterKnife; + +/** + * 带广告播放列表,支持中间插入广告模式 + */ +public class ListADVideoActivity2 extends AppCompatActivity { + + @BindView(R.id.video_list) + ListView videoList; + + ListADNormalAdapter listADNormalAdapter; + + + @Override + protected void onCreate(Bundle savedInstanceState) { + // 设置一个exit transition + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + getWindow().requestFeature(Window.FEATURE_CONTENT_TRANSITIONS); + getWindow().setEnterTransition(new Explode()); + getWindow().setExitTransition(new Explode()); + } + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_list_video); + ButterKnife.bind(this); + + listADNormalAdapter = new ListADNormalAdapter(this); + videoList.setAdapter(listADNormalAdapter); + + videoList.setOnScrollListener(new AbsListView.OnScrollListener() { + @Override + public void onScrollStateChanged(AbsListView view, int scrollState) { + } + + @Override + public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { + int lastVisibleItem = firstVisibleItem + visibleItemCount; + //大于0说明有播放 + if (GSYVideoManager.instance().getPlayPosition() >= 0) { + //当前播放的位置 + int position = GSYVideoManager.instance().getPlayPosition(); + //对应的播放列表TAG + if (GSYVideoManager.instance().getPlayTag().equals(ListADNormalAdapter.TAG) + && (position < firstVisibleItem || position > lastVisibleItem)) { + //如果滑出去了上面和下面就是否,和今日头条一样 + //释放广告和视频 + if (GSYVideoADManager.instance().listener() != null) { + GSYVideoADManager.instance().listener().onAutoCompletion(); + } + GSYVideoADManager.releaseAllVideos(); + GSYVideoManager.releaseAllVideos(); + listADNormalAdapter.notifyDataSetChanged(); + } + } + } + }); + + } + + @Override + public void onBackPressed() { + if (GSYVideoADManager.backFromWindowFull(this)) { + return; + } + if (GSYVideoManager.backFromWindowFull(this)) { + return; + } + super.onBackPressed(); + } + + @Override + protected void onPause() { + super.onPause(); + GSYVideoManager.onPause(); + GSYVideoADManager.onPause(); + } + + @Override + protected void onResume() { + super.onResume(); + GSYVideoManager.onResume(); + GSYVideoADManager.onResume(); + } + + @Override + protected void onDestroy() { + super.onDestroy(); + GSYVideoManager.releaseAllVideos(); + GSYVideoADManager.releaseAllVideos(); + } + + + public class ListADNormalAdapter extends BaseAdapter { + + public static final String TAG = "ListADNormalAdapter"; + + private List list = new ArrayList<>(); + private LayoutInflater inflater = null; + private Context context; + + public ListADNormalAdapter(Context context) { + super(); + this.context = context; + inflater = LayoutInflater.from(context); + for (int i = 0; i < 40; i++) { + list.add(new VideoModel()); + } + + } + + @Override + public int getCount() { + return list.size(); + } + + @Override + public Object getItem(int position) { + return null; + } + + @Override + public long getItemId(int position) { + return 0; + } + + @Override + public View getView(final int position, View convertView, ViewGroup parent) { + final ViewHolder holder; + if (convertView == null) { + holder = new ViewHolder(); + convertView = inflater.inflate(R.layout.list_video_item_ad2, null); + holder.gsyVideoPlayer = (SampleCoverVideo) convertView.findViewById(R.id.video_item_player); + holder.adVideoPlayer = (RequestListADVideoPlayer) convertView.findViewById(R.id.video_ad_player); + convertView.setTag(holder); + } else { + holder = (ViewHolder) convertView.getTag(); + } + + + final String url = "http://9890.vod.myqcloud.com/9890_4e292f9a3dd011e6b4078980237cc3d3.f20.mp4"; + final String urlAD = "http://7xjmzj.com1.z0.glb.clouddn.com/20171026175005_JObCxCE2.mp4"; + + //多个播放时必须在setUpLazy、setUp和getGSYVideoManager()等前面设置 + holder.gsyVideoPlayer.setPlayTag(TAG); + holder.gsyVideoPlayer.setPlayPosition(position); + + boolean isPlaying = holder.gsyVideoPlayer.getCurrentPlayer().isInPlayingState(); + + if (!isPlaying) { + holder.gsyVideoPlayer.setUpLazy(url, false, null, null, "这是title"); + } + + boolean isADPlaying = holder.adVideoPlayer.getCurrentPlayer().isInPlayingState(); + if (!isADPlaying) { + holder.adVideoPlayer.setUpLazy(urlAD, false, null, null, "这是title"); + } + + + //增加title + holder.gsyVideoPlayer.getTitleTextView().setVisibility(View.GONE); + + //设置返回键 + holder.gsyVideoPlayer.getBackButton().setVisibility(View.GONE); + + + //设置全屏按键功能 + holder.gsyVideoPlayer.getFullscreenButton().setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + resolveFullBtn(holder.gsyVideoPlayer); + } + }); + holder.adVideoPlayer.getFullscreenButton().setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + resolveFullBtn(holder.adVideoPlayer); + } + }); + holder.gsyVideoPlayer.setRotateViewAuto(false); + holder.adVideoPlayer.setRotateViewAuto(false); + holder.gsyVideoPlayer.setLockLand(true); + holder.adVideoPlayer.setLockLand(true); + holder.gsyVideoPlayer.setReleaseWhenLossAudio(false); + holder.adVideoPlayer.setReleaseWhenLossAudio(false); + holder.gsyVideoPlayer.setShowFullAnimation(false); + holder.adVideoPlayer.setShowFullAnimation(false); + holder.gsyVideoPlayer.setIsTouchWiget(false); + holder.adVideoPlayer.setIsTouchWiget(false); + + holder.gsyVideoPlayer.setNeedLockFull(true); + + if (position % 2 == 0) { + holder.gsyVideoPlayer.loadCoverImage(url, R.mipmap.xxx1); + } else { + holder.gsyVideoPlayer.loadCoverImage(url, R.mipmap.xxx2); + } + + holder.gsyVideoPlayer.setVideoAllCallBack(new GSYSampleCallBack() { + + @Override + public void onClickStartIcon(String url, Object... objects) { + super.onClickStartIcon(url, objects); + if (holder.adVideoPlayer.getGSYVideoManager().listener() != null) { + holder.adVideoPlayer.getGSYVideoManager().listener().onAutoCompletion(); + } + } + + @Override + public void onPrepared(String url, Object... objects) { + super.onPrepared(url, objects); + if (isNeedAdOnStart()) { + holder.gsyVideoPlayer.getCurrentPlayer().onVideoPause(); + startAdPlay(holder.adVideoPlayer, holder.gsyVideoPlayer); + } + } + + @Override + public void onQuitFullscreen(String url, Object... objects) { + super.onQuitFullscreen(url, objects); + } + + @Override + public void onEnterFullscreen(String url, Object... objects) { + super.onEnterFullscreen(url, objects); + holder.gsyVideoPlayer.getCurrentPlayer().getTitleTextView().setText((String) objects[0]); + } + + @Override + public void onAutoComplete(String url, Object... objects) { + super.onAutoComplete(url, objects); + } + }); + + holder.adVideoPlayer.setVideoAllCallBack(new GSYSampleCallBack() { + + + @Override + public void onAutoComplete(String url, Object... objects) { + //广告结束,释放 + holder.adVideoPlayer.getCurrentPlayer().release(); + holder.adVideoPlayer.onVideoReset(); + holder.adVideoPlayer.setVisibility(View.GONE); + + //开始播放原视频,根据是否处于全屏状态判断 + int playPosition = holder.gsyVideoPlayer.getGSYVideoManager().getPlayPosition(); + if (position == playPosition) { + holder.gsyVideoPlayer.getCurrentPlayer().startAfterPrepared(); + } + + if (holder.adVideoPlayer.getCurrentPlayer().isIfCurrentIsFullscreen()) { + holder.adVideoPlayer.removeFullWindowViewOnly(); + if (!holder.gsyVideoPlayer.getCurrentPlayer().isIfCurrentIsFullscreen()) { + resolveFullBtn(holder.gsyVideoPlayer); + holder.gsyVideoPlayer.setSaveBeforeFullSystemUiVisibility(holder.adVideoPlayer.getSaveBeforeFullSystemUiVisibility()); + } + } + } + + @Override + public void onQuitFullscreen(String url, Object... objects) { + //退出全屏逻辑 + if (holder.gsyVideoPlayer.isIfCurrentIsFullscreen()) { + holder.gsyVideoPlayer.onBackFullscreen(); + } + } + + }); + + + return convertView; + } + + /** + * 全屏幕按键处理 + */ + private void resolveFullBtn(final StandardGSYVideoPlayer standardGSYVideoPlayer) { + standardGSYVideoPlayer.startWindowFullscreen(context, false, true); + } + + + /** + * 显示播放广告 + */ + public void startAdPlay(GSYADVideoPlayer gsyadVideoPlayer, StandardGSYVideoPlayer normalPlayer) { + gsyadVideoPlayer.setVisibility(View.VISIBLE); + gsyadVideoPlayer.startPlayLogic(); + if (normalPlayer.getCurrentPlayer().isIfCurrentIsFullscreen()) { + resolveFullBtn(gsyadVideoPlayer); + gsyadVideoPlayer.setSaveBeforeFullSystemUiVisibility(normalPlayer.getSaveBeforeFullSystemUiVisibility()); + } + } + + class ViewHolder { + SampleCoverVideo gsyVideoPlayer; + RequestListADVideoPlayer adVideoPlayer; + } + } + + + /** + * 需要片头广告 + */ + public boolean isNeedAdOnStart() { + return true; + } + +} diff --git a/app/src/main/java/com/example/gsyvideoplayer/ListMultiVideoActivity.java b/app/src/main/java/com/example/gsyvideoplayer/ListMultiVideoActivity.java new file mode 100644 index 000000000..bac1e9b7c --- /dev/null +++ b/app/src/main/java/com/example/gsyvideoplayer/ListMultiVideoActivity.java @@ -0,0 +1,112 @@ +package com.example.gsyvideoplayer; + +import android.os.Build; +import android.os.Bundle; +import androidx.appcompat.app.AppCompatActivity; +import android.transition.Explode; +import android.view.Window; +import android.widget.AbsListView; +import android.widget.ListView; + +import com.example.gsyvideoplayer.adapter.ListMultiNormalAdapter; +import com.example.gsyvideoplayer.video.manager.CustomManager; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import butterknife.BindView; +import butterknife.ButterKnife; + +/** + * 多个同时播放的demo + */ +public class ListMultiVideoActivity extends AppCompatActivity { + + @BindView(R.id.video_list) + ListView videoList; + + ListMultiNormalAdapter listMultiNormalAdapter; + + private boolean isPause; + + @Override + protected void onCreate(Bundle savedInstanceState) { + // 设置一个exit transition + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + getWindow().requestFeature(Window.FEATURE_CONTENT_TRANSITIONS); + getWindow().setEnterTransition(new Explode()); + getWindow().setExitTransition(new Explode()); + } + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_list_video); + ButterKnife.bind(this); + + listMultiNormalAdapter = new ListMultiNormalAdapter(this); + videoList.setAdapter(listMultiNormalAdapter); + + videoList.setOnScrollListener(new AbsListView.OnScrollListener() { + @Override + public void onScrollStateChanged(AbsListView view, int scrollState) { + } + + @Override + public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { + int lastVisibleItem = firstVisibleItem + visibleItemCount; + //大于0说明有播放 + if (CustomManager.instance().size() >= 0) { + Map map = CustomManager.instance(); + List removeKey = new ArrayList<>(); + for (Map.Entry customManagerEntry : map.entrySet()) { + CustomManager customManager = customManagerEntry.getValue(); + //当前播放的位置 + int position = customManager.getPlayPosition(); + //对应的播放列表TAG + if (customManager.getPlayTag().equals(ListMultiNormalAdapter.TAG) + && (position < firstVisibleItem || position > lastVisibleItem)) { + CustomManager.releaseAllVideos(customManagerEntry.getKey()); + removeKey.add(customManagerEntry.getKey()); + } + } + if(removeKey.size() > 0) { + for (String key : removeKey) { + map.remove(key); + } + listMultiNormalAdapter.notifyDataSetChanged(); + } + } + } + + }); + + } + + @Override + public void onBackPressed() { + if (CustomManager.backFromWindowFull(this, listMultiNormalAdapter.getFullKey())) { + return; + } + super.onBackPressed(); + } + + @Override + protected void onPause() { + super.onPause(); + CustomManager.onPauseAll(); + isPause = true; + } + + @Override + protected void onResume() { + super.onResume(); + CustomManager.onResumeAll(); + isPause = false; + } + + @Override + protected void onDestroy() { + super.onDestroy(); + CustomManager.clearAllVideo(); + } + +} diff --git a/app/src/main/java/com/example/gsyvideoplayer/ListVideo2Activity.java b/app/src/main/java/com/example/gsyvideoplayer/ListVideo2Activity.java index e9230ad68..6f8260d2c 100644 --- a/app/src/main/java/com/example/gsyvideoplayer/ListVideo2Activity.java +++ b/app/src/main/java/com/example/gsyvideoplayer/ListVideo2Activity.java @@ -3,7 +3,7 @@ import android.graphics.Point; import android.os.Build; import android.os.Bundle; -import android.support.v7.app.AppCompatActivity; +import androidx.appcompat.app.AppCompatActivity; import android.transition.Explode; import android.view.Window; import android.widget.AbsListView; @@ -12,27 +12,37 @@ import android.widget.RelativeLayout; import com.example.gsyvideoplayer.adapter.ListVideoAdapter; -import com.example.gsyvideoplayer.listener.SampleListener; -import com.shuyu.gsyvideoplayer.GSYVideoPlayer; +import com.shuyu.gsyvideoplayer.GSYVideoManager; +import com.shuyu.gsyvideoplayer.listener.GSYSampleCallBack; +import com.shuyu.gsyvideoplayer.utils.GSYVideoHelper; import com.shuyu.gsyvideoplayer.utils.CommonUtil; import com.shuyu.gsyvideoplayer.utils.Debuger; -import com.shuyu.gsyvideoplayer.utils.ListVideoUtil; import butterknife.BindView; import butterknife.ButterKnife; +/** + * 列表小窗口 + */ public class ListVideo2Activity extends AppCompatActivity { @BindView(R.id.video_list) ListView videoList; + @BindView(R.id.video_full_container) FrameLayout videoFullContainer; + @BindView(R.id.activity_list_video) RelativeLayout activityListVideo; - ListVideoUtil listVideoUtil; + GSYVideoHelper smallVideoHelper; + ListVideoAdapter listVideoAdapter; + + GSYVideoHelper.GSYVideoHelperBuilder gsySmallVideoHelperBuilder; + int lastVisibleItem; + int firstVisibleItem; @Override @@ -47,20 +57,51 @@ protected void onCreate(Bundle savedInstanceState) { setContentView(R.layout.activity_list_video2); ButterKnife.bind(this); - listVideoUtil = new ListVideoUtil(this); - listVideoUtil.setFullViewContainer(videoFullContainer); - listVideoUtil.setHideStatusBar(true); - //listVideoUtil.setHideActionBar(true); - listVideoUtil.setNeedLockFull(true); + //创建小窗口帮助类 + smallVideoHelper = new GSYVideoHelper(this); + + //如果不设置即使用默认的 windowViewContainer + //smallVideoHelper.setFullViewContainer(videoFullContainer); + + //配置 + gsySmallVideoHelperBuilder = new GSYVideoHelper.GSYVideoHelperBuilder(); + gsySmallVideoHelperBuilder + .setHideStatusBar(true) + .setNeedLockFull(true) + .setCacheWithPlay(true) + .setShowFullAnimation(false) + .setRotateViewAuto(false) + .setLockLand(true) + .setVideoAllCallBack(new GSYSampleCallBack(){ + @Override + public void onPrepared(String url, Object... objects) { + super.onPrepared(url, objects); + Debuger.printfLog("Duration " + smallVideoHelper.getGsyVideoPlayer().getDuration() + " CurrentPosition " + smallVideoHelper.getGsyVideoPlayer().getCurrentPositionWhenPlaying()); + } - listVideoAdapter = new ListVideoAdapter(this, listVideoUtil); - listVideoAdapter.setRootView(activityListVideo); - videoList.setAdapter(listVideoAdapter); + @Override + public void onQuitSmallWidget(String url, Object... objects) { + super.onQuitSmallWidget(url, objects); + //大于0说明有播放,//对应的播放列表TAG + if (smallVideoHelper.getPlayPosition() >= 0 && smallVideoHelper.getPlayTAG().equals(ListVideoAdapter.TAG)) { + //当前播放的位置 + int position = smallVideoHelper.getPlayPosition(); + //不可视的是时候 + if ((position < firstVisibleItem || position > lastVisibleItem)) { + //释放掉视频 + smallVideoHelper.releaseVideoPlayer(); + listVideoAdapter.notifyDataSetChanged(); + } + } + + } + }); - //listVideoUtil.setShowFullAnimation(true); - //listVideoUtil.setAutoRotation(true); - //listVideoUtil.setFullLandFrist(true); + smallVideoHelper.setGsyVideoOptionBuilder(gsySmallVideoHelperBuilder); + listVideoAdapter = new ListVideoAdapter(this, smallVideoHelper, gsySmallVideoHelperBuilder); + listVideoAdapter.setRootView(activityListVideo); + videoList.setAdapter(listVideoAdapter); videoList.setOnScrollListener(new AbsListView.OnScrollListener() { @Override @@ -72,60 +113,32 @@ public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCoun ListVideo2Activity.this.firstVisibleItem = firstVisibleItem; lastVisibleItem = firstVisibleItem + visibleItemCount; //大于0说明有播放,//对应的播放列表TAG - if (listVideoUtil.getPlayPosition() >= 0 && listVideoUtil.getPlayTAG().equals(ListVideoAdapter.TAG)) { + if (smallVideoHelper.getPlayPosition() >= 0 && smallVideoHelper.getPlayTAG().equals(ListVideoAdapter.TAG)) { //当前播放的位置 - int position = listVideoUtil.getPlayPosition(); + int position = smallVideoHelper.getPlayPosition(); //不可视的是时候 if ((position < firstVisibleItem || position > lastVisibleItem)) { //如果是小窗口就不需要处理 - if (!listVideoUtil.isSmall()) { + if (!smallVideoHelper.isSmall()) { //小窗口 int size = CommonUtil.dip2px(ListVideo2Activity.this, 150); - listVideoUtil.showSmallVideo(new Point(size, size), false, true); + smallVideoHelper.showSmallVideo(new Point(size, size), false, true); } } else { - if (listVideoUtil.isSmall()) { - listVideoUtil.smallVideoToNormal(); + if (smallVideoHelper.isSmall()) { + smallVideoHelper.smallVideoToNormal(); } } } } }); - - //小窗口关闭被点击的时候回调处理回复页面 - listVideoUtil.setVideoAllCallBack(new SampleListener(){ - @Override - public void onPrepared(String url, Object... objects) { - super.onPrepared(url, objects); - Debuger.printfLog("Duration " + listVideoUtil.getDuration() + " CurrentPosition " + listVideoUtil.getCurrentPositionWhenPlaying()); - } - - @Override - public void onQuitSmallWidget(String url, Object... objects) { - super.onQuitSmallWidget(url, objects); - //大于0说明有播放,//对应的播放列表TAG - if (listVideoUtil.getPlayPosition() >= 0 && listVideoUtil.getPlayTAG().equals(ListVideoAdapter.TAG)) { - //当前播放的位置 - int position = listVideoUtil.getPlayPosition(); - //不可视的是时候 - if ((position < firstVisibleItem || position > lastVisibleItem)) { - //释放掉视频 - listVideoUtil.releaseVideoPlayer(); - listVideoAdapter.notifyDataSetChanged(); - } - } - - } - }); - - } @Override public void onBackPressed() { - if (listVideoUtil.backFromFull()) { + if (smallVideoHelper.backFromFull()) { return; } super.onBackPressed(); @@ -135,8 +148,8 @@ public void onBackPressed() { @Override protected void onDestroy() { super.onDestroy(); - listVideoUtil.releaseVideoPlayer(); - GSYVideoPlayer.releaseAllVideos(); + smallVideoHelper.releaseVideoPlayer(); + GSYVideoManager.releaseAllVideos(); } } diff --git a/app/src/main/java/com/example/gsyvideoplayer/ListVideoActivity.java b/app/src/main/java/com/example/gsyvideoplayer/ListVideoActivity.java index 8bc5c64f5..62638bc77 100644 --- a/app/src/main/java/com/example/gsyvideoplayer/ListVideoActivity.java +++ b/app/src/main/java/com/example/gsyvideoplayer/ListVideoActivity.java @@ -1,24 +1,21 @@ package com.example.gsyvideoplayer; +import android.content.res.Configuration; import android.os.Build; import android.os.Bundle; -import android.os.Debug; -import android.support.v7.app.AppCompatActivity; +import androidx.appcompat.app.AppCompatActivity; import android.transition.Explode; -import android.util.Log; import android.view.Window; +import android.view.WindowManager; import android.widget.AbsListView; import android.widget.ListView; import android.widget.RelativeLayout; import com.example.gsyvideoplayer.adapter.ListNormalAdapter; import com.shuyu.gsyvideoplayer.GSYVideoManager; -import com.shuyu.gsyvideoplayer.GSYVideoPlayer; -import com.shuyu.gsyvideoplayer.video.StandardGSYVideoPlayer; import butterknife.BindView; import butterknife.ButterKnife; -import tv.danmaku.ijk.media.player.IjkMediaPlayer; public class ListVideoActivity extends AppCompatActivity { @@ -27,11 +24,16 @@ public class ListVideoActivity extends AppCompatActivity { @BindView(R.id.activity_list_video) RelativeLayout activityListVideo; + ListNormalAdapter listNormalAdapter; + + private boolean isPause; + @Override protected void onCreate(Bundle savedInstanceState) { // 设置一个exit transition if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { getWindow().requestFeature(Window.FEATURE_CONTENT_TRANSITIONS); + getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS); getWindow().setEnterTransition(new Explode()); getWindow().setExitTransition(new Explode()); } @@ -39,7 +41,7 @@ protected void onCreate(Bundle savedInstanceState) { setContentView(R.layout.activity_list_video); ButterKnife.bind(this); - final ListNormalAdapter listNormalAdapter = new ListNormalAdapter(this); + listNormalAdapter = new ListNormalAdapter(this); videoList.setAdapter(listNormalAdapter); videoList.setOnScrollListener(new AbsListView.OnScrollListener() { @@ -57,8 +59,11 @@ public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCoun //对应的播放列表TAG if (GSYVideoManager.instance().getPlayTag().equals(ListNormalAdapter.TAG) && (position < firstVisibleItem || position > lastVisibleItem)) { + if(GSYVideoManager.isFullState(ListVideoActivity.this)) { + return; + } //如果滑出去了上面和下面就是否,和今日头条一样 - GSYVideoPlayer.releaseAllVideos(); + GSYVideoManager.releaseAllVideos(); listNormalAdapter.notifyDataSetChanged(); } } @@ -69,7 +74,10 @@ public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCoun @Override public void onBackPressed() { - if (StandardGSYVideoPlayer.backFromWindowFull(this)) { + //为了支持重力旋转 + onBackPressAdapter(); + + if (GSYVideoManager.backFromWindowFull(this)) { return; } super.onBackPressed(); @@ -79,17 +87,38 @@ public void onBackPressed() { protected void onPause() { super.onPause(); GSYVideoManager.onPause(); + isPause = true; } @Override protected void onResume() { super.onResume(); GSYVideoManager.onResume(); + isPause = false; } @Override protected void onDestroy() { super.onDestroy(); - GSYVideoPlayer.releaseAllVideos(); + GSYVideoManager.releaseAllVideos(); + if (listNormalAdapter != null) { + listNormalAdapter.onDestroy(); + } + } + + /********************************为了支持重力旋转********************************/ + @Override + public void onConfigurationChanged(Configuration newConfig) { + super.onConfigurationChanged(newConfig); + if (listNormalAdapter != null && listNormalAdapter.getListNeedAutoLand() && !isPause) { + listNormalAdapter.onConfigurationChanged(this, newConfig); + } + } + + private void onBackPressAdapter() { + //为了支持重力旋转 + if (listNormalAdapter != null && listNormalAdapter.getListNeedAutoLand()) { + listNormalAdapter.onBackPressed(); + } } } diff --git a/app/src/main/java/com/example/gsyvideoplayer/MainActivity.java b/app/src/main/java/com/example/gsyvideoplayer/MainActivity.java index b5f8a6ac8..39505f7c9 100644 --- a/app/src/main/java/com/example/gsyvideoplayer/MainActivity.java +++ b/app/src/main/java/com/example/gsyvideoplayer/MainActivity.java @@ -1,11 +1,18 @@ package com.example.gsyvideoplayer; -import android.app.Activity; +import android.Manifest; +import android.content.Intent; +import android.os.Build; import android.os.Bundle; -import android.support.v7.app.AppCompatActivity; + +import androidx.annotation.NonNull; +import androidx.appcompat.app.AppCompatActivity; + import android.view.View; import android.widget.Button; +import android.widget.Toast; +import com.example.gsyvideoplayer.simple.SimpleActivity; import com.example.gsyvideoplayer.utils.JumpUtils; import com.shuyu.gsyvideoplayer.GSYVideoManager; import com.shuyu.gsyvideoplayer.utils.Debuger; @@ -13,23 +20,49 @@ import butterknife.BindView; import butterknife.ButterKnife; import butterknife.OnClick; +import permissions.dispatcher.PermissionUtils; public class MainActivity extends AppCompatActivity { @BindView(R.id.open_btn) Button openBtn; + @BindView(R.id.open_btn_empty) + Button openBtn2; + + final String[] permissions = {Manifest.permission.WRITE_EXTERNAL_STORAGE}; + @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Debuger.enable(); ButterKnife.bind(this); + boolean hadPermission = PermissionUtils.hasSelfPermissions(this, permissions); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && !hadPermission) { + String[] permissions = {Manifest.permission.WRITE_EXTERNAL_STORAGE}; + requestPermissions(permissions, 1110); + } } - @OnClick({R.id.open_btn, R.id.list_btn, R.id.list_btn_2, R.id.list_detail, R.id.clear_cache, R.id.recycler, R.id.recycler_2, R.id.list_detail_list, R.id.web_detail, R.id.danmaku_video, R.id.fragment_video, R.id.more_type, R.id.input_type}) + @Override + public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { + super.onRequestPermissionsResult(requestCode, permissions, grantResults); + boolean sdPermissionResult = PermissionUtils.verifyPermissions(grantResults); + if (!sdPermissionResult) { + Toast.makeText(this, "没获取到sd卡权限,无法播放本地视频哦", Toast.LENGTH_LONG).show(); + } + } + + @OnClick({R.id.open_btn, R.id.list_btn, R.id.list_btn_2, R.id.list_detail, R.id.clear_cache, R.id.recycler, R.id.recycler_2, R.id.list_detail_list, R.id.web_detail, R.id.danmaku_video, R.id.fragment_video, + R.id.more_type, R.id.input_type, R.id.open_btn_empty, R.id.open_control, R.id.open_filter, R.id.open_btn_pick, R.id.open_btn_auto, R.id.open_scroll, R.id.open_window, R.id.open_btn_ad, + R.id.open_btn_multi, R.id.open_btn_ad2, R.id.open_list_ad, R.id.open_custom_exo, R.id.open_simple, R.id.open_switch, R.id.media_codec, R.id.detail_normal_activity, R.id.detail_download_activity, R.id.detail_audio_activity, R.id.detail_subtitle_activity}) public void onClick(View view) { switch (view.getId()) { + case R.id.open_simple: + //简单的播放 + startActivity(new Intent(this, SimpleActivity.class)); + break; case R.id.open_btn: //直接一个页面播放的 JumpUtils.goToVideoPlayer(this, openBtn); @@ -59,7 +92,7 @@ public void onClick(View view) { JumpUtils.goToDetailListPlayer(this); break; case R.id.web_detail: - //播放一个连续列表 + //正常播放,带preview JumpUtils.gotoWebDetail(this); break; case R.id.danmaku_video: @@ -77,10 +110,73 @@ public void onClick(View view) { case R.id.input_type: JumpUtils.gotoInput(this); break; + case R.id.open_btn_empty: + JumpUtils.goToPlayEmptyControlActivity(this, openBtn2); + break; + case R.id.open_control: + JumpUtils.gotoControl(this); + break; + case R.id.open_filter: + JumpUtils.gotoFilter(this); + break; + case R.id.open_btn_pick: + //无缝切换 + JumpUtils.goToVideoPickPlayer(this, openBtn); + break; + case R.id.open_btn_auto: + //列表自动播放 + JumpUtils.goToAutoVideoPlayer(this); + break; + case R.id.open_scroll: + //列表自动播放 + JumpUtils.goToScrollDetailPlayer(this); + break; + case R.id.open_window: + //多窗体下的悬浮窗 + JumpUtils.goToScrollWindow(this); + break; + case R.id.open_btn_ad: + //广告 + JumpUtils.goToVideoADPlayer(this); + break; + case R.id.open_btn_multi: + //多个同时播放 + JumpUtils.goToMultiVideoPlayer(this); + break; + case R.id.open_btn_ad2: + //多个同时播放 + JumpUtils.goToVideoADPlayer2(this); + break; + case R.id.open_list_ad: + //多个同时播放 + JumpUtils.goToADListVideoPlayer(this); + break; + case R.id.open_custom_exo: + //多个同时播放 + JumpUtils.goToDetailExoListPlayer(this); + break; + case R.id.open_switch: + JumpUtils.goToSwitch(this); + break; + case R.id.media_codec: + JumpUtils.goMediaCodec(this); + break; + case R.id.detail_normal_activity: + JumpUtils.goToDetailNormalActivity(this); + break; + case R.id.detail_download_activity: + JumpUtils.goToDetailDownloadActivity(this); + break; + case R.id.detail_subtitle_activity: + JumpUtils.goToGSYExoSubTitleDetailPlayer(this); + break; + case R.id.detail_audio_activity: + JumpUtils.goToDetailAudioActivity(this); + break; case R.id.clear_cache: //清理缓存 - GSYVideoManager.clearAllDefaultCache(MainActivity.this); - //String url = "http://baobab.wdjcdn.com/14564977406580.mp4"; + GSYVideoManager.instance().clearAllDefaultCache(MainActivity.this); + //String url = "https://res.exexm.com/cw_145225549855002"; //GSYVideoManager.clearDefaultCache(MainActivity.this, url); break; } diff --git a/app/src/main/java/com/example/gsyvideoplayer/PlayActivity.java b/app/src/main/java/com/example/gsyvideoplayer/PlayActivity.java index 02fc4c62a..3205ce621 100644 --- a/app/src/main/java/com/example/gsyvideoplayer/PlayActivity.java +++ b/app/src/main/java/com/example/gsyvideoplayer/PlayActivity.java @@ -5,8 +5,9 @@ import android.os.Build; import android.os.Bundle; import android.os.Handler; -import android.support.v4.view.ViewCompat; -import android.support.v7.app.AppCompatActivity; + +import androidx.appcompat.app.AppCompatActivity; +import androidx.core.view.ViewCompat; import android.transition.Transition; import android.view.View; import android.widget.ImageView; @@ -15,12 +16,9 @@ import com.example.gsyvideoplayer.listener.OnTransitionListener; import com.example.gsyvideoplayer.model.SwitchVideoModel; import com.example.gsyvideoplayer.video.SampleVideo; -import com.shuyu.gsyvideoplayer.GSYVideoPlayer; -import com.shuyu.gsyvideoplayer.utils.FileUtils; +import com.shuyu.gsyvideoplayer.GSYVideoManager; import com.shuyu.gsyvideoplayer.utils.OrientationUtils; -import com.shuyu.gsyvideoplayer.video.StandardGSYVideoPlayer; -import java.io.File; import java.util.ArrayList; import java.util.List; @@ -55,7 +53,7 @@ protected void onCreate(Bundle savedInstanceState) { } private void init() { - String url = "http://baobab.wdjcdn.com/14564977406580.mp4"; + String url = "https://res.exexm.com/cw_145225549855002"; //String url = "http://7xse1z.com1.z0.glb.clouddn.com/1491813192"; //需要路径的 @@ -74,7 +72,7 @@ private void init() { list.add(switchVideoModel); list.add(switchVideoModel2); - videoPlayer.setUp(list, true, ""); + videoPlayer.setUp(list, true, "测试视频"); //增加封面 ImageView imageView = new ImageView(this); @@ -84,7 +82,6 @@ private void init() { //增加title videoPlayer.getTitleTextView().setVisibility(View.VISIBLE); - videoPlayer.getTitleTextView().setText("测试视频"); //videoPlayer.setShowPauseCover(false); //videoPlayer.setSpeed(2f); @@ -135,6 +132,7 @@ protected void onPause() { @Override protected void onResume() { super.onResume(); + videoPlayer.onVideoResume(); } @TargetApi(Build.VERSION_CODES.KITKAT) @@ -153,8 +151,8 @@ public void onBackPressed() { return; } //释放所有 - videoPlayer.setStandardVideoAllCallBack(null); - GSYVideoPlayer.releaseAllVideos(); + videoPlayer.setVideoAllCallBack(null); + GSYVideoManager.releaseAllVideos(); if (isTransition && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { super.onBackPressed(); } else { diff --git a/app/src/main/java/com/example/gsyvideoplayer/PlayEmptyControlActivity.java b/app/src/main/java/com/example/gsyvideoplayer/PlayEmptyControlActivity.java new file mode 100644 index 000000000..19c1e5d93 --- /dev/null +++ b/app/src/main/java/com/example/gsyvideoplayer/PlayEmptyControlActivity.java @@ -0,0 +1,122 @@ +package com.example.gsyvideoplayer; + +import android.annotation.TargetApi; +import android.os.Build; +import android.os.Bundle; +import android.os.Handler; +import androidx.core.view.ViewCompat; +import androidx.appcompat.app.AppCompatActivity; +import android.transition.Transition; + +import com.example.gsyvideoplayer.listener.OnTransitionListener; +import com.example.gsyvideoplayer.video.EmptyControlVideo; +import com.shuyu.gsyvideoplayer.GSYVideoManager; +import com.shuyu.gsyvideoplayer.utils.OrientationUtils; + +import butterknife.BindView; +import butterknife.ButterKnife; + +/** + * 单独的视频播放页面 + * Created by shuyu on 2016/11/11. + */ +public class PlayEmptyControlActivity extends AppCompatActivity { + + public final static String IMG_TRANSITION = "IMG_TRANSITION"; + public final static String TRANSITION = "TRANSITION"; + + @BindView(R.id.video_player) + EmptyControlVideo videoPlayer; + + OrientationUtils orientationUtils; + + private boolean isTransition; + + private Transition transition; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_play_empty_control); + ButterKnife.bind(this); + isTransition = getIntent().getBooleanExtra(TRANSITION, false); + init(); + } + + private void init() { + String url = "https://res.exexm.com/cw_145225549855002"; + + videoPlayer.setUp(url, true, ""); + + //过渡动画 + initTransition(); + } + + + @Override + protected void onPause() { + super.onPause(); + } + + @Override + protected void onResume() { + super.onResume(); + } + + @TargetApi(Build.VERSION_CODES.KITKAT) + @Override + protected void onDestroy() { + super.onDestroy(); + videoPlayer.release(); + if (orientationUtils != null) + orientationUtils.releaseListener(); + } + + @Override + public void onBackPressed() { + //释放所有 + videoPlayer.setVideoAllCallBack(null); + GSYVideoManager.releaseAllVideos(); + if (isTransition && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + super.onBackPressed(); + } else { + new Handler().postDelayed(new Runnable() { + @Override + public void run() { + finish(); + overridePendingTransition(R.anim.abc_fade_in, R.anim.abc_fade_out); + } + }, 500); + } + } + + + private void initTransition() { + if (isTransition && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + postponeEnterTransition(); + ViewCompat.setTransitionName(videoPlayer, IMG_TRANSITION); + addTransitionListener(); + startPostponedEnterTransition(); + } else { + videoPlayer.startPlayLogic(); + } + } + + @TargetApi(Build.VERSION_CODES.LOLLIPOP) + private boolean addTransitionListener() { + transition = getWindow().getSharedElementEnterTransition(); + if (transition != null) { + transition.addListener(new OnTransitionListener(){ + @Override + public void onTransitionEnd(Transition transition) { + super.onTransitionEnd(transition); + videoPlayer.startPlayLogic(); + transition.removeListener(this); + } + }); + return true; + } + return false; + } + +} diff --git a/app/src/main/java/com/example/gsyvideoplayer/PlayPickActivity.java b/app/src/main/java/com/example/gsyvideoplayer/PlayPickActivity.java new file mode 100644 index 000000000..c9eb978ee --- /dev/null +++ b/app/src/main/java/com/example/gsyvideoplayer/PlayPickActivity.java @@ -0,0 +1,180 @@ +package com.example.gsyvideoplayer; + +import android.annotation.TargetApi; +import android.content.pm.ActivityInfo; +import android.os.Build; +import android.os.Bundle; +import android.os.Handler; +import androidx.core.view.ViewCompat; +import androidx.appcompat.app.AppCompatActivity; +import android.transition.Transition; +import android.view.View; +import android.widget.ImageView; + +import com.example.gsyvideoplayer.listener.OnTransitionListener; +import com.example.gsyvideoplayer.model.SwitchVideoModel; +import com.example.gsyvideoplayer.video.SmartPickVideo; +import com.shuyu.gsyvideoplayer.GSYVideoManager; +import com.shuyu.gsyvideoplayer.utils.OrientationUtils; + +import java.util.ArrayList; +import java.util.List; + +import butterknife.BindView; +import butterknife.ButterKnife; + +/** + * 单独的视频播放页面 + * Created by shuyu on 2016/11/11. + */ +public class PlayPickActivity extends AppCompatActivity { + + public final static String IMG_TRANSITION = "IMG_TRANSITION"; + public final static String TRANSITION = "TRANSITION"; + + @BindView(R.id.video_player) + SmartPickVideo videoPlayer; + + OrientationUtils orientationUtils; + + private boolean isTransition; + + private Transition transition; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_play_pick); + ButterKnife.bind(this); + isTransition = getIntent().getBooleanExtra(TRANSITION, false); + init(); + } + + private void init() { + //借用了jjdxm_ijkplayer的URL + String source1 = "http://9890.vod.myqcloud.com/9890_4e292f9a3dd011e6b4078980237cc3d3.f20.mp4"; + String name = "普通"; + SwitchVideoModel switchVideoModel = new SwitchVideoModel(name, source1); + + String source2 = "http://9890.vod.myqcloud.com/9890_4e292f9a3dd011e6b4078980237cc3d3.f30.mp4"; + String name2 = "清晰"; + SwitchVideoModel switchVideoModel2 = new SwitchVideoModel(name2, source2); + + List list = new ArrayList<>(); + list.add(switchVideoModel); + list.add(switchVideoModel2); + + videoPlayer.setUp(list, false, "测试视频"); + + //增加封面 + ImageView imageView = new ImageView(this); + imageView.setScaleType(ImageView.ScaleType.CENTER_CROP); + imageView.setImageResource(R.mipmap.xxx1); + videoPlayer.setThumbImageView(imageView); + + //增加title + videoPlayer.getTitleTextView().setVisibility(View.VISIBLE); + + //设置返回键 + videoPlayer.getBackButton().setVisibility(View.VISIBLE); + + //设置旋转 + orientationUtils = new OrientationUtils(this, videoPlayer); + + //设置全屏按键功能,这是使用的是选择屏幕,而不是全屏 + videoPlayer.getFullscreenButton().setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + orientationUtils.resolveByClick(); + } + }); + + //是否可以滑动调整 + videoPlayer.setIsTouchWiget(true); + + //设置返回按键功能 + videoPlayer.getBackButton().setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + onBackPressed(); + } + }); + + //过渡动画 + initTransition(); + } + + + @Override + protected void onPause() { + super.onPause(); + videoPlayer.onVideoPause(); + } + + @Override + protected void onResume() { + super.onResume(); + videoPlayer.onVideoResume(); + } + + @TargetApi(Build.VERSION_CODES.KITKAT) + @Override + protected void onDestroy() { + super.onDestroy(); + if (orientationUtils != null) + orientationUtils.releaseListener(); + } + + @Override + public void onBackPressed() { + //先返回正常状态 + if (orientationUtils.getScreenType() == ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE) { + videoPlayer.getFullscreenButton().performClick(); + return; + } + //释放所有 + videoPlayer.setVideoAllCallBack(null); + GSYVideoManager.releaseAllVideos(); + if (isTransition && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + super.onBackPressed(); + } else { + new Handler().postDelayed(new Runnable() { + @Override + public void run() { + finish(); + overridePendingTransition(R.anim.abc_fade_in, R.anim.abc_fade_out); + } + }, 500); + } + } + + + private void initTransition() { + if (isTransition && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + postponeEnterTransition(); + ViewCompat.setTransitionName(videoPlayer, IMG_TRANSITION); + addTransitionListener(); + startPostponedEnterTransition(); + } else { + videoPlayer.startPlayLogic(); + } + } + + @TargetApi(Build.VERSION_CODES.LOLLIPOP) + private boolean addTransitionListener() { + transition = getWindow().getSharedElementEnterTransition(); + if (transition != null) { + transition.addListener(new OnTransitionListener(){ + @Override + public void onTransitionEnd(Transition transition) { + super.onTransitionEnd(transition); + videoPlayer.startPlayLogic(); + transition.removeListener(this); + } + }); + return true; + } + return false; + } + +} diff --git a/app/src/main/java/com/example/gsyvideoplayer/RecyclerView2Activity.java b/app/src/main/java/com/example/gsyvideoplayer/RecyclerView2Activity.java index 515b5f763..69988de33 100644 --- a/app/src/main/java/com/example/gsyvideoplayer/RecyclerView2Activity.java +++ b/app/src/main/java/com/example/gsyvideoplayer/RecyclerView2Activity.java @@ -3,35 +3,37 @@ import android.graphics.Point; import android.os.Build; import android.os.Bundle; -import android.support.v7.app.AppCompatActivity; -import android.support.v7.widget.LinearLayoutManager; -import android.support.v7.widget.RecyclerView; +import androidx.appcompat.app.AppCompatActivity; import android.transition.Explode; -import android.view.View; import android.view.Window; -import android.widget.AbsListView; import android.widget.FrameLayout; -import com.example.gsyvideoplayer.adapter.ListVideoAdapter; import com.example.gsyvideoplayer.adapter.RecyclerBaseAdapter; import com.example.gsyvideoplayer.holder.RecyclerItemViewHolder; -import com.example.gsyvideoplayer.listener.SampleListener; import com.example.gsyvideoplayer.model.VideoModel; -import com.shuyu.gsyvideoplayer.GSYVideoPlayer; +import com.shuyu.gsyvideoplayer.GSYVideoManager; +import com.shuyu.gsyvideoplayer.listener.GSYSampleCallBack; +import com.shuyu.gsyvideoplayer.utils.GSYVideoHelper; +import com.shuyu.gsyvideoplayer.video.NormalGSYVideoPlayer; import com.shuyu.gsyvideoplayer.utils.CommonUtil; import com.shuyu.gsyvideoplayer.utils.Debuger; -import com.shuyu.gsyvideoplayer.utils.ListVideoUtil; import java.util.ArrayList; import java.util.List; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; import butterknife.BindView; import butterknife.ButterKnife; +/** + * 小窗口 + */ public class RecyclerView2Activity extends AppCompatActivity { @BindView(R.id.list_item_recycler) RecyclerView listItemRecycler; + @BindView(R.id.video_full_container) FrameLayout videoFullContainer; @@ -41,8 +43,12 @@ public class RecyclerView2Activity extends AppCompatActivity { List dataList = new ArrayList<>(); - ListVideoUtil listVideoUtil; + GSYVideoHelper smallVideoHelper; + + GSYVideoHelper.GSYVideoHelperBuilder gsySmallVideoHelperBuilder; + int lastVisibleItem; + int firstVisibleItem; @Override @@ -60,13 +66,6 @@ protected void onCreate(Bundle savedInstanceState) { initView(); - listVideoUtil.setHideActionBar(true); - - //listVideoUtil.setShowFullAnimation(true); - //listVideoUtil.setAutoRotation(true); - //listVideoUtil.setFullLandFrist(true); - - listItemRecycler.addOnScrollListener(new RecyclerView.OnScrollListener() { @Override public void onScrollStateChanged(RecyclerView recyclerView, int newState) { @@ -80,59 +79,31 @@ public void onScrolled(RecyclerView recyclerView, int dx, int dy) { lastVisibleItem = linearLayoutManager.findLastVisibleItemPosition(); Debuger.printfLog("firstVisibleItem " + firstVisibleItem +" lastVisibleItem " + lastVisibleItem); //大于0说明有播放,//对应的播放列表TAG - if (listVideoUtil.getPlayPosition() >= 0 && listVideoUtil.getPlayTAG().equals(RecyclerItemViewHolder.TAG)) { + if (smallVideoHelper.getPlayPosition() >= 0 && smallVideoHelper.getPlayTAG().equals(RecyclerItemViewHolder.TAG)) { //当前播放的位置 - int position = listVideoUtil.getPlayPosition(); + int position = smallVideoHelper.getPlayPosition(); //不可视的是时候 if ((position < firstVisibleItem || position > lastVisibleItem)) { //如果是小窗口就不需要处理 - if (!listVideoUtil.isSmall() && !listVideoUtil.isFull()) { + if (!smallVideoHelper.isSmall() && !smallVideoHelper.isFull()) { //小窗口 int size = CommonUtil.dip2px(RecyclerView2Activity.this, 150); //actionbar为true才不会掉下面去 - listVideoUtil.showSmallVideo(new Point(size, size), true, true); + smallVideoHelper.showSmallVideo(new Point(size, size), true, true); } } else { - if (listVideoUtil.isSmall()) { - listVideoUtil.smallVideoToNormal(); + if (smallVideoHelper.isSmall()) { + smallVideoHelper.smallVideoToNormal(); } } } } }); - - //小窗口关闭被点击的时候回调处理回复页面 - listVideoUtil.setVideoAllCallBack(new SampleListener() { - @Override - public void onPrepared(String url, Object... objects) { - super.onPrepared(url, objects); - Debuger.printfLog("Duration " + listVideoUtil.getDuration() + " CurrentPosition " + listVideoUtil.getCurrentPositionWhenPlaying()); - } - - @Override - public void onQuitSmallWidget(String url, Object... objects) { - super.onQuitSmallWidget(url, objects); - //大于0说明有播放,//对应的播放列表TAG - if (listVideoUtil.getPlayPosition() >= 0 && listVideoUtil.getPlayTAG().equals(RecyclerItemViewHolder.TAG)) { - //当前播放的位置 - int position = listVideoUtil.getPlayPosition(); - //不可视的是时候 - if ((position < firstVisibleItem || position > lastVisibleItem)) { - //释放掉视频 - listVideoUtil.releaseVideoPlayer(); - recyclerBaseAdapter.notifyDataSetChanged(); - } - } - - } - }); - - } @Override public void onBackPressed() { - if (listVideoUtil.backFromFull()) { + if (smallVideoHelper.backFromFull()) { return; } super.onBackPressed(); @@ -142,8 +113,8 @@ public void onBackPressed() { @Override protected void onDestroy() { super.onDestroy(); - listVideoUtil.releaseVideoPlayer(); - GSYVideoPlayer.releaseAllVideos(); + smallVideoHelper.releaseVideoPlayer(); + GSYVideoManager.releaseAllVideos(); } private void initView() { @@ -156,10 +127,46 @@ private void initView() { listItemRecycler.setAdapter(recyclerBaseAdapter); - listVideoUtil = new ListVideoUtil(this); - listVideoUtil.setFullViewContainer(videoFullContainer); - listVideoUtil.setHideStatusBar(true); - recyclerBaseAdapter.setListVideoUtil(listVideoUtil); + smallVideoHelper = new GSYVideoHelper(this, new NormalGSYVideoPlayer(this)); + smallVideoHelper.setFullViewContainer(videoFullContainer); + + //配置 + gsySmallVideoHelperBuilder = new GSYVideoHelper.GSYVideoHelperBuilder(); + gsySmallVideoHelperBuilder + .setHideActionBar(true) + .setHideStatusBar(true) + .setNeedLockFull(true) + .setCacheWithPlay(true) + .setAutoFullWithSize(true) + .setShowFullAnimation(true) + .setLockLand(true).setVideoAllCallBack(new GSYSampleCallBack() { + @Override + public void onPrepared(String url, Object... objects) { + super.onPrepared(url, objects); + Debuger.printfLog("Duration " + smallVideoHelper.getGsyVideoPlayer().getDuration() + " CurrentPosition " + smallVideoHelper.getGsyVideoPlayer().getCurrentPositionWhenPlaying()); + } + + @Override + public void onQuitSmallWidget(String url, Object... objects) { + super.onQuitSmallWidget(url, objects); + //大于0说明有播放,//对应的播放列表TAG + if (smallVideoHelper.getPlayPosition() >= 0 && smallVideoHelper.getPlayTAG().equals(RecyclerItemViewHolder.TAG)) { + //当前播放的位置 + int position = smallVideoHelper.getPlayPosition(); + //不可视的是时候 + if ((position < firstVisibleItem || position > lastVisibleItem)) { + //释放掉视频 + smallVideoHelper.releaseVideoPlayer(); + recyclerBaseAdapter.notifyDataSetChanged(); + } + } + + } + }); + + smallVideoHelper.setGsyVideoOptionBuilder(gsySmallVideoHelperBuilder); + + recyclerBaseAdapter.setVideoHelper(smallVideoHelper, gsySmallVideoHelperBuilder); } diff --git a/app/src/main/java/com/example/gsyvideoplayer/RecyclerView3Activity.java b/app/src/main/java/com/example/gsyvideoplayer/RecyclerView3Activity.java new file mode 100644 index 000000000..50678939e --- /dev/null +++ b/app/src/main/java/com/example/gsyvideoplayer/RecyclerView3Activity.java @@ -0,0 +1,205 @@ +package com.example.gsyvideoplayer; + +import android.graphics.Point; +import android.os.Build; +import android.os.Bundle; +import android.transition.Explode; +import android.view.Window; +import android.widget.FrameLayout; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.appcompat.app.AppCompatActivity; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; + +import com.example.gsyvideoplayer.adapter.RecyclerBaseAdapter; +import com.example.gsyvideoplayer.holder.RecyclerItemViewHolder; +import com.example.gsyvideoplayer.mediacodec.MediaCodecVideo; +import com.example.gsyvideoplayer.model.VideoModel; +import com.shuyu.gsyvideoplayer.GSYVideoManager; +import com.shuyu.gsyvideoplayer.listener.GSYSampleCallBack; +import com.shuyu.gsyvideoplayer.utils.CommonUtil; +import com.shuyu.gsyvideoplayer.utils.Debuger; +import com.shuyu.gsyvideoplayer.utils.GSYVideoHelper; +import com.shuyu.gsyvideoplayer.utils.GSYVideoType; + +import java.util.ArrayList; +import java.util.List; + +import butterknife.BindView; +import butterknife.ButterKnife; + +/** + * MediaCodec + * + * + * + * 没有适配小窗口切换的,请悉知 + */ +public class RecyclerView3Activity extends AppCompatActivity { + + @BindView(R.id.list_item_recycler) + RecyclerView listItemRecycler; + + @BindView(R.id.video_full_container) + FrameLayout videoFullContainer; + + LinearLayoutManager linearLayoutManager; + + RecyclerBaseAdapter recyclerBaseAdapter; + + List dataList = new ArrayList<>(); + + GSYVideoHelper smallVideoHelper; + + GSYVideoHelper.GSYVideoHelperBuilder gsySmallVideoHelperBuilder; + + int lastVisibleItem; + + int firstVisibleItem; + + @Override + protected void onCreate(Bundle savedInstanceState) { + // 设置一个exit transition + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + getWindow().requestFeature(Window.FEATURE_CONTENT_TRANSITIONS); + getWindow().setEnterTransition(new Explode()); + getWindow().setExitTransition(new Explode()); + } + + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_recycler_view2); + ButterKnife.bind(this); + + initView(); + + listItemRecycler.addOnScrollListener(new RecyclerView.OnScrollListener() { + @Override + public void onScrollStateChanged(@NonNull RecyclerView recyclerView, int newState) { + super.onScrollStateChanged(recyclerView, newState); + } + + @Override + public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) { + super.onScrolled(recyclerView, dx, dy); + firstVisibleItem = linearLayoutManager.findFirstVisibleItemPosition(); + lastVisibleItem = linearLayoutManager.findLastVisibleItemPosition(); + Debuger.printfLog("firstVisibleItem " + firstVisibleItem +" lastVisibleItem " + lastVisibleItem); + //大于0说明有播放,//对应的播放列表TAG + if (smallVideoHelper.getPlayPosition() >= 0 && smallVideoHelper.getPlayTAG().equals(RecyclerItemViewHolder.TAG)) { + //当前播放的位置 + int position = smallVideoHelper.getPlayPosition(); + //不可视的是时候 + if ((position < firstVisibleItem || position > lastVisibleItem)) { + //如果是小窗口就不需要处理 + if (!smallVideoHelper.isSmall() && !smallVideoHelper.isFull()) { + //小窗口 + int size = CommonUtil.dip2px(RecyclerView3Activity.this, 150); + //actionbar为true才不会掉下面去 + smallVideoHelper.showSmallVideo(new Point(size, size), true, true); + } + } else { + if (smallVideoHelper.isSmall()) { + smallVideoHelper.smallVideoToNormal(); + } + } + } + } + }); + } + + @Override + protected void onStart() { + super.onStart(); + + /// 打开硬解码 + GSYVideoType.enableMediaCodec(); + } + + + @Override + protected void onDestroy() { + super.onDestroy(); + + /// 关闭硬解码 + GSYVideoType.disableMediaCodec(); + + + smallVideoHelper.releaseVideoPlayer(); + GSYVideoManager.releaseAllVideos(); + } + + @Override + public void onBackPressed() { + if (smallVideoHelper.backFromFull()) { + return; + } + super.onBackPressed(); + } + + + private void initView() { + linearLayoutManager = new LinearLayoutManager(this); + listItemRecycler.setLayoutManager(linearLayoutManager); + + resolveData(); + + recyclerBaseAdapter = new RecyclerBaseAdapter(this, dataList); + listItemRecycler.setAdapter(recyclerBaseAdapter); + + + smallVideoHelper = new GSYVideoHelper(this, new MediaCodecVideo(this)); + smallVideoHelper.setFullViewContainer(videoFullContainer); + + //配置 + gsySmallVideoHelperBuilder = new GSYVideoHelper.GSYVideoHelperBuilder(); + gsySmallVideoHelperBuilder + .setHideActionBar(true) + .setHideStatusBar(true) + .setNeedLockFull(true) + .setCacheWithPlay(true) + .setAutoFullWithSize(false) + .setShowFullAnimation(false) + .setLockLand(true).setVideoAllCallBack(new GSYSampleCallBack() { + @Override + public void onPrepared(String url, Object... objects) { + super.onPrepared(url, objects); + Debuger.printfLog("Duration " + smallVideoHelper.getGsyVideoPlayer().getDuration() + " CurrentPosition " + smallVideoHelper.getGsyVideoPlayer().getCurrentPositionWhenPlaying()); + } + + @Override + public void onQuitSmallWidget(String url, Object... objects) { + super.onQuitSmallWidget(url, objects); + //大于0说明有播放,//对应的播放列表TAG + if (smallVideoHelper.getPlayPosition() >= 0 && smallVideoHelper.getPlayTAG().equals(RecyclerItemViewHolder.TAG)) { + //当前播放的位置 + int position = smallVideoHelper.getPlayPosition(); + //不可视的是时候 + if ((position < firstVisibleItem || position > lastVisibleItem)) { + //释放掉视频 + smallVideoHelper.releaseVideoPlayer(); + recyclerBaseAdapter.notifyDataSetChanged(); + } + } + + } + }); + + smallVideoHelper.setGsyVideoOptionBuilder(gsySmallVideoHelperBuilder); + + recyclerBaseAdapter.setVideoHelper(smallVideoHelper, gsySmallVideoHelperBuilder); + + } + + private void resolveData() { + for (int i = 0; i < 19; i++) { + VideoModel videoModel = new VideoModel(); + dataList.add(videoModel); + } + if (recyclerBaseAdapter != null) + recyclerBaseAdapter.notifyDataSetChanged(); + } + + +} diff --git a/app/src/main/java/com/example/gsyvideoplayer/RecyclerViewActivity.java b/app/src/main/java/com/example/gsyvideoplayer/RecyclerViewActivity.java index d6c35ad95..02c110f1f 100644 --- a/app/src/main/java/com/example/gsyvideoplayer/RecyclerViewActivity.java +++ b/app/src/main/java/com/example/gsyvideoplayer/RecyclerViewActivity.java @@ -1,30 +1,24 @@ package com.example.gsyvideoplayer; -import android.content.pm.ActivityInfo; -import android.content.res.Configuration; import android.os.Build; -import android.support.v7.app.AppCompatActivity; +import androidx.appcompat.app.AppCompatActivity; import android.os.Bundle; -import android.support.v7.widget.LinearLayoutManager; -import android.support.v7.widget.RecyclerView; import android.transition.Explode; import android.view.Window; -import com.example.gsyvideoplayer.adapter.ListNormalAdapter; import com.example.gsyvideoplayer.adapter.RecyclerBaseAdapter; import com.example.gsyvideoplayer.adapter.RecyclerNormalAdapter; import com.example.gsyvideoplayer.holder.RecyclerItemNormalHolder; import com.example.gsyvideoplayer.model.VideoModel; import com.shuyu.gsyvideoplayer.GSYVideoManager; -import com.shuyu.gsyvideoplayer.GSYVideoPlayer; -import com.shuyu.gsyvideoplayer.utils.Debuger; -import com.shuyu.gsyvideoplayer.video.StandardGSYVideoPlayer; import java.util.ArrayList; import java.util.List; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; import butterknife.BindView; import butterknife.ButterKnife; @@ -40,7 +34,6 @@ public class RecyclerViewActivity extends AppCompatActivity { List dataList = new ArrayList<>(); - boolean mFull = false; @Override protected void onCreate(Bundle savedInstanceState) { @@ -85,8 +78,8 @@ public void onScrolled(RecyclerView recyclerView, int dx, int dy) { //如果滑出去了上面和下面就是否,和今日头条一样 //是否全屏 - if(!mFull) { - GSYVideoPlayer.releaseAllVideos(); + if(!GSYVideoManager.isFullState(RecyclerViewActivity.this)) { + GSYVideoManager.releaseAllVideos(); recyclerNormalAdapter.notifyDataSetChanged(); } } @@ -96,21 +89,9 @@ public void onScrolled(RecyclerView recyclerView, int dx, int dy) { } - @Override - public void onConfigurationChanged(Configuration newConfig) { - super.onConfigurationChanged(newConfig); - //如果旋转了就全屏 - if (newConfig.orientation != ActivityInfo.SCREEN_ORIENTATION_USER) { - mFull = false; - } else { - mFull = true; - } - - } - @Override public void onBackPressed() { - if (StandardGSYVideoPlayer.backFromWindowFull(this)) { + if (GSYVideoManager.backFromWindowFull(this)) { return; } super.onBackPressed(); @@ -125,13 +106,13 @@ protected void onPause() { @Override protected void onResume() { super.onResume(); - GSYVideoManager.onResume(); + GSYVideoManager.onResume(false); } @Override protected void onDestroy() { super.onDestroy(); - GSYVideoPlayer.releaseAllVideos(); + GSYVideoManager.releaseAllVideos(); } diff --git a/app/src/main/java/com/example/gsyvideoplayer/ScrollingActivity.java b/app/src/main/java/com/example/gsyvideoplayer/ScrollingActivity.java new file mode 100644 index 000000000..d4b2c82b1 --- /dev/null +++ b/app/src/main/java/com/example/gsyvideoplayer/ScrollingActivity.java @@ -0,0 +1,276 @@ +package com.example.gsyvideoplayer; + +import android.content.res.Configuration; +import android.graphics.Point; +import android.os.Bundle; +import android.view.View; +import android.widget.ImageView; + +import com.example.gsyvideoplayer.listener.AppBarStateChangeListener; +import com.example.gsyvideoplayer.video.LandLayoutVideo; +import com.google.android.material.appbar.AppBarLayout; +import com.google.android.material.appbar.CollapsingToolbarLayout; +import com.google.android.material.floatingactionbutton.FloatingActionButton; +import com.shuyu.gsyvideoplayer.GSYVideoManager; +import com.shuyu.gsyvideoplayer.builder.GSYVideoOptionBuilder; +import com.shuyu.gsyvideoplayer.listener.GSYSampleCallBack; +import com.shuyu.gsyvideoplayer.listener.GSYVideoProgressListener; +import com.shuyu.gsyvideoplayer.listener.LockClickListener; +import com.shuyu.gsyvideoplayer.utils.CommonUtil; +import com.shuyu.gsyvideoplayer.utils.Debuger; +import com.shuyu.gsyvideoplayer.utils.OrientationUtils; +import com.shuyu.gsyvideoplayer.video.base.GSYVideoPlayer; + +import androidx.appcompat.app.AppCompatActivity; +import androidx.appcompat.widget.Toolbar; +import androidx.coordinatorlayout.widget.CoordinatorLayout; + +/** + * CollapsingToolbarLayout的播放页面 + * 额,有点懒,细节上没处理 + */ +public class ScrollingActivity extends AppCompatActivity { + + private boolean isPlay; + private boolean isPause; + private boolean isSamll; + + private OrientationUtils orientationUtils; + private LandLayoutVideo detailPlayer; + private AppBarLayout appBar; + private FloatingActionButton fab; + private CoordinatorLayout root; + + private CollapsingToolbarLayout toolBarLayout; + + private AppBarStateChangeListener.State curState; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_scrolling); + + initView(); + + String url = "http://9890.vod.myqcloud.com/9890_4e292f9a3dd011e6b4078980237cc3d3.f20.mp4"; + + //增加封面 + ImageView imageView = new ImageView(this); + imageView.setScaleType(ImageView.ScaleType.CENTER_CROP); + imageView.setImageResource(R.mipmap.xxx1); + + resolveNormalVideoUI(); + + //外部辅助的旋转,帮助全屏 + orientationUtils = new OrientationUtils(this, detailPlayer); + //初始化不打开外部的旋转 + orientationUtils.setEnable(false); + + GSYVideoOptionBuilder gsyVideoOption = new GSYVideoOptionBuilder(); + gsyVideoOption.setThumbImageView(imageView) + .setIsTouchWiget(true) + .setRotateViewAuto(false) + .setLockLand(false) + .setShowFullAnimation(false) + .setNeedLockFull(true) + .setSeekRatio(1) + .setUrl(url) + .setCacheWithPlay(false) + .setVideoTitle("测试视频") + .setVideoAllCallBack(new GSYSampleCallBack() { + + @Override + public void onPrepared(String url, Object... objects) { + Debuger.printfError("***** onPrepared **** " + objects[0]); + Debuger.printfError("***** onPrepared **** " + objects[1]); + super.onPrepared(url, objects); + //开始播放了才能旋转和全屏 + orientationUtils.setEnable(true); + isPlay = true; + root.removeView(fab); + } + + @Override + public void onEnterFullscreen(String url, Object... objects) { + super.onEnterFullscreen(url, objects); + Debuger.printfError("***** onEnterFullscreen **** " + objects[0]);//title + Debuger.printfError("***** onEnterFullscreen **** " + objects[1]);//当前全屏player + } + + @Override + public void onAutoComplete(String url, Object... objects) { + super.onAutoComplete(url, objects); + } + + @Override + public void onClickStartError(String url, Object... objects) { + super.onClickStartError(url, objects); + } + + @Override + public void onQuitFullscreen(String url, Object... objects) { + super.onQuitFullscreen(url, objects); + Debuger.printfError("***** onQuitFullscreen **** " + objects[0]);//title + Debuger.printfError("***** onQuitFullscreen **** " + objects[1]);//当前非全屏player + if (orientationUtils != null) { + orientationUtils.backToProtVideo(); + } + } + }) + .setLockClickListener(new LockClickListener() { + @Override + public void onClick(View view, boolean lock) { + if (orientationUtils != null) { + //配合下方的onConfigurationChanged + orientationUtils.setEnable(!lock); + } + } + }) + .setGSYVideoProgressListener(new GSYVideoProgressListener() { + @Override + public void onProgress(int progress, int secProgress, int currentPosition, int duration) { + Debuger.printfLog(" progress " + progress + " secProgress " + secProgress + " currentPosition " + currentPosition + " duration " + duration); + } + }) + .build(detailPlayer); + + detailPlayer.getFullscreenButton().setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + //直接横屏 + orientationUtils.resolveByClick(); + + //第一个true是否需要隐藏actionbar,第二个true是否需要隐藏statusbar + detailPlayer.startWindowFullscreen(ScrollingActivity.this, true, true); + } + }); + + detailPlayer.setLinkScroll(true); + } + + @Override + public void onBackPressed() { + + if (orientationUtils != null) { + orientationUtils.backToProtVideo(); + } + + if (GSYVideoManager.backFromWindowFull(this)) { + return; + } + super.onBackPressed(); + } + + + @Override + protected void onPause() { + getCurPlay().onVideoPause(); + super.onPause(); + isPause = true; + } + + @Override + protected void onResume() { + getCurPlay().onVideoResume(); + appBar.addOnOffsetChangedListener(appBarStateChangeListener); + super.onResume(); + isPause = false; + } + + @Override + protected void onDestroy() { + super.onDestroy(); + if (isPlay) { + getCurPlay().release(); + } + if (orientationUtils != null) + orientationUtils.releaseListener(); + } + + + @Override + public void onConfigurationChanged(Configuration newConfig) { + super.onConfigurationChanged(newConfig); + //如果旋转了就全屏 + if (isPlay && !isPause) { + detailPlayer.onConfigurationChanged(this, newConfig, orientationUtils, true, true); + } + } + + private void initView() { + Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); + detailPlayer = (LandLayoutVideo) findViewById(R.id.detail_player); + root = (CoordinatorLayout) findViewById(R.id.root_layout); + + setSupportActionBar(toolbar); + toolBarLayout = (CollapsingToolbarLayout) findViewById(R.id.toolbar_layout); + toolBarLayout.setTitle(getTitle()); + + fab = (FloatingActionButton) findViewById(R.id.fab); + fab.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + detailPlayer.startPlayLogic(); + root.removeView(fab); + } + }); + + appBar = (AppBarLayout) findViewById(R.id.app_bar); + appBar.addOnOffsetChangedListener(appBarStateChangeListener); + } + + private void resolveNormalVideoUI() { + //增加title + detailPlayer.getTitleTextView().setVisibility(View.GONE); + detailPlayer.getBackButton().setVisibility(View.GONE); + } + + private GSYVideoPlayer getCurPlay() { + if (detailPlayer.getFullWindowPlayer() != null) { + return detailPlayer.getFullWindowPlayer(); + } + return detailPlayer; + } + + AppBarStateChangeListener appBarStateChangeListener = new AppBarStateChangeListener() { + @Override + public void onStateChanged(AppBarLayout appBarLayout, AppBarStateChangeListener.State + state) { + if (state == AppBarStateChangeListener.State.EXPANDED) { + //展开状态 + curState = state; + toolBarLayout.setTitle(""); + } else if (state == AppBarStateChangeListener.State.COLLAPSED) { + //折叠状态 + //如果是小窗口就不需要处理 + toolBarLayout.setTitle("Title"); + if (!isSamll && isPlay) { + isSamll = true; + int size = CommonUtil.dip2px(ScrollingActivity.this, 150); + detailPlayer.showSmallVideo(new Point(size, size), true, true); + orientationUtils.setEnable(false); + } + curState = state; + } else { + if (curState == AppBarStateChangeListener.State.COLLAPSED) { + //由折叠变为中间状态 + toolBarLayout.setTitle(""); + if (isSamll) { + isSamll = false; + orientationUtils.setEnable(true); + //必须 + detailPlayer.postDelayed(new Runnable() { + @Override + public void run() { + detailPlayer.hideSmallVideo(); + } + }, 50); + } + } + curState = state; + //中间状态 + } + } + }; + +} diff --git a/app/src/main/java/com/example/gsyvideoplayer/WebDetailActivity.java b/app/src/main/java/com/example/gsyvideoplayer/WebDetailActivity.java index 652933ec1..84568e2fd 100644 --- a/app/src/main/java/com/example/gsyvideoplayer/WebDetailActivity.java +++ b/app/src/main/java/com/example/gsyvideoplayer/WebDetailActivity.java @@ -1,24 +1,23 @@ package com.example.gsyvideoplayer; -import android.content.pm.ActivityInfo; -import android.content.res.Configuration; import android.graphics.Point; import android.os.Bundle; -import android.support.v4.widget.NestedScrollView; -import android.support.v7.app.AppCompatActivity; +import androidx.core.widget.NestedScrollView; import android.view.View; import android.webkit.WebSettings; import android.widget.ImageView; import android.widget.RelativeLayout; -import com.example.gsyvideoplayer.listener.SampleListener; +import com.bumptech.glide.Glide; +import com.bumptech.glide.request.RequestOptions; +import com.example.gsyvideoplayer.video.PreViewGSYVideoPlayer; import com.example.gsyvideoplayer.view.ScrollWebView; -import com.shuyu.gsyvideoplayer.GSYVideoPlayer; +import com.shuyu.gsyvideoplayer.GSYBaseActivityDetail; +import com.shuyu.gsyvideoplayer.builder.GSYVideoOptionBuilder; +import com.shuyu.gsyvideoplayer.utils.GSYVideoType; +import com.shuyu.gsyvideoplayer.video.StandardGSYVideoPlayer; import com.shuyu.gsyvideoplayer.listener.LockClickListener; import com.shuyu.gsyvideoplayer.utils.CommonUtil; -import com.shuyu.gsyvideoplayer.utils.OrientationUtils; -import com.shuyu.gsyvideoplayer.video.NormalGSYVideoPlayer; -import com.shuyu.gsyvideoplayer.video.StandardGSYVideoPlayer; import butterknife.BindView; import butterknife.ButterKnife; @@ -27,22 +26,20 @@ * Created by shuyu on 2016/12/26. */ -public class WebDetailActivity extends AppCompatActivity { +public class WebDetailActivity extends GSYBaseActivityDetail { @BindView(R.id.scroll_webView) ScrollWebView webView; @BindView(R.id.web_player) - NormalGSYVideoPlayer webPlayer; + PreViewGSYVideoPlayer webPlayer; @BindView(R.id.web_top_layout) NestedScrollView webTopLayout; @BindView(R.id.web_top_layout_video) RelativeLayout webTopLayoutVideo; - private boolean isPlay; - private boolean isPause; - private boolean isSamll; + private boolean isSmall; - private OrientationUtils orientationUtils; + private int backupRendType; @Override protected void onCreate(Bundle savedInstanceState) { @@ -50,68 +47,15 @@ protected void onCreate(Bundle savedInstanceState) { setContentView(R.layout.activity_web_detail); ButterKnife.bind(this); - String url = "http://baobab.wdjcdn.com/14564977406580.mp4"; - //String url = "https://d131x7vzzf85jg.cloudfront.net/upload/documents/paper/b2/61/00/00/20160420_115018_b544.mp4"; - webPlayer.setUp(url, false, null, "测试视频"); + backupRendType = GSYVideoType.getRenderType(); - //增加封面 - ImageView imageView = new ImageView(this); - imageView.setScaleType(ImageView.ScaleType.CENTER_CROP); - imageView.setImageResource(R.mipmap.xxx1); - webPlayer.setThumbImageView(imageView); + //设置为Surface播放模式,注意此设置是全局的 + GSYVideoType.setRenderType(GSYVideoType.SUFRACE); resolveNormalVideoUI(); - //外部辅助的旋转,帮助全屏 - orientationUtils = new OrientationUtils(this, webPlayer); - //初始化不打开外部的旋转 - orientationUtils.setEnable(false); - - webPlayer.setIsTouchWiget(true); - //关闭自动旋转 - webPlayer.setRotateViewAuto(false); - webPlayer.setLockLand(false); - webPlayer.setShowFullAnimation(false); - webPlayer.setNeedLockFull(true); - //detailPlayer.setOpenPreView(true); - webPlayer.getFullscreenButton().setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - //直接横屏 - orientationUtils.resolveByClick(); - - //第一个true是否需要隐藏actionbar,第二个true是否需要隐藏statusbar - webPlayer.startWindowFullscreen(WebDetailActivity.this, true, true); - } - }); - - webPlayer.setStandardVideoAllCallBack(new SampleListener() { - @Override - public void onPrepared(String url, Object... objects) { - super.onPrepared(url, objects); - //开始播放了才能旋转和全屏 - orientationUtils.setEnable(true); - isPlay = true; - } - - @Override - public void onAutoComplete(String url, Object... objects) { - super.onAutoComplete(url, objects); - } - - @Override - public void onClickStartError(String url, Object... objects) { - super.onClickStartError(url, objects); - } + initVideoBuilderMode(); - @Override - public void onQuitFullscreen(String url, Object... objects) { - super.onQuitFullscreen(url, objects); - if (orientationUtils != null) { - orientationUtils.backToProtVideo(); - } - } - }); webPlayer.setLockClickListener(new LockClickListener() { @Override @@ -119,6 +63,7 @@ public void onClick(View view, boolean lock) { if (orientationUtils != null) { //配合下方的onConfigurationChanged orientationUtils.setEnable(!lock); + webPlayer.getCurrentPlayer().setRotateViewAuto(!lock); } } }); @@ -128,21 +73,24 @@ public void onClick(View view, boolean lock) { settings.setJavaScriptEnabled(true); webView.loadUrl("https://www.baidu.com"); + + orientationUtils.setRotateWithSystem(false); + webTopLayout.setOnScrollChangeListener(new NestedScrollView.OnScrollChangeListener() { @Override public void onScrollChange(NestedScrollView v, int scrollX, int scrollY, int oldScrollX, int oldScrollY) { if (!webPlayer.isIfCurrentIsFullscreen() && scrollY >= 0 && isPlay) { if (scrollY > webPlayer.getHeight()) { //如果是小窗口就不需要处理 - if (!isSamll) { - isSamll = true; + if (!isSmall) { + isSmall = true; int size = CommonUtil.dip2px(WebDetailActivity.this, 150); webPlayer.showSmallVideo(new Point(size, size), true, true); orientationUtils.setEnable(false); } } else { - if (isSamll) { - isSamll = false; + if (isSmall) { + isSmall = false; orientationUtils.setEnable(true); //必须 webTopLayoutVideo.postDelayed(new Runnable() { @@ -161,65 +109,71 @@ public void run() { } @Override - public void onBackPressed() { - - if (orientationUtils != null) { - orientationUtils.backToProtVideo(); - } - - if (StandardGSYVideoPlayer.backFromWindowFull(this)) { - return; - } - super.onBackPressed(); + protected void onDestroy() { + super.onDestroy(); + //设置为GL播放模式,才能支持滤镜,注意此设置是全局的 + GSYVideoType.setRenderType(backupRendType); } - @Override - protected void onPause() { - super.onPause(); - isPause = true; + public StandardGSYVideoPlayer getGSYVideoPlayer() { + return webPlayer; } @Override - protected void onResume() { - super.onResume(); - isPause = false; + public GSYVideoOptionBuilder getGSYVideoOptionBuilder() { + String url = "http://9890.vod.myqcloud.com/9890_4e292f9a3dd011e6b4078980237cc3d3.f20.mp4"; + //String url = "https://d131x7vzzf85jg.cloudfront.net/upload/documents/paper/b2/61/00/00/20160420_115018_b544.mp4"; + //增加封面。内置封面可参考SampleCoverVideo + ImageView imageView = new ImageView(this); + loadCover(imageView, url); + return new GSYVideoOptionBuilder() + .setThumbImageView(imageView) + .setUrl(url) + .setCacheWithPlay(false) + .setRotateWithSystem(false) + .setVideoTitle("测试视频") + .setIsTouchWiget(true) + .setRotateViewAuto(false) + .setLockLand(false) + .setShowFullAnimation(false) + .setNeedLockFull(true); } @Override - protected void onDestroy() { - super.onDestroy(); - GSYVideoPlayer.releaseAllVideos(); - if (orientationUtils != null) - orientationUtils.releaseListener(); + public void clickForFullScreen() { + } + /** + * 是否启动旋转横屏,true表示启动 + * @return true + */ @Override - public void onConfigurationChanged(Configuration newConfig) { - super.onConfigurationChanged(newConfig); - //如果旋转了就全屏 - if (isPlay && !isPause && !isSamll) { - if (newConfig.orientation == ActivityInfo.SCREEN_ORIENTATION_USER) { - if (!webPlayer.isIfCurrentIsFullscreen()) { - webPlayer.startWindowFullscreen(WebDetailActivity.this, true, true); - } - } else { - //新版本isIfCurrentIsFullscreen的标志位内部提前设置了,所以不会和手动点击冲突 - if (webPlayer.isIfCurrentIsFullscreen()) { - StandardGSYVideoPlayer.backFromWindowFull(this); - } - if (orientationUtils != null) { - orientationUtils.setEnable(true); - } - } - } + public boolean getDetailOrientationRotateAuto() { + return true; + } + + private void loadCover(ImageView imageView, String url) { + + imageView.setScaleType(ImageView.ScaleType.CENTER_CROP); + imageView.setImageResource(R.mipmap.xxx1); + + Glide.with(this.getApplicationContext()) + .setDefaultRequestOptions( + new RequestOptions() + .frame(3000000) + .centerCrop() + .error(R.mipmap.xxx2) + .placeholder(R.mipmap.xxx1)) + .load(url) + .into(imageView); } private void resolveNormalVideoUI() { //增加title webPlayer.getTitleTextView().setVisibility(View.GONE); - webPlayer.getTitleTextView().setText("测试视频"); webPlayer.getBackButton().setVisibility(View.GONE); } } diff --git a/app/src/main/java/com/example/gsyvideoplayer/WindowActivity.java b/app/src/main/java/com/example/gsyvideoplayer/WindowActivity.java new file mode 100644 index 000000000..ecbccffab --- /dev/null +++ b/app/src/main/java/com/example/gsyvideoplayer/WindowActivity.java @@ -0,0 +1,105 @@ +package com.example.gsyvideoplayer; + +import android.content.Intent; +import android.net.Uri; +import android.os.Build; +import android.os.Bundle; +import android.provider.Settings; +import androidx.annotation.RequiresApi; +import androidx.appcompat.app.AppCompatActivity; +import android.view.View; +import android.view.animation.BounceInterpolator; +import android.widget.Button; + + +import com.example.gsyvideoplayer.utils.floatUtil.FloatWindow; +import com.example.gsyvideoplayer.utils.floatUtil.MoveType; +import com.example.gsyvideoplayer.utils.floatUtil.Screen; +import com.example.gsyvideoplayer.utils.floatUtil.Util; +import com.example.gsyvideoplayer.view.FloatPlayerView; +import com.shuyu.gsyvideoplayer.GSYVideoManager; + +import butterknife.BindView; +import butterknife.ButterKnife; +import butterknife.OnClick; + +/** + * 多窗体下的悬浮窗页面 + */ +public class WindowActivity extends AppCompatActivity { + + @BindView(R.id.start_window) + Button startWindow; + @BindView(R.id.jump_other) + Button jumpOther; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_window); + ButterKnife.bind(this); + if (Build.VERSION.SDK_INT >= 23) { + if (!Util.hasPermission(this)) { + requestAlertWindowPermission(); + } + } + } + + @Override + protected void onDestroy() { + super.onDestroy(); + GSYVideoManager.instance().releaseMediaPlayer(); + /** + * 这里在返回主页的时候销毁了,因为不想和DEMO中其他页面冲突 + */ + FloatWindow.destroy(); + } + + @RequiresApi(api = 23) + private void requestAlertWindowPermission() { + Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, Uri.parse("package:" + getPackageName())); + startActivityForResult(intent, 1); + } + + + @RequiresApi(api = 23) + @Override + protected void onActivityResult(int requestCode, int resultCode, Intent data) { + super.onActivityResult(requestCode, resultCode, data); + if (Build.VERSION.SDK_INT >= 23){ + //todo 用23以上编译即可出现canDrawOverlays + if (Util.hasPermission(this)) { + + } else { + this.finish(); + } + } + } + + @OnClick({R.id.start_window, R.id.jump_other}) + public void onViewClicked(View view) { + switch (view.getId()) { + case R.id.start_window: + if (FloatWindow.get() != null) { + return; + } + FloatPlayerView floatPlayerView = new FloatPlayerView(getApplicationContext()); + FloatWindow + .with(getApplicationContext()) + .setView(floatPlayerView) + .setWidth(Screen.width, 0.4f) + .setHeight(Screen.width, 0.4f) + .setX(Screen.width, 0.8f) + .setY(Screen.height, 0.3f) + .setMoveType(MoveType.slide) + .setFilter(false) + .setMoveStyle(500, new BounceInterpolator()) + .build(); + FloatWindow.get().show(); + break; + case R.id.jump_other: + startActivity(new Intent(this, EmptyActivity.class)); + break; + } + } +} diff --git a/app/src/main/java/com/example/gsyvideoplayer/adapter/ListMultiNormalAdapter.java b/app/src/main/java/com/example/gsyvideoplayer/adapter/ListMultiNormalAdapter.java new file mode 100644 index 000000000..f6ecf833b --- /dev/null +++ b/app/src/main/java/com/example/gsyvideoplayer/adapter/ListMultiNormalAdapter.java @@ -0,0 +1,151 @@ +package com.example.gsyvideoplayer.adapter; + +import android.content.Context; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.BaseAdapter; + +import com.example.gsyvideoplayer.R; +import com.example.gsyvideoplayer.model.VideoModel; +import com.example.gsyvideoplayer.video.MultiSampleVideo; +import com.shuyu.gsyvideoplayer.listener.GSYSampleCallBack; +import com.shuyu.gsyvideoplayer.video.StandardGSYVideoPlayer; + +import java.util.ArrayList; +import java.util.List; + +/** + * 多个播放的listview adapter + * Created by shuyu on 2016/11/12. + */ + +public class ListMultiNormalAdapter extends BaseAdapter { + + public static final String TAG = "ListMultiNormalAdapter"; + + private List list = new ArrayList<>(); + private LayoutInflater inflater = null; + private Context context; + + private String fullKey = "null"; + + public ListMultiNormalAdapter(Context context) { + super(); + this.context = context; + inflater = LayoutInflater.from(context); + for (int i = 0; i < 40; i++) { + list.add(new VideoModel()); + } + + } + + @Override + public int getCount() { + return list.size(); + } + + @Override + public Object getItem(int position) { + return null; + } + + @Override + public long getItemId(int position) { + return 0; + } + + @Override + public View getView(final int position, View convertView, ViewGroup parent) { + final ViewHolder holder; + if (convertView == null) { + holder = new ViewHolder(); + convertView = inflater.inflate(R.layout.list_video_item_mutli, null); + holder.gsyVideoPlayer = (MultiSampleVideo) convertView.findViewById(R.id.video_item_player); + convertView.setTag(holder); + } else { + holder = (ViewHolder) convertView.getTag(); + } + + + final String url = "http://9890.vod.myqcloud.com/9890_4e292f9a3dd011e6b4078980237cc3d3.f20.mp4"; + + //多个播放时必须在setUpLazy、setUp和getGSYVideoManager()等前面设置 + holder.gsyVideoPlayer.setPlayTag(TAG); + holder.gsyVideoPlayer.setPlayPosition(position); + + boolean isPlaying = holder.gsyVideoPlayer.getCurrentPlayer().isInPlayingState(); + + if (!isPlaying) { + holder.gsyVideoPlayer.setUpLazy(url, false, null, null, "这是title"); + } + + //增加title + holder.gsyVideoPlayer.getTitleTextView().setVisibility(View.GONE); + + //设置返回键 + holder.gsyVideoPlayer.getBackButton().setVisibility(View.GONE); + + //设置全屏按键功能 + holder.gsyVideoPlayer.getFullscreenButton().setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + resolveFullBtn(holder.gsyVideoPlayer); + } + }); + holder.gsyVideoPlayer.setRotateViewAuto(true); + holder.gsyVideoPlayer.setLockLand(true); + holder.gsyVideoPlayer.setReleaseWhenLossAudio(false); + holder.gsyVideoPlayer.setShowFullAnimation(true); + holder.gsyVideoPlayer.setIsTouchWiget(false); + + holder.gsyVideoPlayer.setNeedLockFull(true); + + if (position % 2 == 0) { + holder.gsyVideoPlayer.loadCoverImage(url, R.mipmap.xxx1); + } else { + holder.gsyVideoPlayer.loadCoverImage(url, R.mipmap.xxx2); + } + + holder.gsyVideoPlayer.setVideoAllCallBack(new GSYSampleCallBack() { + + + @Override + public void onQuitFullscreen(String url, Object... objects) { + super.onQuitFullscreen(url, objects); + fullKey = "null"; + } + + @Override + public void onEnterFullscreen(String url, Object... objects) { + super.onEnterFullscreen(url, objects); + holder.gsyVideoPlayer.getCurrentPlayer().getTitleTextView().setText((String) objects[0]); + fullKey = holder.gsyVideoPlayer.getKey(); + } + + @Override + public void onAutoComplete(String url, Object... objects) { + super.onAutoComplete(url, objects); + } + }); + + return convertView; + } + + /** + * 全屏幕按键处理 + */ + private void resolveFullBtn(final StandardGSYVideoPlayer standardGSYVideoPlayer) { + standardGSYVideoPlayer.startWindowFullscreen(context, false, true); + } + + class ViewHolder { + MultiSampleVideo gsyVideoPlayer; + } + + + public String getFullKey() { + return fullKey; + } + +} diff --git a/app/src/main/java/com/example/gsyvideoplayer/adapter/ListNormalAdapter.java b/app/src/main/java/com/example/gsyvideoplayer/adapter/ListNormalAdapter.java index 6bce2003c..b13e794fa 100644 --- a/app/src/main/java/com/example/gsyvideoplayer/adapter/ListNormalAdapter.java +++ b/app/src/main/java/com/example/gsyvideoplayer/adapter/ListNormalAdapter.java @@ -1,28 +1,27 @@ package com.example.gsyvideoplayer.adapter; +import android.app.Activity; import android.content.Context; +import android.content.res.Configuration; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.BaseAdapter; -import android.widget.ImageView; import com.example.gsyvideoplayer.R; -import com.example.gsyvideoplayer.listener.SampleListener; import com.example.gsyvideoplayer.model.VideoModel; +import com.example.gsyvideoplayer.video.SampleCoverVideo; import com.shuyu.gsyvideoplayer.GSYVideoManager; -import com.shuyu.gsyvideoplayer.GSYVideoPlayer; -import com.shuyu.gsyvideoplayer.listener.StandardVideoAllCallBack; +import com.shuyu.gsyvideoplayer.listener.GSYSampleCallBack; import com.shuyu.gsyvideoplayer.utils.Debuger; import com.shuyu.gsyvideoplayer.utils.FileUtils; +import com.shuyu.gsyvideoplayer.utils.OrientationUtils; import com.shuyu.gsyvideoplayer.video.StandardGSYVideoPlayer; import java.io.File; import java.util.ArrayList; import java.util.List; -import static com.shuyu.gsyvideoplayer.GSYVideoPlayer.CURRENT_STATE_NORMAL; - /** * Created by shuyu on 2016/11/12. */ @@ -35,7 +34,14 @@ public class ListNormalAdapter extends BaseAdapter { private LayoutInflater inflater = null; private Context context; - private boolean isFullVideo; + private StandardGSYVideoPlayer curPlayer; + private StandardGSYVideoPlayer itemPlayer; + + protected OrientationUtils orientationUtils; + + protected boolean isPlay; + + protected boolean isFull; public ListNormalAdapter(Context context) { super(); @@ -68,38 +74,38 @@ public View getView(final int position, View convertView, ViewGroup parent) { if (convertView == null) { holder = new ViewHolder(); convertView = inflater.inflate(R.layout.list_video_item_normal, null); - holder.gsyVideoPlayer = (StandardGSYVideoPlayer) convertView.findViewById(R.id.video_item_player); - holder.imageView = new ImageView(context); + holder.gsyVideoPlayer = (SampleCoverVideo) convertView.findViewById(R.id.video_item_player); convertView.setTag(holder); } else { holder = (ViewHolder) convertView.getTag(); } - //增加封面 - holder.imageView.setScaleType(ImageView.ScaleType.CENTER_CROP); + + //final String url = "https://res.exexm.com/cw_145225549855002"; + final String urlH = "http://9890.vod.myqcloud.com/9890_4e292f9a3dd011e6b4078980237cc3d3.f20.mp4"; + final String urlV = "http://7xjmzj.com1.z0.glb.clouddn.com/20171026175005_JObCxCE2.mp4"; + final String url = (position % 2 == 0) ? urlH : urlV; + //final String url = "http://111.198.24.133:83/yyy_login_server/pic/YB059284/97778276040859/1.mp4"; + + if (position % 2 == 0) { - holder.imageView.setImageResource(R.mipmap.xxx1); + holder.gsyVideoPlayer.loadCoverImage(url, R.mipmap.xxx1); } else { - holder.imageView.setImageResource(R.mipmap.xxx2); + holder.gsyVideoPlayer.loadCoverImage(url, R.mipmap.xxx2); } - if (holder.imageView.getParent() != null) { - ViewGroup viewGroup = (ViewGroup) holder.imageView.getParent(); - viewGroup.removeView(holder.imageView); - } - holder.gsyVideoPlayer.setThumbImageView(holder.imageView); - final String url = "http://baobab.wdjcdn.com/14564977406580.mp4"; - //final String url = "http://7xse1z.com1.z0.glb.clouddn.com/1491813192"; - //final String url = "http://111.198.24.133:83/yyy_login_server/pic/YB059284/97778276040859/1.mp4"; + //防止错位,离开释放 + //holder.gsyVideoPlayer.initUIState(); //默认缓存路径 - holder.gsyVideoPlayer.setUp(url, true , null, "这是title"); + //使用lazy的set可以避免滑动卡的情况存在 + holder.gsyVideoPlayer.setUpLazy(url, true, null, null, "这是title"); //holder.gsyVideoPlayer.setNeedShowWifiTip(false); /************************下方为其他路径************************************/ //如果一个列表的缓存路劲都一一致 - //holder.gsyVideoPlayer.setUp(url, true, new File(FileUtils.getTestPath(), "")); + //holder.gsyVideoPlayer.setUp(url, true, new File(FileUtils.getTestPath()), "这是title"); /************************下方为其他路径************************************/ //如果一个列表里的缓存路劲不一致 @@ -143,10 +149,13 @@ public void onClick(View v) { resolveFullBtn(holder.gsyVideoPlayer); } }); - holder.gsyVideoPlayer.setRotateViewAuto(true); - holder.gsyVideoPlayer.setLockLand(true); + holder.gsyVideoPlayer.setRotateViewAuto(!getListNeedAutoLand()); + holder.gsyVideoPlayer.setLockLand(!getListNeedAutoLand()); holder.gsyVideoPlayer.setPlayTag(TAG); - holder.gsyVideoPlayer.setShowFullAnimation(true); + holder.gsyVideoPlayer.setAutoFullWithSize(true); + holder.gsyVideoPlayer.setReleaseWhenLossAudio(false); + holder.gsyVideoPlayer.setShowFullAnimation(!getListNeedAutoLand()); + holder.gsyVideoPlayer.setIsTouchWiget(false); //循环 //holder.gsyVideoPlayer.setLooping(true); holder.gsyVideoPlayer.setNeedLockFull(true); @@ -155,27 +164,61 @@ public void onClick(View v) { holder.gsyVideoPlayer.setPlayPosition(position); - holder.gsyVideoPlayer.setStandardVideoAllCallBack(new SampleListener(){ + holder.gsyVideoPlayer.setVideoAllCallBack(new GSYSampleCallBack() { + @Override + public void onClickStartIcon(String url, Object... objects) { + super.onClickStartIcon(url, objects); + } + @Override public void onPrepared(String url, Object... objects) { super.onPrepared(url, objects); Debuger.printfLog("onPrepared"); - if (!holder.gsyVideoPlayer.isIfCurrentIsFullscreen()) { + boolean full = holder.gsyVideoPlayer.getCurrentPlayer().isIfCurrentIsFullscreen(); + if (!holder.gsyVideoPlayer.getCurrentPlayer().isIfCurrentIsFullscreen()) { GSYVideoManager.instance().setNeedMute(true); } - + if (holder.gsyVideoPlayer.getCurrentPlayer().isIfCurrentIsFullscreen()) { + GSYVideoManager.instance().setLastListener(holder.gsyVideoPlayer); + } + curPlayer = (StandardGSYVideoPlayer) objects[1]; + itemPlayer = holder.gsyVideoPlayer; + isPlay = true; + if (getListNeedAutoLand()) { + //重力全屏工具类 + initOrientationUtils(holder.gsyVideoPlayer, full); + ListNormalAdapter.this.onPrepared(); + } } @Override public void onQuitFullscreen(String url, Object... objects) { super.onQuitFullscreen(url, objects); + isFull = false; GSYVideoManager.instance().setNeedMute(true); + if (getListNeedAutoLand()) { + ListNormalAdapter.this.onQuitFullscreen(); + } } @Override public void onEnterFullscreen(String url, Object... objects) { super.onEnterFullscreen(url, objects); GSYVideoManager.instance().setNeedMute(false); + isFull = true; + holder.gsyVideoPlayer.getCurrentPlayer().getTitleTextView().setText((String) objects[0]); + } + + @Override + public void onAutoComplete(String url, Object... objects) { + super.onAutoComplete(url, objects); + curPlayer = null; + itemPlayer = null; + isPlay = false; + isFull = false; + if (getListNeedAutoLand()) { + ListNormalAdapter.this.onAutoComplete(); + } } }); @@ -186,14 +229,103 @@ public void onEnterFullscreen(String url, Object... objects) { * 全屏幕按键处理 */ private void resolveFullBtn(final StandardGSYVideoPlayer standardGSYVideoPlayer) { + if (getListNeedAutoLand() && orientationUtils != null) { + resolveFull(); + } standardGSYVideoPlayer.startWindowFullscreen(context, false, true); - isFullVideo = true; } - class ViewHolder { - StandardGSYVideoPlayer gsyVideoPlayer; - ImageView imageView; + SampleCoverVideo gsyVideoPlayer; + } + + public void clearCache() { + if (curPlayer != null) { + curPlayer.getCurrentPlayer().clearCurrentCache(); + } + } + + public boolean isFull() { + return isFull; + } + + /**************************支持全屏重力全屏的部分**************************/ + + /** + * 列表时是否需要支持重力旋转 + * + * @return 返回true为支持列表重力全屏 + */ + public boolean getListNeedAutoLand() { + return true; + } + + private void initOrientationUtils(StandardGSYVideoPlayer standardGSYVideoPlayer, boolean full) { + orientationUtils = new OrientationUtils((Activity) context, standardGSYVideoPlayer); + //是否需要跟随系统旋转设置 + //orientationUtils.setRotateWithSystem(false); + orientationUtils.setEnable(false); + orientationUtils.setIsLand((full) ? 1 : 0); + } + + private void resolveFull() { + if (getListNeedAutoLand() && orientationUtils != null) { + //直接横屏 + orientationUtils.resolveByClick(); + } + } + + private void onQuitFullscreen() { + if (orientationUtils != null) { + orientationUtils.backToProtVideo(); + } + } + + public void onAutoComplete() { + if (orientationUtils != null) { + orientationUtils.setEnable(false); + orientationUtils.releaseListener(); + orientationUtils = null; + } + isPlay = false; + } + + public void onPrepared() { + if (orientationUtils == null) { + return; + } + //开始播放了才能旋转和全屏 + orientationUtils.setEnable(true); } + public void onConfigurationChanged(Activity activity, Configuration newConfig) { + //如果旋转了就全屏 + if (isPlay && itemPlayer != null && orientationUtils != null) { + itemPlayer.onConfigurationChanged(activity, newConfig, orientationUtils, false, true); + } + } + + public OrientationUtils getOrientationUtils() { + return orientationUtils; + } + + + public void onBackPressed() { + if (orientationUtils != null) { + orientationUtils.backToProtVideo(); + } + } + + public void onDestroy() { + if (isPlay && curPlayer != null) { + curPlayer.getCurrentPlayer().release(); + } + if (orientationUtils != null) { + orientationUtils.releaseListener(); + orientationUtils = null; + } + } + + + } diff --git a/app/src/main/java/com/example/gsyvideoplayer/adapter/ListVideoAdapter.java b/app/src/main/java/com/example/gsyvideoplayer/adapter/ListVideoAdapter.java index 8ddd26d62..edd05c1fb 100644 --- a/app/src/main/java/com/example/gsyvideoplayer/adapter/ListVideoAdapter.java +++ b/app/src/main/java/com/example/gsyvideoplayer/adapter/ListVideoAdapter.java @@ -10,9 +10,7 @@ import com.example.gsyvideoplayer.R; import com.example.gsyvideoplayer.model.VideoModel; -import com.shuyu.gsyvideoplayer.listener.StandardVideoAllCallBack; -import com.shuyu.gsyvideoplayer.utils.FileUtils; -import com.shuyu.gsyvideoplayer.utils.ListVideoUtil; +import com.shuyu.gsyvideoplayer.utils.GSYVideoHelper; import com.shuyu.gsyvideoplayer.utils.OrientationUtils; import java.io.File; @@ -32,16 +30,20 @@ public class ListVideoAdapter extends BaseAdapter { private Context context; private ViewGroup rootView; + private OrientationUtils orientationUtils; private boolean isFullVideo; - private ListVideoUtil listVideoUtil; + private GSYVideoHelper smallVideoHelper; + + private GSYVideoHelper.GSYVideoHelperBuilder gsySmallVideoHelperBuilder; - public ListVideoAdapter(Context context, ListVideoUtil listVideoUtil) { + public ListVideoAdapter(Context context, GSYVideoHelper smallVideoHelper, GSYVideoHelper.GSYVideoHelperBuilder gsySmallVideoHelperBuilder) { super(); this.context = context; - this.listVideoUtil = listVideoUtil; + this.smallVideoHelper = smallVideoHelper; + this.gsySmallVideoHelperBuilder = gsySmallVideoHelperBuilder; inflater = LayoutInflater.from(context); for (int i = 0; i < 40; i++) { @@ -82,20 +84,21 @@ public View getView(final int position, View convertView, ViewGroup parent) { //增加封面 holder.imageView.setScaleType(ImageView.ScaleType.CENTER_CROP); holder.imageView.setImageResource(R.mipmap.xxx1); - listVideoUtil.addVideoPlayer(position, holder.imageView, TAG, holder.videoContainer, holder.playerBtn); + smallVideoHelper.addVideoPlayer(position, holder.imageView, TAG, holder.videoContainer, holder.playerBtn); holder.playerBtn.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { notifyDataSetChanged(); //listVideoUtil.setLoop(true); - listVideoUtil.setPlayPositionAndTag(position, TAG); - final String url = "http://baobab.wdjcdn.com/14564977406580.mp4"; - listVideoUtil.setTitle("title " + position); - //listVideoUtil.setCachePath(new File(FileUtils.getPath())); - listVideoUtil.startPlay(url); + smallVideoHelper.setPlayPositionAndTag(position, TAG); + //final String url = "https://res.exexm.com/cw_145225549855002"; + final String url = "http://9890.vod.myqcloud.com/9890_4e292f9a3dd011e6b4078980237cc3d3.f20.mp4"; + gsySmallVideoHelperBuilder.setVideoTitle("title " + position) + .setUrl(url); + smallVideoHelper.startPlay(); //必须在startPlay之后设置才能生效 - //listVideoUtil.getGsyVideoPlayer().getTitleTextView().setVisibility(View.VISIBLE); + //smallVideoHelper.getGsyVideoPlayer().getTitleTextView().setVisibility(View.VISIBLE); } }); return convertView; diff --git a/app/src/main/java/com/example/gsyvideoplayer/adapter/RecyclerBaseAdapter.java b/app/src/main/java/com/example/gsyvideoplayer/adapter/RecyclerBaseAdapter.java index ed8c2e4db..6e44f083c 100644 --- a/app/src/main/java/com/example/gsyvideoplayer/adapter/RecyclerBaseAdapter.java +++ b/app/src/main/java/com/example/gsyvideoplayer/adapter/RecyclerBaseAdapter.java @@ -1,7 +1,6 @@ package com.example.gsyvideoplayer.adapter; import android.content.Context; -import android.support.v7.widget.RecyclerView; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -9,10 +8,12 @@ import com.example.gsyvideoplayer.R; import com.example.gsyvideoplayer.holder.RecyclerItemViewHolder; import com.example.gsyvideoplayer.model.VideoModel; -import com.shuyu.gsyvideoplayer.utils.ListVideoUtil; +import com.shuyu.gsyvideoplayer.utils.GSYVideoHelper; import java.util.List; +import androidx.recyclerview.widget.RecyclerView; + /** * Created by GUO on 2015/12/3. */ @@ -25,8 +26,12 @@ public class RecyclerBaseAdapter extends RecyclerView.Adapter { private final static String TAG = "RecyclerBaseAdapter"; private List itemDataList = null; + private Context context = null; - private ListVideoUtil listVideoUtil; + + private GSYVideoHelper smallVideoHelper; + + private GSYVideoHelper.GSYVideoHelperBuilder gsySmallVideoHelperBuilder; public RecyclerBaseAdapter(Context context, List itemDataList) { this.itemDataList = itemDataList; @@ -45,7 +50,7 @@ public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, @Override public void onBindViewHolder(final RecyclerView.ViewHolder holder, int position) { RecyclerItemViewHolder recyclerItemViewHolder = (RecyclerItemViewHolder) holder; - recyclerItemViewHolder.setListVideoUtil(listVideoUtil); + recyclerItemViewHolder.setVideoHelper(smallVideoHelper, gsySmallVideoHelperBuilder); recyclerItemViewHolder.setRecyclerBaseAdapter(this); recyclerItemViewHolder.onBind(position, itemDataList.get(position)); } @@ -66,11 +71,12 @@ public void setListData(List data) { notifyDataSetChanged(); } - public ListVideoUtil getListVideoUtil() { - return listVideoUtil; + public GSYVideoHelper getVideoHelper() { + return smallVideoHelper; } - public void setListVideoUtil(ListVideoUtil listVideoUtil) { - this.listVideoUtil = listVideoUtil; + public void setVideoHelper(GSYVideoHelper smallVideoHelper, GSYVideoHelper.GSYVideoHelperBuilder gsySmallVideoHelperBuilder) { + this.smallVideoHelper = smallVideoHelper; + this.gsySmallVideoHelperBuilder = gsySmallVideoHelperBuilder; } } diff --git a/app/src/main/java/com/example/gsyvideoplayer/adapter/RecyclerNormalAdapter.java b/app/src/main/java/com/example/gsyvideoplayer/adapter/RecyclerNormalAdapter.java index c80949565..3869d0679 100644 --- a/app/src/main/java/com/example/gsyvideoplayer/adapter/RecyclerNormalAdapter.java +++ b/app/src/main/java/com/example/gsyvideoplayer/adapter/RecyclerNormalAdapter.java @@ -1,19 +1,18 @@ package com.example.gsyvideoplayer.adapter; import android.content.Context; -import android.support.v7.widget.RecyclerView; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import com.example.gsyvideoplayer.R; import com.example.gsyvideoplayer.holder.RecyclerItemNormalHolder; -import com.example.gsyvideoplayer.holder.RecyclerItemViewHolder; import com.example.gsyvideoplayer.model.VideoModel; -import com.shuyu.gsyvideoplayer.utils.ListVideoUtil; import java.util.List; +import androidx.recyclerview.widget.RecyclerView; + /** * Created by guoshuyu on 2017/1/9. */ diff --git a/app/src/main/java/com/example/gsyvideoplayer/effect/BitmapEffect.java b/app/src/main/java/com/example/gsyvideoplayer/effect/BitmapEffect.java new file mode 100644 index 000000000..e06038180 --- /dev/null +++ b/app/src/main/java/com/example/gsyvideoplayer/effect/BitmapEffect.java @@ -0,0 +1,32 @@ +package com.example.gsyvideoplayer.effect; + +import android.opengl.GLSurfaceView; + +import com.shuyu.gsyvideoplayer.render.view.GSYVideoGLView.ShaderInterface; + + +/** + * 合成两个渲染图画面 + */ +public class BitmapEffect implements ShaderInterface { + public BitmapEffect() { + } + + @Override + public String getShader(GLSurfaceView mGlSurfaceView) { + String shader = + "#extension GL_OES_EGL_image_external : require\n" + + "precision mediump float;\n" + + "varying vec2 vTextureCoord;\n" + + "uniform samplerExternalOES sTexture;\n" + + "uniform sampler2D sTexture2;\n" + + "void main() {\n" + + " vec4 c2 = texture2D(sTexture, vTextureCoord);\n" + + " vec4 c1 = texture2D(sTexture2, vTextureCoord);\n" + + " vec4 whiteColor = vec4(1.0);\n" + + " gl_FragColor = whiteColor - (whiteColor - c2) / c1;\n" + + "}\n"; + return shader; + + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/gsyvideoplayer/effect/BitmapIconEffect.java b/app/src/main/java/com/example/gsyvideoplayer/effect/BitmapIconEffect.java new file mode 100644 index 000000000..a5bc13c82 --- /dev/null +++ b/app/src/main/java/com/example/gsyvideoplayer/effect/BitmapIconEffect.java @@ -0,0 +1,142 @@ +package com.example.gsyvideoplayer.effect; + +import android.graphics.Bitmap; +import android.opengl.GLSurfaceView; + +import com.shuyu.gsyvideoplayer.render.view.GSYVideoGLView.ShaderInterface; + + +/** + * 水印效果 + */ +public class BitmapIconEffect implements ShaderInterface { + + private final static int NEVER_SET = -5555; + + private GLSurfaceView mGlSurfaceViewl; + + private Bitmap mBitmap; + + private int mWidth = -1; + + private int mHeight = -1; + + private float mAlpha = 1.0f; + + private float mPositionOffset = 1.0f; + + private float mPositionX = NEVER_SET; + + private float mPositionY = NEVER_SET; + + public BitmapIconEffect(Bitmap bitmap) { + this(bitmap, bitmap.getWidth(), bitmap.getHeight()); + } + + public BitmapIconEffect(Bitmap bitmap, int width, int height) { + this(bitmap, width, height, 1); + } + + public BitmapIconEffect(Bitmap bitmap, int width, int height, float alpha) { + this.mBitmap = bitmap; + this.mWidth = width; + this.mHeight = height; + this.mAlpha = alpha; + } + + @Override + public String getShader(GLSurfaceView mGlSurfaceView) { + this.mGlSurfaceViewl = mGlSurfaceView; + String shader = + "#extension GL_OES_EGL_image_external : require\n" + + "precision mediump float;\n" + + "varying vec2 vTextureCoord;\n" + + "uniform samplerExternalOES sTexture;\n" + + "uniform sampler2D sTexture2;\n" + + "void main() {\n" + + " vec4 c1 = texture2D(sTexture2, vTextureCoord);\n" + + " gl_FragColor = vec4(c1.rgb, c1.a *" + mAlpha + ");\n" + + "}\n"; + return shader; + + } + + public void setPositionX(float positionX) { + this.mPositionX = positionX; + } + + public void setPositionY(float positionY) { + this.mPositionY = positionY; + } + + public float getAlpha() { + return mAlpha; + } + + public float getPositionOffset() { + return mPositionOffset; + } + + public float getWidth() { + return (float) mWidth; + } + + public float getHeight() { + return (float) mHeight; + } + + /** + * 水印图的默认比例 + */ + public float getScaleW() { + return getWidth() / mGlSurfaceViewl.getWidth(); + } + + /** + * 水印图的默认比例 + */ + public float getScaleH() { + return getHeight() / mGlSurfaceViewl.getHeight(); + } + + /** + * 水印图的起始位置,默认右边 + */ + public float getPositionX() { + if (mPositionX != NEVER_SET) { + return mPositionX; + } + return -(mGlSurfaceViewl.getWidth() / (getWidth()) - mPositionOffset); + } + + /** + * 水印图的起始位置,默认上 + */ + public float getPositionY() { + if (mPositionY != NEVER_SET) { + return mPositionY; + } + return -(mGlSurfaceViewl.getHeight() / (getHeight()) - mPositionOffset); + } + + + public float getMaxPositionX() { + return mGlSurfaceViewl.getWidth() / (getWidth()) - mPositionOffset; + } + + public float getMaxPositionY() { + return mGlSurfaceViewl.getHeight() / (getHeight()) - mPositionOffset; + } + + public float getMinPositionX() { + return -(mGlSurfaceViewl.getWidth() / (getWidth()) - mPositionOffset); + } + + public float getMinPositionY() { + return -(mGlSurfaceViewl.getHeight() / (getHeight()) - mPositionOffset); + } + + public Bitmap getBitmap() { + return mBitmap; + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/gsyvideoplayer/effect/GSYVideoGLViewCustomRender.java b/app/src/main/java/com/example/gsyvideoplayer/effect/GSYVideoGLViewCustomRender.java new file mode 100644 index 000000000..6fb5f7396 --- /dev/null +++ b/app/src/main/java/com/example/gsyvideoplayer/effect/GSYVideoGLViewCustomRender.java @@ -0,0 +1,230 @@ +package com.example.gsyvideoplayer.effect; + +import android.annotation.SuppressLint; +import android.graphics.Bitmap; +import android.opengl.GLES20; +import android.opengl.GLUtils; +import android.opengl.Matrix; + +import com.shuyu.gsyvideoplayer.render.glrender.GSYVideoGLViewSimpleRender; + +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.FloatBuffer; + +import javax.microedition.khronos.egl.EGLConfig; +import javax.microedition.khronos.opengles.GL10; + + +/** + * 自定义渲染功能,继承原本的提供视频渲染 + * 自定义实现水印 + */ +@SuppressLint("ViewConstructor") +public class GSYVideoGLViewCustomRender extends GSYVideoGLViewSimpleRender { + + private static final int FLOAT_SIZE_BYTES = 4; + + private static final int TRIANGLE_VERTICES_DATA_STRIDE_BYTES = 5 * FLOAT_SIZE_BYTES; + + private static final int TRIANGLE_VERTICES_DATA_POS_OFFSET = 0; + + private static final int TRIANGLE_VERTICES_DATA_UV_OFFSET = 3; + + private final float[] mTriangleVerticesData = { + // X, Y, Z, U, V + -1.0f, -1.0f, 0.0f, 0.0f, + 0.0f, 1.0f, -1.0f, 0.0f, + 1.0f, 0.0f, -1.0f, 1.0f, + 0.0f, 0.0f, 1.0f, 1.0f, + 1.0f, 0.0f, 1.0f, 1.0f,}; + + private float[] mMVPMatrix = new float[16]; + + private float[] mSTMatrix = new float[16]; + + private int muMVPMatrixHandle; + + private int muSTMatrixHandle; + + private int maPositionHandle; + + private int maTextureHandle; + + private int curProgram; + + private int mTexturesBitmap[] = new int[1]; + + private FloatBuffer mTriangleVertices; + + //水印圖 + private BitmapIconEffect mBitmapEffect; + + public GSYVideoGLViewCustomRender() { + super(); + init(); + } + + public GSYVideoGLViewCustomRender(Bitmap bitmap) { + this(bitmap, bitmap.getWidth(), bitmap.getHeight()); + + } + + public GSYVideoGLViewCustomRender(Bitmap bitmap, int width, int height) { + this(bitmap, width, height, 1.0f); + } + + public GSYVideoGLViewCustomRender(Bitmap bitmap, int width, int height, float alpha) { + super(); + init(); + + mBitmapEffect = new BitmapIconEffect(bitmap, width, height, alpha); + } + + private void init() { + mTriangleVertices = ByteBuffer + .allocateDirect( + mTriangleVerticesData.length * FLOAT_SIZE_BYTES) + .order(ByteOrder.nativeOrder()).asFloatBuffer(); + mTriangleVertices.put(mTriangleVerticesData).position(0); + + Matrix.setIdentityM(mSTMatrix, 0); + Matrix.setIdentityM(mMVPMatrix, 0); + } + + @Override + public void onDrawFrame(GL10 glUnused) { + super.onDrawFrame(glUnused); + + //curProgram = createProgram(getVertexShader(), mBitmapEffect.getShader(mSurfaceView)); + + GLES20.glUseProgram(curProgram); + checkGlError("glUseProgram"); + + //绑定注入bitmap + int mFilterInputTextureUniform2 = GLES20.glGetUniformLocation(curProgram, "sTexture2"); + GLES20.glActiveTexture(GLES20.GL_TEXTURE3); + GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mTexturesBitmap[0]); + GLES20.glUniform1i(mFilterInputTextureUniform2, mTexturesBitmap[0]); + + mTriangleVertices.position(TRIANGLE_VERTICES_DATA_POS_OFFSET); + GLES20.glVertexAttribPointer(maPositionHandle, 3, GLES20.GL_FLOAT, + false, TRIANGLE_VERTICES_DATA_STRIDE_BYTES, + mTriangleVertices); + checkGlError("glVertexAttribPointer maPosition"); + GLES20.glEnableVertexAttribArray(maPositionHandle); + checkGlError("glEnableVertexAttribArray maPositionHandle"); + + mTriangleVertices.position(TRIANGLE_VERTICES_DATA_UV_OFFSET); + GLES20.glVertexAttribPointer(maTextureHandle, 3, GLES20.GL_FLOAT, + false, TRIANGLE_VERTICES_DATA_STRIDE_BYTES, + mTriangleVertices); + checkGlError("glVertexAttribPointer maTextureHandle"); + GLES20.glEnableVertexAttribArray(maTextureHandle); + checkGlError("glEnableVertexAttribArray maTextureHandle"); + + GLES20.glUniformMatrix4fv(muSTMatrixHandle, 1, false, mSTMatrix, 0); + + + GLES20.glUniformMatrix4fv(muMVPMatrixHandle, 1, false, mMVPMatrix, 0); + + //水印透明 + GLES20.glEnable(GLES20.GL_BLEND); + GLES20.glBlendFunc(GLES20.GL_SRC_ALPHA, GLES20.GL_ONE_MINUS_SRC_ALPHA); + + GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4); + checkGlError("glDrawArrays"); + + GLES20.glFinish(); + GLES20.glDisable(GLES20.GL_BLEND); + } + + @Override + public void onSurfaceChanged(GL10 glUnused, int width, int height) { + super.onSurfaceChanged(glUnused, width, height); + //旋转到正常角度 + Matrix.setRotateM(mMVPMatrix, 0, 180f, 0.0f, 0, 1.0f); + //调整大小比例 + Matrix.scaleM(mMVPMatrix, 0, mBitmapEffect.getScaleW(), mBitmapEffect.getScaleH(), 1); + //调整位置 + Matrix.translateM(mMVPMatrix, 0, mBitmapEffect.getPositionX(), mBitmapEffect.getPositionY(), 0f); + } + + @Override + public void onSurfaceCreated(GL10 glUnused, EGLConfig config) { + super.onSurfaceCreated(glUnused, config); + + curProgram = createProgram(getVertexShader(), mBitmapEffect.getShader(mSurfaceView)); + + if (curProgram == 0) { + return; + } + maPositionHandle = GLES20 + .glGetAttribLocation(curProgram, "aPosition"); + checkGlError("glGetAttribLocation aPosition"); + if (maPositionHandle == -1) { + throw new RuntimeException( + "Could not get attrib location for aPosition"); + } + maTextureHandle = GLES20.glGetAttribLocation(curProgram, + "aTextureCoord"); + checkGlError("glGetAttribLocation aTextureCoord"); + if (maTextureHandle == -1) { + throw new RuntimeException( + "Could not get attrib location for aTextureCoord"); + } + + muMVPMatrixHandle = GLES20.glGetUniformLocation(curProgram, + "uMVPMatrix"); + checkGlError("glGetUniformLocation uMVPMatrix"); + if (muMVPMatrixHandle == -1) { + throw new RuntimeException( + "Could not get attrib location for uMVPMatrix"); + } + + muSTMatrixHandle = GLES20.glGetUniformLocation(curProgram, + "uSTMatrix"); + checkGlError("glGetUniformLocation uSTMatrix"); + if (muSTMatrixHandle == -1) { + throw new RuntimeException( + "Could not get attrib location for uSTMatrix"); + } + + //创建bitmap + GLES20.glGenTextures(1, mTexturesBitmap, 0); + GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mTexturesBitmap[0]); + GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, + GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_NEAREST); + GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, + GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_NEAREST); + GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, + GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE); + GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, + GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE); + GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, mBitmapEffect.getBitmap(), 0); + } + + + @Override + public void releaseAll() { + super.releaseAll(); + Bitmap bitmap = mBitmapEffect.getBitmap(); + if (bitmap != null && !bitmap.isRecycled()) { + bitmap.recycle(); + } + } + + public float[] getCurrentMVPMatrix() { + return mMVPMatrix; + } + + public void setCurrentMVPMatrix(float[] mMVPMatrix) { + this.mMVPMatrix = mMVPMatrix; + } + + public void setBitmapEffect(BitmapIconEffect bitmapEffect) { + this.mBitmapEffect = bitmapEffect; + } +} + + diff --git a/app/src/main/java/com/example/gsyvideoplayer/effect/GSYVideoGLViewCustomRender2.java b/app/src/main/java/com/example/gsyvideoplayer/effect/GSYVideoGLViewCustomRender2.java new file mode 100644 index 000000000..427a8cc75 --- /dev/null +++ b/app/src/main/java/com/example/gsyvideoplayer/effect/GSYVideoGLViewCustomRender2.java @@ -0,0 +1,34 @@ +package com.example.gsyvideoplayer.effect; + +import android.annotation.SuppressLint; +import android.opengl.GLES20; +import android.opengl.Matrix; + +import com.shuyu.gsyvideoplayer.render.glrender.GSYVideoGLViewSimpleRender; + +import javax.microedition.khronos.opengles.GL10; + + +/** + * 双重播放效果 + */ +@SuppressLint("ViewConstructor") +public class GSYVideoGLViewCustomRender2 extends GSYVideoGLViewSimpleRender { + + public GSYVideoGLViewCustomRender2() { + super(); + } + + @Override + public void onDrawFrame(GL10 glUnused) { + super.onDrawFrame(glUnused); + float[] transform = new float[16]; + Matrix.setIdentityM(transform, 0); + Matrix.scaleM(transform, 0, 0.8f, 0.8f, 1); + GLES20.glUniformMatrix4fv(getMuMVPMatrixHandle(), 1, false, transform, 0); + GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4); + GLES20.glFinish(); + } +} + + diff --git a/app/src/main/java/com/example/gsyvideoplayer/effect/GSYVideoGLViewCustomRender3.java b/app/src/main/java/com/example/gsyvideoplayer/effect/GSYVideoGLViewCustomRender3.java new file mode 100644 index 000000000..92fdbeb12 --- /dev/null +++ b/app/src/main/java/com/example/gsyvideoplayer/effect/GSYVideoGLViewCustomRender3.java @@ -0,0 +1,69 @@ +package com.example.gsyvideoplayer.effect; + +import android.annotation.SuppressLint; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.opengl.GLES20; +import android.opengl.GLUtils; + +import com.example.gsyvideoplayer.R; +import com.shuyu.gsyvideoplayer.render.glrender.GSYVideoGLViewSimpleRender; + +import javax.microedition.khronos.egl.EGLConfig; +import javax.microedition.khronos.opengles.GL10; + + +/** + * 图片穿孔透视播放 + */ +@SuppressLint("ViewConstructor") +public class GSYVideoGLViewCustomRender3 extends GSYVideoGLViewSimpleRender { + + private int mTexturesBitmap[] = new int[1]; + + private BitmapEffect mBitmapEffect = new BitmapEffect(); + + public GSYVideoGLViewCustomRender3() { + super(); + } + + @Override + protected void bindDrawFrameTexture() { + super.bindDrawFrameTexture(); + + //绑定注入bitmap + int mFilterInputTextureUniform2 = GLES20.glGetUniformLocation(getProgram(), "sTexture2"); + GLES20.glActiveTexture(GLES20.GL_TEXTURE3); + GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mTexturesBitmap[0]); + GLES20.glUniform1i(mFilterInputTextureUniform2, mTexturesBitmap[0]); + } + + @Override + public void onSurfaceCreated(GL10 glUnused, EGLConfig config) { + super.onSurfaceCreated(glUnused, config); + + Bitmap bitmap = BitmapFactory.decodeResource(mSurfaceView.getResources(), R.drawable.video_brightness_6_white_36dp); + //创建bitmap + GLES20.glGenTextures(1, mTexturesBitmap, 0); + GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mTexturesBitmap[0]); + GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, + GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_NEAREST); + GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, + GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_NEAREST); + GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, + GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE); + GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, + GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE); + GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, bitmap, 0); + bitmap.recycle(); + } + + @Override + protected String getFragmentShader() { + return mBitmapEffect.getShader(mSurfaceView); + } + + +} + + diff --git a/app/src/main/java/com/example/gsyvideoplayer/effect/GSYVideoGLViewCustomRender4.java b/app/src/main/java/com/example/gsyvideoplayer/effect/GSYVideoGLViewCustomRender4.java new file mode 100644 index 000000000..066c13cc6 --- /dev/null +++ b/app/src/main/java/com/example/gsyvideoplayer/effect/GSYVideoGLViewCustomRender4.java @@ -0,0 +1,59 @@ +package com.example.gsyvideoplayer.effect; + +import android.annotation.SuppressLint; +import android.opengl.GLES20; +import android.opengl.Matrix; + +import com.shuyu.gsyvideoplayer.render.effect.NoEffect; +import com.shuyu.gsyvideoplayer.render.glrender.GSYVideoGLViewSimpleRender; +import com.shuyu.gsyvideoplayer.utils.Debuger; + +import javax.microedition.khronos.egl.EGLConfig; +import javax.microedition.khronos.opengles.GL10; + + +/** + * 铺满的双重播放 + * 配合高斯模糊,可以实现,高斯拉伸视频铺满背景,替换黑色,前台正常比例播放 + */ +@SuppressLint("ViewConstructor") +public class GSYVideoGLViewCustomRender4 extends GSYVideoGLViewSimpleRender { + + private int mProgram; + + public GSYVideoGLViewCustomRender4() { + super(); + } + + @Override + public void onDrawFrame(GL10 glUnused) { + super.onDrawFrame(glUnused); + + GLES20.glUseProgram(mProgram); + + float[] transform = new float[16]; + Matrix.setIdentityM(transform, 0); + Matrix.scaleM(transform, 0, (float) mCurrentViewWidth / mSurfaceView.getWidth(), + (float) mCurrentViewHeight / mSurfaceView.getHeight(), 1); + + GLES20.glUniformMatrix4fv(getMuSTMatrixHandle(), 1, false, mSTMatrix, 0); + + GLES20.glUniformMatrix4fv(getMuMVPMatrixHandle(), 1, false, transform, 0); + GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4); + GLES20.glFinish(); + } + + + @Override + public void onSurfaceCreated(GL10 glUnused, EGLConfig config) { + super.onSurfaceCreated(glUnused, config); + mProgram = createProgram(getVertexShader(), new NoEffect().getShader(mSurfaceView)); + } + + @Override + public void initRenderSize() { + Matrix.scaleM(mMVPMatrix, 0, 1f, 1f, 1); + } +} + + diff --git a/app/src/main/java/com/example/gsyvideoplayer/effect/PixelationEffect.java b/app/src/main/java/com/example/gsyvideoplayer/effect/PixelationEffect.java new file mode 100644 index 000000000..9fdc9790b --- /dev/null +++ b/app/src/main/java/com/example/gsyvideoplayer/effect/PixelationEffect.java @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2012 CyberAgent + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.example.gsyvideoplayer.effect; + +import android.opengl.GLSurfaceView; + +import com.shuyu.gsyvideoplayer.render.view.GSYVideoGLView; + +/** + * 马赛克效果 + * Created by guoshuyu on 2017/09/20. + */ +public class PixelationEffect implements GSYVideoGLView.ShaderInterface { + + private float pixel = 40f; + + + public PixelationEffect() { + } + + /** + * 1 - 100 + */ + public PixelationEffect(float pixel) { + this.pixel = pixel; + } + + @Override + public String getShader(GLSurfaceView mGlSurfaceView) { + + String shader = "#extension GL_OES_EGL_image_external : require\n" + + "precision mediump float;\n"+ + "varying vec2 vTextureCoord;\n" + + + "float imageWidthFactor = "+ (1 / (float)mGlSurfaceView.getWidth()) +";\n" + + "float imageHeightFactor = " + ( 1 /(float)mGlSurfaceView.getHeight()) + ";\n" + + "uniform samplerExternalOES sTexture;\n" + + "float pixel = " + pixel +";\n" + + + "void main()\n" + + "{\n" + + " vec2 uv = vTextureCoord.xy;\n" + + " float dx = pixel * imageWidthFactor;\n" + + " float dy = pixel * imageHeightFactor;\n" + + " vec2 coord = vec2(dx * floor(uv.x / dx), dy * floor(uv.y / dy));\n" + + " vec3 tc = texture2D(sTexture, coord).xyz;\n" + + " gl_FragColor = vec4(tc, 1.0);\n" + + "}"; + + return shader; + + } +} diff --git a/app/src/main/java/com/example/gsyvideoplayer/exo/DetailExoListPlayer.java b/app/src/main/java/com/example/gsyvideoplayer/exo/DetailExoListPlayer.java new file mode 100644 index 000000000..4820e8485 --- /dev/null +++ b/app/src/main/java/com/example/gsyvideoplayer/exo/DetailExoListPlayer.java @@ -0,0 +1,159 @@ +package com.example.gsyvideoplayer.exo; + +import android.os.Bundle; +import androidx.core.widget.NestedScrollView; +import android.view.View; +import android.widget.ImageView; +import android.widget.RelativeLayout; + +import com.example.gsyvideoplayer.R; +import com.shuyu.gsyvideoplayer.GSYBaseActivityDetail; +import com.shuyu.gsyvideoplayer.builder.GSYVideoOptionBuilder; +import com.shuyu.gsyvideoplayer.listener.LockClickListener; +import com.shuyu.gsyvideoplayer.model.GSYVideoModel; +import com.shuyu.gsyvideoplayer.video.base.GSYVideoPlayer; + +import java.util.ArrayList; +import java.util.List; + +import butterknife.BindView; +import butterknife.ButterKnife; + +/** + * 使用自定义的ExoPlayer,实现无缝切换下一集功能 + */ +public class DetailExoListPlayer extends GSYBaseActivityDetail { + + + @BindView(R.id.post_detail_nested_scroll) + NestedScrollView postDetailNestedScroll; + @BindView(R.id.detail_player) + GSYExo2PlayerView detailPlayer; + @BindView(R.id.activity_detail_player) + RelativeLayout activityDetailPlayer; + @BindView(R.id.next) + View next; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_deatil_exo_list_player); + ButterKnife.bind(this); + + //GSYBaseActivityDetail 的 普通模式初始化 + initVideo(); + + List urls = new ArrayList<>(); + + urls.add(new GSYVideoModel("http://7xjmzj.com1.z0.glb.clouddn.com/20171026175005_JObCxCE2.mp4", "标题1")); + urls.add(new GSYVideoModel("https://media6.smartstudy.com/ae/07/3997/2/dest.m3u8", "标题3")); + urls.add(new GSYVideoModel("http://9890.vod.myqcloud.com/9890_4e292f9a3dd011e6b4078980237cc3d3.f20.mp4", "标题2")); + detailPlayer.setUp(urls, 0); + + //使用 exo 的 CacheDataSourceFactory 实现 + detailPlayer.setExoCache(false); + //detailPlayer.setOverrideExtension("m3u8"); + + //增加封面 + ImageView imageView = new ImageView(this); + imageView.setScaleType(ImageView.ScaleType.CENTER_CROP); + imageView.setImageResource(R.mipmap.xxx1); + detailPlayer.setThumbImageView(imageView); + + resolveNormalVideoUI(); + + detailPlayer.setIsTouchWiget(true); + //关闭自动旋转 + detailPlayer.setRotateViewAuto(false); + detailPlayer.setLockLand(false); + detailPlayer.setShowFullAnimation(false); + detailPlayer.setNeedLockFull(true); + + detailPlayer.setVideoAllCallBack(this); + + detailPlayer.setLockClickListener(new LockClickListener() { + @Override + public void onClick(View view, boolean lock) { + if (orientationUtils != null) { + //配合下方的onConfigurationChanged + orientationUtils.setEnable(!lock); + } + } + }); + + next.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + GSYExoVideoManager.instance().next(); + } + }); + + } + + + + + + /** + * 重载为GSYExoVideoManager的方法处理 + */ + @Override + public void onBackPressed() { + if (orientationUtils != null) { + orientationUtils.backToProtVideo(); + } + if (GSYExoVideoManager.backFromWindowFull(this)) { + return; + } + super.onBackPressed(); + } + + + @Override + public GSYExo2PlayerView getGSYVideoPlayer() { + return detailPlayer; + } + + @Override + public GSYVideoOptionBuilder getGSYVideoOptionBuilder() { + //不用builder的模式 + return null; + } + + @Override + public void clickForFullScreen() { + } + + /** + * 是否启动旋转横屏,true表示启动 + * @return true + */ + @Override + public boolean getDetailOrientationRotateAuto() { + return true; + } + + @Override + public void onEnterFullscreen(String url, Object... objects) { + super.onEnterFullscreen(url, objects); + //隐藏调全屏对象的返回按键 + GSYVideoPlayer gsyVideoPlayer = (GSYVideoPlayer)objects[1]; + gsyVideoPlayer.getBackButton().setVisibility(View.GONE); + } + + + private void resolveNormalVideoUI() { + //增加title + detailPlayer.getTitleTextView().setVisibility(View.VISIBLE); + detailPlayer.getBackButton().setVisibility(View.VISIBLE); + } + + private GSYVideoPlayer getCurPlay() { + if (detailPlayer.getFullWindowPlayer() != null) { + return detailPlayer.getFullWindowPlayer(); + } + return detailPlayer; + } + + +} diff --git a/app/src/main/java/com/example/gsyvideoplayer/exo/GSYExo2MediaPlayer.java b/app/src/main/java/com/example/gsyvideoplayer/exo/GSYExo2MediaPlayer.java new file mode 100644 index 000000000..a0edca325 --- /dev/null +++ b/app/src/main/java/com/example/gsyvideoplayer/exo/GSYExo2MediaPlayer.java @@ -0,0 +1,128 @@ +package com.example.gsyvideoplayer.exo; + +import android.content.Context; +import android.net.Uri; + +import com.google.android.exoplayer2.C; +import com.google.android.exoplayer2.Timeline; +import com.google.android.exoplayer2.source.ConcatenatingMediaSource; +import com.google.android.exoplayer2.source.MediaSource; +import com.shuyu.gsyvideoplayer.utils.Debuger; + +import java.io.FileDescriptor; +import java.util.List; +import java.util.Map; + +import tv.danmaku.ijk.media.exo2.IjkExo2MediaPlayer; +import tv.danmaku.ijk.media.player.IMediaPlayer; + +/** + * 自定义exo player,实现不同于库的exo 无缝切换效果 + */ +public class GSYExo2MediaPlayer extends IjkExo2MediaPlayer { + + private static final String TAG = "GSYExo2MediaPlayer"; + + private static final long MAX_POSITION_FOR_SEEK_TO_PREVIOUS = 3000; + + private final Timeline.Window window = new Timeline.Window(); + + public static final int POSITION_DISCONTINUITY = 899; + + public GSYExo2MediaPlayer(Context context) { + super(context); + } + + @Override + @Deprecated + public void setDataSource(Context context, Uri uri) { + throw new UnsupportedOperationException("Deprecated, try setDataSource(List uris, Map headers)"); + } + + @Override + @Deprecated + public void setDataSource(Context context, Uri uri, Map headers) { + throw new UnsupportedOperationException("Deprecated, try setDataSource(List uris, Map headers)"); + } + + @Override + @Deprecated + public void setDataSource(String path) { + throw new UnsupportedOperationException("Deprecated, try setDataSource(List uris, Map headers)"); + } + + @Override + @Deprecated + public void setDataSource(FileDescriptor fd) { + throw new UnsupportedOperationException("Deprecated, try setDataSource(List uris, Map headers)"); + } + + @Override + public void onPositionDiscontinuity(int reason) { + super.onPositionDiscontinuity(reason); + notifyOnInfo(POSITION_DISCONTINUITY, reason); + } + + public void setDataSource(List uris, Map headers, boolean cache) { + mHeaders = headers; + if (uris == null) { + return; + } + ConcatenatingMediaSource concatenatedSource = new ConcatenatingMediaSource(); + for (String uri : uris) { + MediaSource mediaSource = mExoHelper.getMediaSource(uri, isPreview, cache, false, mCacheDir, getOverrideExtension()); + concatenatedSource.addMediaSource(mediaSource); + } + mMediaSource = concatenatedSource; + } + + /** + * 上一集 + */ + public void previous() { + if (mInternalPlayer == null) { + return; + } + Timeline timeline = mInternalPlayer.getCurrentTimeline(); + if (timeline.isEmpty()) { + return; + } + int windowIndex = mInternalPlayer.getCurrentWindowIndex(); + timeline.getWindow(windowIndex, window); + int previousWindowIndex = mInternalPlayer.getPreviousWindowIndex(); + if (previousWindowIndex != C.INDEX_UNSET + && (mInternalPlayer.getCurrentPosition() <= MAX_POSITION_FOR_SEEK_TO_PREVIOUS + || (window.isDynamic && !window.isSeekable))) { + mInternalPlayer.seekTo(previousWindowIndex, C.TIME_UNSET); + } else { + mInternalPlayer.seekTo(0); + } + } + + /** + * 下一集 + */ + public void next() { + if (mInternalPlayer == null) { + return; + } + Timeline timeline = mInternalPlayer.getCurrentTimeline(); + if (timeline.isEmpty()) { + return; + } + int windowIndex = mInternalPlayer.getCurrentWindowIndex(); + int nextWindowIndex = mInternalPlayer.getNextWindowIndex(); + if (nextWindowIndex != C.INDEX_UNSET) { + mInternalPlayer.seekTo(nextWindowIndex, C.TIME_UNSET); + } else if (timeline.getWindow(windowIndex, window, false).isDynamic) { + mInternalPlayer.seekTo(windowIndex, C.TIME_UNSET); + } + } + + public int getCurrentWindowIndex() { + if (mInternalPlayer == null) { + return 0; + } + return mInternalPlayer.getCurrentWindowIndex(); + } +} diff --git a/app/src/main/java/com/example/gsyvideoplayer/exo/GSYExo2PlayerView.java b/app/src/main/java/com/example/gsyvideoplayer/exo/GSYExo2PlayerView.java new file mode 100644 index 000000000..99a814be3 --- /dev/null +++ b/app/src/main/java/com/example/gsyvideoplayer/exo/GSYExo2PlayerView.java @@ -0,0 +1,265 @@ +package com.example.gsyvideoplayer.exo; + +import android.app.Activity; +import android.app.AlertDialog; +import android.content.Context; +import android.content.DialogInterface; +import android.media.AudioManager; +import android.text.TextUtils; +import android.util.AttributeSet; +import android.view.View; +import android.view.ViewGroup; +import android.view.WindowManager; + +import com.example.gsyvideoplayer.R; +import com.shuyu.gsyvideoplayer.model.GSYVideoModel; +import com.shuyu.gsyvideoplayer.utils.Debuger; +import com.shuyu.gsyvideoplayer.utils.NetworkUtils; +import com.shuyu.gsyvideoplayer.video.StandardGSYVideoPlayer; +import com.shuyu.gsyvideoplayer.video.base.GSYBaseVideoPlayer; +import com.shuyu.gsyvideoplayer.video.base.GSYVideoPlayer; +import com.shuyu.gsyvideoplayer.video.base.GSYVideoViewBridge; + +import java.io.File; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static com.example.gsyvideoplayer.exo.GSYExo2MediaPlayer.POSITION_DISCONTINUITY; + + +/** + Created by guoshuyu on 2018/5/16. + 自定义View支持exo的list数据,实现无缝切换效果 + 这是一种思路,通过自定义后GSYExo2MediaPlayer内部,通过ConcatenatingMediaSource实现列表播放 + 诸如此类,还可以实现AdsMediaSource等 + */ + +public class GSYExo2PlayerView extends StandardGSYVideoPlayer { + + protected List mUriList = new ArrayList<>(); + protected int mPlayPosition; + protected boolean mExoCache = false; + + /** + 1.5.0开始加入,如果需要不同布局区分功能,需要重载 + */ + public GSYExo2PlayerView(Context context, Boolean fullFlag) { + super(context, fullFlag); + } + + public GSYExo2PlayerView(Context context) { + super(context); + } + + public GSYExo2PlayerView(Context context, AttributeSet attrs) { + super(context, attrs); + } + + + /** + 设置播放URL + + @param url 播放url + @param position 需要播放的位置 + @return + */ + public boolean setUp(List url, int position) { + return setUp(url, position, null, new HashMap()); + } + + /** + 设置播放URL + + @param url 播放url + @param position 需要播放的位置 + @param cachePath 缓存路径,如果是M3U8或者HLS,请设置为false + @return + */ + public boolean setUp(List url, int position, File cachePath) { + return setUp(url, position, cachePath, new HashMap()); + } + + /** + 设置播放URL + + @param url 播放url + @param position 需要播放的位置 + @param cachePath 缓存路径,如果是M3U8或者HLS,请设置为false + @param mapHeadData http header + @return + */ + public boolean setUp(List url, int position, File cachePath, Map mapHeadData) { + return setUp(url, position, cachePath, mapHeadData, true); + } + + /** + 设置播放URL + + @param url 播放url + @param position 需要播放的位置 + @param cachePath 缓存路径,如果是M3U8或者HLS,请设置为false + @param mapHeadData http header + @param changeState 切换的时候释放surface + @return + */ + protected boolean setUp(List url, int position, File cachePath, Map mapHeadData, boolean changeState) { + mUriList = url; + mPlayPosition = position; + mMapHeadData = mapHeadData; + GSYVideoModel gsyVideoModel = url.get(position); + + //不支持边播边缓存 + boolean set = setUp(gsyVideoModel.getUrl(), false, cachePath, gsyVideoModel.getTitle(), changeState); + if (!TextUtils.isEmpty(gsyVideoModel.getTitle())) { + mTitleTextView.setText(gsyVideoModel.getTitle()); + } + return set; + } + + @Override + protected void cloneParams(GSYBaseVideoPlayer from, GSYBaseVideoPlayer to) { + super.cloneParams(from, to); + GSYExo2PlayerView sf = (GSYExo2PlayerView) from; + GSYExo2PlayerView st = (GSYExo2PlayerView) to; + st.mPlayPosition = sf.mPlayPosition; + st.mUriList = sf.mUriList; + st.mExoCache = sf.mExoCache; + st.mTitle = sf.mTitle; + } + + @Override + public GSYBaseVideoPlayer startWindowFullscreen(Context context, boolean actionBar, boolean statusBar) { + GSYBaseVideoPlayer gsyBaseVideoPlayer = super.startWindowFullscreen(context, actionBar, statusBar); + if (gsyBaseVideoPlayer != null) { + GSYExo2PlayerView GSYExo2PlayerView = (GSYExo2PlayerView) gsyBaseVideoPlayer; + GSYVideoModel gsyVideoModel = mUriList.get(mPlayPosition); + if (!TextUtils.isEmpty(gsyVideoModel.getTitle())) { + GSYExo2PlayerView.mTitleTextView.setText(gsyVideoModel.getTitle()); + } + } + return gsyBaseVideoPlayer; + } + + @Override + protected void resolveNormalVideoShow(View oldF, ViewGroup vp, GSYVideoPlayer gsyVideoPlayer) { + if (gsyVideoPlayer != null) { + GSYVideoModel gsyVideoModel = mUriList.get(mPlayPosition); + if (!TextUtils.isEmpty(gsyVideoModel.getTitle())) { + mTitleTextView.setText(gsyVideoModel.getTitle()); + } + } + super.resolveNormalVideoShow(oldF, vp, gsyVideoPlayer); + } + + + @Override + protected void startPrepare() { + if (getGSYVideoManager().listener() != null) { + getGSYVideoManager().listener().onCompletion(); + } + if (mVideoAllCallBack != null) { + Debuger.printfLog("onStartPrepared"); + mVideoAllCallBack.onStartPrepared(mOriginUrl, mTitle, this); + } + getGSYVideoManager().setListener(this); + getGSYVideoManager().setPlayTag(mPlayTag); + getGSYVideoManager().setPlayPosition(mPlayPosition); + mAudioManager.requestAudioFocus(onAudioFocusChangeListener, AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN_TRANSIENT); + try { + ((Activity) getActivityContext()).getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); + } catch (Exception e) { + e.printStackTrace(); + } + mBackUpPlayingBufferState = -1; + + //prepare通过list初始化 + List urls = new ArrayList<>(); + for (GSYVideoModel gsyVideoModel : mUriList) { + urls.add(gsyVideoModel.getUrl()); + } + + if (urls.size() == 0) { + Debuger.printfError("********************** urls isEmpty . Do you know why ? **********************"); + } + + ((GSYExoVideoManager) getGSYVideoManager()).prepare(urls, (mMapHeadData == null) ? new HashMap() : mMapHeadData, mLooping, mSpeed, mExoCache, mCachePath, mOverrideExtension); + + setStateAndUi(CURRENT_STATE_PREPAREING); + } + + + /** + 显示wifi确定框,如需要自定义继承重写即可 + */ + @Override + protected void showWifiDialog() { + if (!NetworkUtils.isAvailable(mContext)) { + startPlayLogic(); + return; + } + AlertDialog.Builder builder = new AlertDialog.Builder(getActivityContext()); + builder.setMessage(getResources().getString(R.string.tips_not_wifi)); + builder.setPositiveButton(getResources().getString(R.string.tips_not_wifi_confirm), new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + dialog.dismiss(); + startPlayLogic(); + } + }); + builder.setNegativeButton(getResources().getString(R.string.tips_not_wifi_cancel), new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + dialog.dismiss(); + } + }); + builder.create().show(); + } + + public void setExoCache(boolean exoCache) { + this.mExoCache = exoCache; + } + + /**********以下重载GSYVideoPlayer的GSYVideoViewBridge相关实现***********/ + + @Override + public GSYVideoViewBridge getGSYVideoManager() { + GSYExoVideoManager.instance().initContext(getContext().getApplicationContext()); + return GSYExoVideoManager.instance(); + } + + @Override + protected boolean backFromFull(Context context) { + return GSYExoVideoManager.backFromWindowFull(context); + } + + @Override + protected void releaseVideos() { + GSYExoVideoManager.releaseAllVideos(); + } + + @Override + protected int getFullId() { + return GSYExoVideoManager.FULLSCREEN_ID; + } + + @Override + protected int getSmallId() { + return GSYExoVideoManager.SMALL_ID; + } + + @Override + public void onInfo(int what, int extra) { + if (what == POSITION_DISCONTINUITY) { + int window = ((GSYExo2MediaPlayer)getGSYVideoManager().getPlayer().getMediaPlayer()).getCurrentWindowIndex(); + mPlayPosition = window; + GSYVideoModel gsyVideoModel = mUriList.get(window); + if (!TextUtils.isEmpty(gsyVideoModel.getTitle())) { + mTitleTextView.setText(gsyVideoModel.getTitle()); + } + } else { + super.onInfo(what, extra); + } + } +} diff --git a/app/src/main/java/com/example/gsyvideoplayer/exo/GSYExoModel.java b/app/src/main/java/com/example/gsyvideoplayer/exo/GSYExoModel.java new file mode 100644 index 000000000..839828365 --- /dev/null +++ b/app/src/main/java/com/example/gsyvideoplayer/exo/GSYExoModel.java @@ -0,0 +1,32 @@ +package com.example.gsyvideoplayer.exo; + +import com.shuyu.gsyvideoplayer.model.GSYModel; + +import java.io.File; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +/** + * Created by guoshuyu on 2018/5/16. + * 自定义列表数据model + */ + +public class GSYExoModel extends GSYModel { + + List urls = new ArrayList<>(); + + public GSYExoModel(List urls, Map mapHeadData, boolean loop, float speed, boolean cache, File cachePath, String overrideExtension) { + super("", mapHeadData, loop, speed, cache, cachePath, overrideExtension); + this.urls = urls; + } + + public List getUrls() { + return urls; + } + + public void setUrls(List urls) { + this.urls = urls; + } + +} diff --git a/app/src/main/java/com/example/gsyvideoplayer/exo/GSYExoPlayerManager.java b/app/src/main/java/com/example/gsyvideoplayer/exo/GSYExoPlayerManager.java new file mode 100644 index 000000000..2057c46dd --- /dev/null +++ b/app/src/main/java/com/example/gsyvideoplayer/exo/GSYExoPlayerManager.java @@ -0,0 +1,238 @@ +package com.example.gsyvideoplayer.exo; + +import android.content.Context; +import android.media.AudioManager; +import android.os.Message; +import android.view.Surface; + +import com.google.android.exoplayer2.video.DummySurface; +import com.shuyu.gsyvideoplayer.cache.ICacheManager; +import com.shuyu.gsyvideoplayer.model.VideoOptionModel; +import com.shuyu.gsyvideoplayer.player.BasePlayerManager; +import com.shuyu.gsyvideoplayer.utils.Debuger; + +import java.util.List; + +import tv.danmaku.ijk.media.player.IMediaPlayer; + +/** + * Created by guoshuyu on 2018/5/16. + * 自定义player管理器,装载自定义exo player,实现无缝切换效果 + */ +public class GSYExoPlayerManager extends BasePlayerManager { + + private GSYExo2MediaPlayer mediaPlayer; + + private Surface surface; + + private DummySurface dummySurface; + + @Override + public IMediaPlayer getMediaPlayer() { + return mediaPlayer; + } + + @Override + public void initVideoPlayer(Context context, Message msg, List optionModelList, ICacheManager cacheManager) { + mediaPlayer = new GSYExo2MediaPlayer(context); + mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC); + if (dummySurface == null) { + dummySurface = DummySurface.newInstanceV17(context, false); + } + try { + mediaPlayer.setLooping(((GSYExoModel) msg.obj).isLooping()); + Debuger.printfError("###### " + ((GSYExoModel) msg.obj).getOverrideExtension()); + mediaPlayer.setOverrideExtension(((GSYExoModel) msg.obj).getOverrideExtension()); + mediaPlayer.setDataSource(((GSYExoModel) msg.obj).getUrls(), ((GSYExoModel) msg.obj).getMapHeadData(), ((GSYExoModel) msg.obj).isCache()); + //很遗憾,EXO2的setSpeed只能在播放前生效 + if (((GSYExoModel) msg.obj).getSpeed() != 1 && ((GSYExoModel) msg.obj).getSpeed() > 0) { + mediaPlayer.setSpeed(((GSYExoModel) msg.obj).getSpeed(), 1); + } + } catch (Exception e) { + e.printStackTrace(); + } + } + + @Override + public void showDisplay(Message msg) { + if (mediaPlayer == null) { + return; + } + if (msg.obj == null) { + mediaPlayer.setSurface(dummySurface); + } else { + Surface holder = (Surface) msg.obj; + surface = holder; + mediaPlayer.setSurface(holder); + } + } + + @Override + public void setSpeed(float speed, boolean soundTouch) { + //很遗憾,EXO2的setSpeed只能在播放前生效 + //Debuger.printfError("很遗憾,目前EXO2的setSpeed只能在播放前设置生效"); + if (mediaPlayer != null) { + try { + mediaPlayer.setSpeed(speed, 1); + } catch (Exception e) { + e.printStackTrace(); + } + } + } + + @Override + public void setNeedMute(boolean needMute) { + if(mediaPlayer != null) { + if (needMute) { + mediaPlayer.setVolume(0, 0); + } else { + mediaPlayer.setVolume(1, 1); + } + } + } + + + @Override + public void releaseSurface() { + if (surface != null) { + //surface.release(); + surface = null; + } + } + + @Override + public void release() { + if(mediaPlayer != null) { + mediaPlayer.setSurface(null); + mediaPlayer.release(); + } + if (dummySurface != null) { + dummySurface.release(); + dummySurface = null; + } + } + + @Override + public int getBufferedPercentage() { + return -1; + } + + /** + * 上一集 + */ + public void previous() { + if (mediaPlayer == null) { + return; + } + mediaPlayer.previous(); + } + + /** + * 下一集 + */ + public void next() { + if (mediaPlayer == null) { + return; + } + mediaPlayer.next(); + } + + @Override + public long getNetSpeed() { + return 0; + } + + @Override + public void setSpeedPlaying(float speed, boolean soundTouch) { + + } + + @Override + public void start() { + if (mediaPlayer != null) { + mediaPlayer.start(); + } + } + + @Override + public void stop() { + if (mediaPlayer != null) { + mediaPlayer.stop(); + } + } + + @Override + public void pause() { + if (mediaPlayer != null) { + mediaPlayer.pause(); + } + } + + @Override + public int getVideoWidth() { + if (mediaPlayer != null) { + return mediaPlayer.getVideoWidth(); + } + return 0; + } + + @Override + public int getVideoHeight() { + if (mediaPlayer != null) { + return mediaPlayer.getVideoHeight(); + } + return 0; + } + + @Override + public boolean isPlaying() { + if (mediaPlayer != null) { + return mediaPlayer.isPlaying(); + } + return false; + } + + @Override + public void seekTo(long time) { + if (mediaPlayer != null) { + mediaPlayer.seekTo(time); + } + } + + @Override + public long getCurrentPosition() { + if (mediaPlayer != null) { + return mediaPlayer.getCurrentPosition(); + } + return 0; + } + + @Override + public long getDuration() { + if (mediaPlayer != null) { + return mediaPlayer.getDuration(); + } + return 0; + } + + @Override + public int getVideoSarNum() { + if (mediaPlayer != null) { + return mediaPlayer.getVideoSarNum(); + } + return 1; + } + + @Override + public int getVideoSarDen() { + if (mediaPlayer != null) { + return mediaPlayer.getVideoSarDen(); + } + return 1; + } + + @Override + public boolean isSurfaceSupportLockCanvas() { + return false; + } +} diff --git a/app/src/main/java/com/example/gsyvideoplayer/exo/GSYExoVideoManager.java b/app/src/main/java/com/example/gsyvideoplayer/exo/GSYExoVideoManager.java new file mode 100644 index 000000000..0b5af9bbe --- /dev/null +++ b/app/src/main/java/com/example/gsyvideoplayer/exo/GSYExoVideoManager.java @@ -0,0 +1,164 @@ +package com.example.gsyvideoplayer.exo; + +import android.annotation.SuppressLint; +import android.app.Activity; +import android.content.Context; +import android.os.Message; +import android.view.View; +import android.view.ViewGroup; +import android.view.Window; + +import com.shuyu.gsyvideoplayer.GSYVideoBaseManager; +import com.shuyu.gsyvideoplayer.player.IPlayerManager; +import com.shuyu.gsyvideoplayer.utils.CommonUtil; +import com.shuyu.gsyvideoplayer.video.base.GSYVideoPlayer; + +import java.io.File; +import java.util.List; +import java.util.Map; + +import static com.shuyu.gsyvideoplayer.utils.CommonUtil.hideNavKey; + +/** + * Created by guoshuyu on 2018/5/16. + * 自定义管理器,连接自定义exo view 和 exo player,实现无缝切换效果 + */ +public class GSYExoVideoManager extends GSYVideoBaseManager { + public static final int SMALL_ID = com.shuyu.gsyvideoplayer.R.id.small_id; + + public static final int FULLSCREEN_ID = com.shuyu.gsyvideoplayer.R.id.full_id; + + public static String TAG = "GSYExoVideoManager"; + + @SuppressLint("StaticFieldLeak") + private static GSYExoVideoManager videoManager; + + + private GSYExoVideoManager() { + init(); + } + + /** + * 单例管理器 + */ + public static synchronized GSYExoVideoManager instance() { + if (videoManager == null) { + videoManager = new GSYExoVideoManager(); + } + return videoManager; + } + + @Override + protected IPlayerManager getPlayManager() { + return new GSYExoPlayerManager(); + } + + public void prepare(List urls, Map mapHeadData, boolean loop, float speed, boolean cache, File cachePath, String overrideExtension) { + if (urls.size() == 0) return; + Message msg = new Message(); + msg.what = HANDLER_PREPARE; + msg.obj = new GSYExoModel(urls, mapHeadData, loop, speed, cache, cachePath, overrideExtension); + sendMessage(msg); + if (needTimeOutOther) { + startTimeOutBuffer(); + } + } + + + /** + * 上一集 + */ + public void previous() { + if (playerManager == null) { + return; + } + ((GSYExoPlayerManager)playerManager).previous(); + } + + /** + * 下一集 + */ + public void next() { + if (playerManager == null) { + return; + } + ((GSYExoPlayerManager)playerManager).next(); + } + + /** + * 退出全屏,主要用于返回键 + * + * @return 返回是否全屏 + */ + @SuppressWarnings("ResourceType") + public static boolean backFromWindowFull(Context context) { + boolean backFrom = false; + ViewGroup vp = (ViewGroup) (CommonUtil.scanForActivity(context)).findViewById(Window.ID_ANDROID_CONTENT); + View oldF = vp.findViewById(FULLSCREEN_ID); + if (oldF != null) { + backFrom = true; + hideNavKey(context); + if (GSYExoVideoManager.instance().lastListener() != null) { + GSYExoVideoManager.instance().lastListener().onBackFullscreen(); + } + } + return backFrom; + } + + /** + * 页面销毁了记得调用是否所有的video + */ + public static void releaseAllVideos() { + if (GSYExoVideoManager.instance().listener() != null) { + GSYExoVideoManager.instance().listener().onCompletion(); + } + GSYExoVideoManager.instance().releaseMediaPlayer(); + } + + + /** + * 暂停播放 + */ + public static void onPause() { + if (GSYExoVideoManager.instance().listener() != null) { + GSYExoVideoManager.instance().listener().onVideoPause(); + } + } + + /** + * 恢复播放 + */ + public static void onResume() { + if (GSYExoVideoManager.instance().listener() != null) { + GSYExoVideoManager.instance().listener().onVideoResume(); + } + } + + + /** + * 恢复暂停状态 + * + * @param seek 是否产生seek动作,直播设置为false + */ + public static void onResume(boolean seek) { + if (GSYExoVideoManager.instance().listener() != null) { + GSYExoVideoManager.instance().listener().onVideoResume(seek); + } + } + + /** + * 当前是否全屏状态 + * + * @return 当前是否全屏状态, true代表是。 + */ + @SuppressWarnings("ResourceType") + public static boolean isFullState(Activity activity) { + ViewGroup vp = (ViewGroup) (CommonUtil.scanForActivity(activity)).findViewById(Window.ID_ANDROID_CONTENT); + final View full = vp.findViewById(FULLSCREEN_ID); + GSYVideoPlayer gsyVideoPlayer = null; + if (full != null) { + gsyVideoPlayer = (GSYVideoPlayer) full; + } + return gsyVideoPlayer != null; + } +} diff --git a/app/src/main/java/com/example/gsyvideoplayer/exosubtitle/GSYExoSubTitleDetailPlayer.java b/app/src/main/java/com/example/gsyvideoplayer/exosubtitle/GSYExoSubTitleDetailPlayer.java new file mode 100644 index 000000000..860e912d1 --- /dev/null +++ b/app/src/main/java/com/example/gsyvideoplayer/exosubtitle/GSYExoSubTitleDetailPlayer.java @@ -0,0 +1,228 @@ +package com.example.gsyvideoplayer.exosubtitle; + +import android.content.res.Configuration; +import android.os.Bundle; +import android.view.View; +import android.widget.ImageView; +import android.widget.RelativeLayout; + +import androidx.appcompat.app.AppCompatActivity; +import androidx.core.widget.NestedScrollView; + +import com.example.gsyvideoplayer.R; +import com.google.android.exoplayer2.SeekParameters; +import com.shuyu.gsyvideoplayer.builder.GSYVideoOptionBuilder; +import com.shuyu.gsyvideoplayer.listener.GSYSampleCallBack; +import com.shuyu.gsyvideoplayer.listener.GSYVideoProgressListener; +import com.shuyu.gsyvideoplayer.listener.LockClickListener; +import com.shuyu.gsyvideoplayer.utils.Debuger; +import com.shuyu.gsyvideoplayer.utils.OrientationUtils; +import com.shuyu.gsyvideoplayer.video.base.GSYVideoPlayer; + +import java.util.HashMap; +import java.util.Map; + +import butterknife.BindView; +import butterknife.ButterKnife; +import tv.danmaku.ijk.media.exo2.Exo2PlayerManager; + + +public class GSYExoSubTitleDetailPlayer extends AppCompatActivity { + + @BindView(R.id.post_detail_nested_scroll) + NestedScrollView postDetailNestedScroll; + + //推荐使用StandardGSYVideoPlayer,功能一致 + //CustomGSYVideoPlayer部分功能处于试验阶段 + @BindView(R.id.detail_player) + GSYExoSubTitleVideoView detailPlayer; + + @BindView(R.id.activity_detail_player) + RelativeLayout activityDetailPlayer; + + private boolean isPlay; + private boolean isPause; + + private OrientationUtils orientationUtils; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_detail_exo_subtitle_player); + ButterKnife.bind(this); + + String url = getUrl(); + + //增加封面 + ImageView imageView = new ImageView(this); + imageView.setScaleType(ImageView.ScaleType.CENTER_CROP); + imageView.setImageResource(R.mipmap.xxx1); + + //detailPlayer.setThumbImageView(imageView); + + resolveNormalVideoUI(); + + //外部辅助的旋转,帮助全屏 + orientationUtils = new OrientationUtils(this, detailPlayer); + //初始化不打开外部的旋转 + orientationUtils.setEnable(false); + + + Map header = new HashMap<>(); + header.put("ee", "33"); + header.put("allowCrossProtocolRedirects", "true"); + GSYVideoOptionBuilder gsyVideoOption = new GSYVideoOptionBuilder(); + gsyVideoOption.setThumbImageView(imageView) + .setIsTouchWiget(true) + .setRotateViewAuto(false) + .setLockLand(false) + .setAutoFullWithSize(false) + .setShowFullAnimation(false) + .setNeedLockFull(true) + .setUrl(url) + .setMapHeadData(header) + .setCacheWithPlay(false) + .setVideoTitle("测试视频") + .setVideoAllCallBack(new GSYSampleCallBack() { + @Override + public void onPrepared(String url, Object... objects) { + Debuger.printfError("***** onPrepared **** " + objects[0]); + Debuger.printfError("***** onPrepared **** " + objects[1]); + super.onPrepared(url, objects); + //开始播放了才能旋转和全屏 + orientationUtils.setEnable(true); + isPlay = true; + + //设置 seek 的临近帧。 + if (detailPlayer.getGSYVideoManager().getPlayer() instanceof Exo2PlayerManager) { + ((Exo2PlayerManager) detailPlayer.getGSYVideoManager().getPlayer()).setSeekParameter(SeekParameters.NEXT_SYNC); + Debuger.printfError("***** setSeekParameter **** "); + } + } + + @Override + public void onEnterFullscreen(String url, Object... objects) { + super.onEnterFullscreen(url, objects); + Debuger.printfError("***** onEnterFullscreen **** " + objects[0]);//title + Debuger.printfError("***** onEnterFullscreen **** " + objects[1]);//当前全屏player + } + + @Override + public void onAutoComplete(String url, Object... objects) { + super.onAutoComplete(url, objects); + } + + @Override + public void onClickStartError(String url, Object... objects) { + super.onClickStartError(url, objects); + } + + @Override + public void onQuitFullscreen(String url, Object... objects) { + super.onQuitFullscreen(url, objects); + Debuger.printfError("***** onQuitFullscreen **** " + objects[0]);//title + Debuger.printfError("***** onQuitFullscreen **** " + objects[1]);//当前非全屏player + if (orientationUtils != null) { + orientationUtils.backToProtVideo(); + } + } + }) + .setLockClickListener(new LockClickListener() { + @Override + public void onClick(View view, boolean lock) { + if (orientationUtils != null) { + //配合下方的onConfigurationChanged + orientationUtils.setEnable(!lock); + } + } + }) + .setGSYVideoProgressListener(new GSYVideoProgressListener() { + @Override + public void onProgress(int progress, int secProgress, int currentPosition, int duration) { + Debuger.printfLog(" progress " + progress + " secProgress " + secProgress + " currentPosition " + currentPosition + " duration " + duration); + } + }) + .build(detailPlayer); + + detailPlayer.getFullscreenButton().setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + //直接横屏 + orientationUtils.resolveByClick(); + + //第一个true是否需要隐藏actionbar,第二个true是否需要隐藏statusbar + detailPlayer.startWindowFullscreen(GSYExoSubTitleDetailPlayer.this, true, true); + } + }); + + detailPlayer.setSubTitle("http://img.cdn.guoshuyu.cn/subtitle2.srt"); + } + + @Override + public void onBackPressed() { + + if (orientationUtils != null) { + orientationUtils.backToProtVideo(); + } + + if (GSYExoSubTitleVideoManager.backFromWindowFull(this)) { + return; + } + super.onBackPressed(); + } + + + @Override + protected void onPause() { + getCurPlay().onVideoPause(); + super.onPause(); + isPause = true; + } + + @Override + protected void onResume() { + getCurPlay().onVideoResume(false); + super.onResume(); + isPause = false; + } + + @Override + protected void onDestroy() { + super.onDestroy(); + if (isPlay) { + getCurPlay().release(); + } + //GSYPreViewManager.instance().releaseMediaPlayer(); + if (orientationUtils != null) + orientationUtils.releaseListener(); + } + + + @Override + public void onConfigurationChanged(Configuration newConfig) { + super.onConfigurationChanged(newConfig); + //如果旋转了就全屏 + if (isPlay && !isPause) { + detailPlayer.onConfigurationChanged(this, newConfig, orientationUtils, true, true); + } + } + + + private void resolveNormalVideoUI() { + //增加title + detailPlayer.getTitleTextView().setVisibility(View.GONE); + detailPlayer.getBackButton().setVisibility(View.GONE); + } + + private GSYVideoPlayer getCurPlay() { + if (detailPlayer.getFullWindowPlayer() != null) { + return detailPlayer.getFullWindowPlayer(); + } + return detailPlayer; + } + + + private String getUrl() { + return "http://9890.vod.myqcloud.com/9890_4e292f9a3dd011e6b4078980237cc3d3.f20.mp4"; + } +} diff --git a/app/src/main/java/com/example/gsyvideoplayer/exosubtitle/GSYExoSubTitleModel.java b/app/src/main/java/com/example/gsyvideoplayer/exosubtitle/GSYExoSubTitleModel.java new file mode 100644 index 000000000..f5879f2d9 --- /dev/null +++ b/app/src/main/java/com/example/gsyvideoplayer/exosubtitle/GSYExoSubTitleModel.java @@ -0,0 +1,42 @@ +package com.example.gsyvideoplayer.exosubtitle; + +import com.google.android.exoplayer2.text.TextOutput; +import com.shuyu.gsyvideoplayer.model.GSYModel; + +import java.io.File; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +/** + * Created by guoshuyu on 2018/5/16. + * 自定义列表数据model + */ + +public class GSYExoSubTitleModel extends GSYModel { + + private String subTitle; + private TextOutput textOutput; + + public GSYExoSubTitleModel(String url, String subTitle, TextOutput textOutput, Map mapHeadData, boolean loop, float speed, boolean cache, File cachePath, String overrideExtension) { + super(url, mapHeadData, loop, speed, cache, cachePath, overrideExtension); + this.subTitle = subTitle; + this.textOutput = textOutput; + } + + public String getSubTitle() { + return subTitle; + } + + public void setSubTitle(String subTitle) { + this.subTitle = subTitle; + } + + public TextOutput getTextOutput() { + return textOutput; + } + + public void setTextOutput(TextOutput textOutput) { + this.textOutput = textOutput; + } +} diff --git a/app/src/main/java/com/example/gsyvideoplayer/exosubtitle/GSYExoSubTitlePlayer.java b/app/src/main/java/com/example/gsyvideoplayer/exosubtitle/GSYExoSubTitlePlayer.java new file mode 100644 index 000000000..db247d74f --- /dev/null +++ b/app/src/main/java/com/example/gsyvideoplayer/exosubtitle/GSYExoSubTitlePlayer.java @@ -0,0 +1,131 @@ +package com.example.gsyvideoplayer.exosubtitle; + +import android.content.Context; +import android.net.Uri; +import android.os.Handler; +import android.os.Looper; + +import com.google.android.exoplayer2.C; +import com.google.android.exoplayer2.DefaultLoadControl; +import com.google.android.exoplayer2.DefaultRenderersFactory; +import com.google.android.exoplayer2.ExoPlayerFactory; +import com.google.android.exoplayer2.Format; +import com.google.android.exoplayer2.source.MediaSource; +import com.google.android.exoplayer2.source.MergingMediaSource; +import com.google.android.exoplayer2.source.SingleSampleMediaSource; +import com.google.android.exoplayer2.text.TextOutput; +import com.google.android.exoplayer2.trackselection.DefaultTrackSelector; +import com.google.android.exoplayer2.upstream.DefaultBandwidthMeter; +import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory; +import com.google.android.exoplayer2.upstream.DefaultHttpDataSourceFactory; +import com.google.android.exoplayer2.util.MimeTypes; +import com.google.android.exoplayer2.util.Util; +import com.shuyu.gsyvideoplayer.utils.Debuger; + +import tv.danmaku.ijk.media.exo2.IjkExo2MediaPlayer; +import tv.danmaku.ijk.media.exo2.demo.EventLogger; + +public class GSYExoSubTitlePlayer extends IjkExo2MediaPlayer { + + private String mSubTitile; + private TextOutput mTextOutput; + + public GSYExoSubTitlePlayer(Context context) { + super(context); + } + + + @Override + protected void prepareAsyncInternal() { + new Handler(Looper.getMainLooper()).post( + new Runnable() { + @Override + public void run() { + if (mTrackSelector == null) { + mTrackSelector = new DefaultTrackSelector(); + } + mEventLogger = new EventLogger(mTrackSelector); + boolean preferExtensionDecoders = true; + boolean useExtensionRenderers = true;//是否开启扩展 + @DefaultRenderersFactory.ExtensionRendererMode int extensionRendererMode = useExtensionRenderers + ? (preferExtensionDecoders ? DefaultRenderersFactory.EXTENSION_RENDERER_MODE_PREFER + : DefaultRenderersFactory.EXTENSION_RENDERER_MODE_ON) + : DefaultRenderersFactory.EXTENSION_RENDERER_MODE_OFF; + if (mRendererFactory == null) { + mRendererFactory = new DefaultRenderersFactory(mAppContext); + mRendererFactory.setExtensionRendererMode(extensionRendererMode); + } + if (mLoadControl == null) { + mLoadControl = new DefaultLoadControl(); + } + mInternalPlayer = ExoPlayerFactory.newSimpleInstance(mAppContext, mRendererFactory, mTrackSelector, mLoadControl, null, Looper.getMainLooper()); + mInternalPlayer.addListener(GSYExoSubTitlePlayer.this); + mInternalPlayer.addAnalyticsListener(GSYExoSubTitlePlayer.this); + if (mTextOutput != null) { + mInternalPlayer.addTextOutput(mTextOutput); + } + mInternalPlayer.addListener(mEventLogger); + if (mSpeedPlaybackParameters != null) { + mInternalPlayer.setPlaybackParameters(mSpeedPlaybackParameters); + } + if (mSurface != null) + mInternalPlayer.setVideoSurface(mSurface); + + if (mSubTitile != null) { + MediaSource textMediaSource = getTextSource(Uri.parse(mSubTitile)); + mMediaSource = new MergingMediaSource(mMediaSource, textMediaSource); + } + + mInternalPlayer.prepare(mMediaSource); + mInternalPlayer.setPlayWhenReady(false); + } + } + ); + } + + public MediaSource getTextSource(Uri subTitle) { + //todo C.SELECTION_FLAG_AUTOSELECT language MimeTypes + Format textFormat = Format.createTextSampleFormat(null, MimeTypes.APPLICATION_SUBRIP, C.SELECTION_FLAG_FORCED, "en"); + + DefaultHttpDataSourceFactory factory = new DefaultHttpDataSourceFactory(Util.getUserAgent(mAppContext, + "GSYExoSubTitlePlayer"), new DefaultBandwidthMeter.Builder(mAppContext).build(), + 50000, + 50000, true); + + MediaSource textMediaSource = new SingleSampleMediaSource.Factory(new DefaultDataSourceFactory(mAppContext, null, + factory)) + .createMediaSource(subTitle, textFormat, C.TIME_UNSET); + return textMediaSource; + + } + + + public String getSubTitile() { + return mSubTitile; + } + + public void setSubTitile(String subTitile) { + this.mSubTitile = subTitile; + } + + public TextOutput getTextOutput() { + return mTextOutput; + } + + public void setTextOutput(TextOutput textOutput) { + this.mTextOutput = textOutput; + } + + public void addTextOutputPlaying(TextOutput textOutput) { + if(mInternalPlayer != null) { + mInternalPlayer.addTextOutput(textOutput); + } + } + + public void removeTextOutput(TextOutput textOutput) { + if(mInternalPlayer != null) { + mInternalPlayer.removeTextOutput(textOutput); + } + } + +} diff --git a/app/src/main/java/com/example/gsyvideoplayer/exosubtitle/GSYExoSubTitlePlayerManager.java b/app/src/main/java/com/example/gsyvideoplayer/exosubtitle/GSYExoSubTitlePlayerManager.java new file mode 100644 index 000000000..551841fca --- /dev/null +++ b/app/src/main/java/com/example/gsyvideoplayer/exosubtitle/GSYExoSubTitlePlayerManager.java @@ -0,0 +1,295 @@ +package com.example.gsyvideoplayer.exosubtitle; + +import android.content.Context; +import android.media.AudioManager; +import android.net.TrafficStats; +import android.net.Uri; +import android.os.Message; +import android.view.Surface; + +import androidx.annotation.Nullable; + +import com.google.android.exoplayer2.SeekParameters; +import com.google.android.exoplayer2.text.TextOutput; +import com.google.android.exoplayer2.video.DummySurface; +import com.shuyu.gsyvideoplayer.cache.ICacheManager; +import com.shuyu.gsyvideoplayer.model.GSYModel; +import com.shuyu.gsyvideoplayer.model.VideoOptionModel; +import com.shuyu.gsyvideoplayer.player.BasePlayerManager; +import com.shuyu.gsyvideoplayer.utils.Debuger; + +import java.util.List; + +import tv.danmaku.ijk.media.player.IMediaPlayer; + +/** + * Created by guoshuyu on 2018/5/16. + * 自定义player管理器,装载自定义exo player,实现无缝切换效果 + */ +public class GSYExoSubTitlePlayerManager extends BasePlayerManager { + + private Context context; + + private GSYExoSubTitlePlayer mediaPlayer; + + private Surface surface; + + private DummySurface dummySurface; + + private long lastTotalRxBytes = 0; + + private long lastTimeStamp = 0; + + @Override + public IMediaPlayer getMediaPlayer() { + return mediaPlayer; + } + + @Override + public void initVideoPlayer(Context context, Message msg, List optionModelList, ICacheManager cacheManager) { + this.context = context.getApplicationContext(); + mediaPlayer = new GSYExoSubTitlePlayer(context); + mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC); + if (dummySurface == null) { + dummySurface = DummySurface.newInstanceV17(context, false); + } + //使用自己的cache模式 + GSYExoSubTitleModel gsyModel = (GSYExoSubTitleModel) msg.obj; + try { + mediaPlayer.setLooping(gsyModel.isLooping()); + if (gsyModel.getSubTitle() != null) { + mediaPlayer.setSubTitile(gsyModel.getSubTitle()); + } + mediaPlayer.setPreview(gsyModel.getMapHeadData() != null && gsyModel.getMapHeadData().size() > 0); + if (gsyModel.isCache() && cacheManager != null) { + //通过管理器处理 + cacheManager.doCacheLogic(context, mediaPlayer, gsyModel.getUrl(), gsyModel.getMapHeadData(), gsyModel.getCachePath()); + } else { + //通过自己的内部缓存机制 + mediaPlayer.setCache(gsyModel.isCache()); + mediaPlayer.setCacheDir(gsyModel.getCachePath()); + mediaPlayer.setOverrideExtension(gsyModel.getOverrideExtension()); + mediaPlayer.setDataSource(context, Uri.parse(gsyModel.getUrl()), gsyModel.getMapHeadData()); + } + if (gsyModel.getSpeed() != 1 && gsyModel.getSpeed() > 0) { + mediaPlayer.setSpeed(gsyModel.getSpeed(), 1); + } + if (gsyModel.getTextOutput() != null) { + mediaPlayer.setTextOutput(gsyModel.getTextOutput()); + } + } catch (Exception e) { + e.printStackTrace(); + } + initSuccess(gsyModel); + } + + @Override + public void showDisplay(final Message msg) { + if (mediaPlayer == null) { + return; + } + if (msg.obj == null) { + mediaPlayer.setSurface(dummySurface); + } else { + Surface holder = (Surface) msg.obj; + surface = holder; + mediaPlayer.setSurface(holder); + } + } + + @Override + public void setSpeed(final float speed, final boolean soundTouch) { + if (mediaPlayer != null) { + try { + mediaPlayer.setSpeed(speed, 1); + } catch (Exception e) { + e.printStackTrace(); + } + } + } + + @Override + public void setNeedMute(final boolean needMute) { + if (mediaPlayer != null) { + if (needMute) { + mediaPlayer.setVolume(0, 0); + } else { + mediaPlayer.setVolume(1, 1); + } + } + } + + + @Override + public void releaseSurface() { + if (surface != null) { + //surface.release(); + surface = null; + } + } + + @Override + public void release() { + if (mediaPlayer != null) { + mediaPlayer.setSurface(null); + mediaPlayer.release(); + } + if (dummySurface != null) { + dummySurface.release(); + dummySurface = null; + } + lastTotalRxBytes = 0; + lastTimeStamp = 0; + } + + @Override + public int getBufferedPercentage() { + if (mediaPlayer != null) { + return mediaPlayer.getBufferedPercentage(); + } + return 0; + } + + @Override + public long getNetSpeed() { + if (mediaPlayer != null) { + return getNetSpeed(context); + } + return 0; + } + + + @Override + public void setSpeedPlaying(float speed, boolean soundTouch) { + + } + + + @Override + public void start() { + if (mediaPlayer != null) { + mediaPlayer.start(); + } + } + + @Override + public void stop() { + if (mediaPlayer != null) { + mediaPlayer.stop(); + } + } + + @Override + public void pause() { + if (mediaPlayer != null) { + mediaPlayer.pause(); + } + } + + @Override + public int getVideoWidth() { + if (mediaPlayer != null) { + return mediaPlayer.getVideoWidth(); + } + return 0; + } + + @Override + public int getVideoHeight() { + if (mediaPlayer != null) { + return mediaPlayer.getVideoHeight(); + } + return 0; + } + + @Override + public boolean isPlaying() { + if (mediaPlayer != null) { + return mediaPlayer.isPlaying(); + } + return false; + } + + @Override + public void seekTo(long time) { + if (mediaPlayer != null) { + mediaPlayer.seekTo(time); + } + } + + @Override + public long getCurrentPosition() { + if (mediaPlayer != null) { + return mediaPlayer.getCurrentPosition(); + } + return 0; + } + + @Override + public long getDuration() { + if (mediaPlayer != null) { + return mediaPlayer.getDuration(); + } + return 0; + } + + @Override + public int getVideoSarNum() { + if (mediaPlayer != null) { + return mediaPlayer.getVideoSarNum(); + } + return 1; + } + + @Override + public int getVideoSarDen() { + if (mediaPlayer != null) { + return mediaPlayer.getVideoSarDen(); + } + return 1; + } + + @Override + public boolean isSurfaceSupportLockCanvas() { + return false; + } + + public void addTextOutputPlaying(TextOutput textOutput) { + if(mediaPlayer != null) { + mediaPlayer.addTextOutputPlaying(textOutput); + } + } + + public void removeTextOutput(TextOutput textOutput) { + if(mediaPlayer != null) { + mediaPlayer.removeTextOutput(textOutput); + } + } + + /** + * 设置seek 的临近帧。 + **/ + public void setSeekParameter(@Nullable SeekParameters seekParameters) { + if (mediaPlayer != null) { + mediaPlayer.setSeekParameter(seekParameters); + } + } + + + private long getNetSpeed(Context context) { + if (context == null) { + return 0; + } + long nowTotalRxBytes = TrafficStats.getUidRxBytes(context.getApplicationInfo().uid) == TrafficStats.UNSUPPORTED ? 0 : (TrafficStats.getTotalRxBytes() / 1024);//转为KB + long nowTimeStamp = System.currentTimeMillis(); + long calculationTime = (nowTimeStamp - lastTimeStamp); + if (calculationTime == 0) { + return calculationTime; + } + //毫秒转换 + long speed = ((nowTotalRxBytes - lastTotalRxBytes) * 1000 / calculationTime); + lastTimeStamp = nowTimeStamp; + lastTotalRxBytes = nowTotalRxBytes; + return speed; + } +} diff --git a/app/src/main/java/com/example/gsyvideoplayer/exosubtitle/GSYExoSubTitleVideoManager.java b/app/src/main/java/com/example/gsyvideoplayer/exosubtitle/GSYExoSubTitleVideoManager.java new file mode 100644 index 000000000..97561e4ef --- /dev/null +++ b/app/src/main/java/com/example/gsyvideoplayer/exosubtitle/GSYExoSubTitleVideoManager.java @@ -0,0 +1,161 @@ +package com.example.gsyvideoplayer.exosubtitle; + +import android.annotation.SuppressLint; +import android.app.Activity; +import android.content.Context; +import android.os.Message; +import android.view.View; +import android.view.ViewGroup; +import android.view.Window; + +import com.google.android.exoplayer2.text.TextOutput; +import com.shuyu.gsyvideoplayer.GSYVideoBaseManager; +import com.shuyu.gsyvideoplayer.player.IPlayerManager; +import com.shuyu.gsyvideoplayer.utils.CommonUtil; +import com.shuyu.gsyvideoplayer.video.base.GSYVideoPlayer; + +import java.io.File; +import java.util.Map; + +import static com.shuyu.gsyvideoplayer.utils.CommonUtil.hideNavKey; + +public class GSYExoSubTitleVideoManager extends GSYVideoBaseManager { + + public static final int SMALL_ID = com.shuyu.gsyvideoplayer.R.id.small_id; + + public static final int FULLSCREEN_ID = com.shuyu.gsyvideoplayer.R.id.full_id; + + public static String TAG = "GSYExoVideoManager"; + + @SuppressLint("StaticFieldLeak") + private static GSYExoSubTitleVideoManager videoManager; + + + private GSYExoSubTitleVideoManager() { + init(); + } + + /** + * 单例管理器 + */ + public static synchronized GSYExoSubTitleVideoManager instance() { + if (videoManager == null) { + videoManager = new GSYExoSubTitleVideoManager(); + } + return videoManager; + } + + @Override + protected IPlayerManager getPlayManager() { + playerManager = new GSYExoSubTitlePlayerManager(); + return playerManager; + } + + public void prepare(String url, String subTitle, TextOutput textOutput, Map mapHeadData, boolean loop, float speed, boolean cache, File cachePath, String overrideExtension) { + Message msg = new Message(); + msg.what = HANDLER_PREPARE; + msg.obj = new GSYExoSubTitleModel(url, subTitle, textOutput, mapHeadData, loop, speed, cache, cachePath, overrideExtension); + sendMessage(msg); + if (needTimeOutOther) { + startTimeOutBuffer(); + } + } + + + /** + * 上一集 + */ + public void previous() { + if (playerManager == null) { + return; + } + ((GSYExoSubTitleVideoManager) playerManager).previous(); + } + + /** + * 下一集 + */ + public void next() { + if (playerManager == null) { + return; + } + ((GSYExoSubTitleVideoManager) playerManager).next(); + } + + /** + * 退出全屏,主要用于返回键 + * + * @return 返回是否全屏 + */ + @SuppressWarnings("ResourceType") + public static boolean backFromWindowFull(Context context) { + boolean backFrom = false; + ViewGroup vp = (ViewGroup) (CommonUtil.scanForActivity(context)).findViewById(Window.ID_ANDROID_CONTENT); + View oldF = vp.findViewById(FULLSCREEN_ID); + if (oldF != null) { + backFrom = true; + hideNavKey(context); + if (GSYExoSubTitleVideoManager.instance().lastListener() != null) { + GSYExoSubTitleVideoManager.instance().lastListener().onBackFullscreen(); + } + } + return backFrom; + } + + /** + * 页面销毁了记得调用是否所有的video + */ + public static void releaseAllVideos() { + if (GSYExoSubTitleVideoManager.instance().listener() != null) { + GSYExoSubTitleVideoManager.instance().listener().onCompletion(); + } + GSYExoSubTitleVideoManager.instance().releaseMediaPlayer(); + } + + + /** + * 暂停播放 + */ + public static void onPause() { + if (GSYExoSubTitleVideoManager.instance().listener() != null) { + GSYExoSubTitleVideoManager.instance().listener().onVideoPause(); + } + } + + /** + * 恢复播放 + */ + public static void onResume() { + if (GSYExoSubTitleVideoManager.instance().listener() != null) { + GSYExoSubTitleVideoManager.instance().listener().onVideoResume(); + } + } + + + /** + * 恢复暂停状态 + * + * @param seek 是否产生seek动作,直播设置为false + */ + public static void onResume(boolean seek) { + if (GSYExoSubTitleVideoManager.instance().listener() != null) { + GSYExoSubTitleVideoManager.instance().listener().onVideoResume(seek); + } + } + + /** + * 当前是否全屏状态 + * + * @return 当前是否全屏状态, true代表是。 + */ + @SuppressWarnings("ResourceType") + public static boolean isFullState(Activity activity) { + ViewGroup vp = (ViewGroup) (CommonUtil.scanForActivity(activity)).findViewById(Window.ID_ANDROID_CONTENT); + final View full = vp.findViewById(FULLSCREEN_ID); + GSYVideoPlayer gsyVideoPlayer = null; + if (full != null) { + gsyVideoPlayer = (GSYVideoPlayer) full; + } + return gsyVideoPlayer != null; + } +} diff --git a/app/src/main/java/com/example/gsyvideoplayer/exosubtitle/GSYExoSubTitleVideoView.java b/app/src/main/java/com/example/gsyvideoplayer/exosubtitle/GSYExoSubTitleVideoView.java new file mode 100644 index 000000000..af795d311 --- /dev/null +++ b/app/src/main/java/com/example/gsyvideoplayer/exosubtitle/GSYExoSubTitleVideoView.java @@ -0,0 +1,144 @@ +package com.example.gsyvideoplayer.exosubtitle; + +import android.app.Activity; +import android.content.Context; +import android.graphics.Color; +import android.media.AudioManager; +import android.util.AttributeSet; +import android.util.TypedValue; +import android.view.View; +import android.view.ViewGroup; +import android.view.WindowManager; + +import com.example.gsyvideoplayer.R; +import com.google.android.exoplayer2.text.CaptionStyleCompat; +import com.google.android.exoplayer2.ui.SubtitleView; +import com.shuyu.gsyvideoplayer.utils.Debuger; +import com.shuyu.gsyvideoplayer.video.NormalGSYVideoPlayer; +import com.shuyu.gsyvideoplayer.video.base.GSYBaseVideoPlayer; +import com.shuyu.gsyvideoplayer.video.base.GSYVideoPlayer; + +import java.util.HashMap; + +public class GSYExoSubTitleVideoView extends NormalGSYVideoPlayer { + + private SubtitleView mSubtitleView; + private String mSubTitle; + + public GSYExoSubTitleVideoView(Context context, Boolean fullFlag) { + super(context, fullFlag); + } + + public GSYExoSubTitleVideoView(Context context) { + super(context); + } + + public GSYExoSubTitleVideoView(Context context, AttributeSet attrs) { + super(context, attrs); + } + + @Override + protected void init(Context context) { + super.init(context); + mSubtitleView = findViewById(R.id.sub_title_view); + + + mSubtitleView.setStyle(new CaptionStyleCompat(Color.BLACK, Color.TRANSPARENT, Color.TRANSPARENT, CaptionStyleCompat.EDGE_TYPE_NONE, CaptionStyleCompat.EDGE_TYPE_NONE, null)); + mSubtitleView.setFixedTextSize(TypedValue.COMPLEX_UNIT_DIP, 14); + } + + @Override + public int getLayoutId() { + return R.layout.video_layout_subtitle; + } + + + @Override + protected void startPrepare() { + if (getGSYVideoManager().listener() != null) { + getGSYVideoManager().listener().onCompletion(); + } + if (mVideoAllCallBack != null) { + Debuger.printfLog("onStartPrepared"); + mVideoAllCallBack.onStartPrepared(mOriginUrl, mTitle, this); + } + getGSYVideoManager().setListener(this); + getGSYVideoManager().setPlayTag(mPlayTag); + getGSYVideoManager().setPlayPosition(mPlayPosition); + mAudioManager.requestAudioFocus(onAudioFocusChangeListener, AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN_TRANSIENT); + try { + if (mContext instanceof Activity) { + ((Activity) mContext).getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); + } + } catch (Exception e) { + e.printStackTrace(); + } + mBackUpPlayingBufferState = -1; + + getGSYVideoManager().prepare(mUrl, mSubTitle, mSubtitleView, (mMapHeadData == null) ? new HashMap() : mMapHeadData, mLooping, mSpeed, mCache, mCachePath, mOverrideExtension); + setStateAndUi(CURRENT_STATE_PREPAREING); + } + + + public String getSubTitle() { + return mSubTitle; + } + + public void setSubTitle(String subTitle) { + this.mSubTitle = subTitle; + } + + + /**********以下重载 GSYVideoPlayer 的 全屏 SubtitleView 相关实现***********/ + + + @Override + public GSYBaseVideoPlayer startWindowFullscreen(Context context, boolean actionBar, boolean statusBar) { + GSYBaseVideoPlayer gsyBaseVideoPlayer = super.startWindowFullscreen(context, actionBar, statusBar); + GSYExoSubTitleVideoView gsyExoSubTitleVideoView = (GSYExoSubTitleVideoView) gsyBaseVideoPlayer; + ((GSYExoSubTitlePlayerManager) GSYExoSubTitleVideoManager.instance().getPlayer()) + .addTextOutputPlaying(gsyExoSubTitleVideoView.mSubtitleView); + + return gsyBaseVideoPlayer; + } + + @Override + protected void resolveNormalVideoShow(View oldF, ViewGroup vp, GSYVideoPlayer gsyVideoPlayer) { + super.resolveNormalVideoShow(oldF, vp, gsyVideoPlayer); + GSYExoSubTitleVideoView gsyExoSubTitleVideoView = (GSYExoSubTitleVideoView) gsyVideoPlayer; + ((GSYExoSubTitlePlayerManager) GSYExoSubTitleVideoManager.instance().getPlayer()) + .removeTextOutput(gsyExoSubTitleVideoView.mSubtitleView); + + } + + + /**********以下重载GSYVideoPlayer的GSYVideoViewBridge相关实现***********/ + + + @Override + public GSYExoSubTitleVideoManager getGSYVideoManager() { + GSYExoSubTitleVideoManager.instance().initContext(getContext().getApplicationContext()); + return GSYExoSubTitleVideoManager.instance(); + } + + @Override + protected boolean backFromFull(Context context) { + return GSYExoSubTitleVideoManager.backFromWindowFull(context); + } + + @Override + protected void releaseVideos() { + GSYExoSubTitleVideoManager.releaseAllVideos(); + } + + @Override + protected int getFullId() { + return GSYExoSubTitleVideoManager.FULLSCREEN_ID; + } + + @Override + protected int getSmallId() { + return GSYExoSubTitleVideoManager.SMALL_ID; + + } +} diff --git a/app/src/main/java/com/example/gsyvideoplayer/fragment/VideoFragment.java b/app/src/main/java/com/example/gsyvideoplayer/fragment/VideoFragment.java index a7e6bad34..422928138 100644 --- a/app/src/main/java/com/example/gsyvideoplayer/fragment/VideoFragment.java +++ b/app/src/main/java/com/example/gsyvideoplayer/fragment/VideoFragment.java @@ -1,32 +1,24 @@ package com.example.gsyvideoplayer.fragment; -import android.content.pm.ActivityInfo; -import android.content.res.Configuration; -import android.os.Build; import android.os.Bundle; -import android.support.v4.app.Fragment; -import android.support.v7.widget.LinearLayoutManager; -import android.support.v7.widget.RecyclerView; -import android.transition.Explode; +import androidx.fragment.app.Fragment; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; -import android.view.Window; import com.example.gsyvideoplayer.R; -import com.example.gsyvideoplayer.adapter.ListNormalAdapter; import com.example.gsyvideoplayer.adapter.RecyclerBaseAdapter; import com.example.gsyvideoplayer.adapter.RecyclerNormalAdapter; import com.example.gsyvideoplayer.holder.RecyclerItemNormalHolder; import com.example.gsyvideoplayer.model.VideoModel; import com.shuyu.gsyvideoplayer.GSYVideoManager; -import com.shuyu.gsyvideoplayer.GSYVideoPlayer; -import com.shuyu.gsyvideoplayer.video.StandardGSYVideoPlayer; import java.util.ArrayList; import java.util.List; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; import butterknife.BindView; import butterknife.ButterKnife; @@ -42,7 +34,6 @@ public class VideoFragment extends Fragment { List dataList = new ArrayList<>(); - boolean mFull; public VideoFragment() { // Required empty public constructor @@ -92,8 +83,8 @@ public void onScrolled(RecyclerView recyclerView, int dx, int dy) { if (GSYVideoManager.instance().getPlayTag().equals(RecyclerItemNormalHolder.TAG) && (position < firstVisibleItem || position > lastVisibleItem)) { //如果滑出去了上面和下面就是否,和今日头条一样 - if(!mFull) { - GSYVideoPlayer.releaseAllVideos(); + if(!GSYVideoManager.isFullState(getActivity())) { + GSYVideoManager.releaseAllVideos(); recyclerNormalAdapter.notifyDataSetChanged(); } } @@ -105,24 +96,12 @@ public void onScrolled(RecyclerView recyclerView, int dx, int dy) { } public boolean onBackPressed() { - if (StandardGSYVideoPlayer.backFromWindowFull(getActivity())) { + if (GSYVideoManager.backFromWindowFull(getActivity())) { return true; } return false; } - @Override - public void onConfigurationChanged(Configuration newConfig) { - super.onConfigurationChanged(newConfig); - //如果旋转了就全屏 - if (newConfig.orientation != ActivityInfo.SCREEN_ORIENTATION_USER) { - mFull = false; - } else { - mFull = true; - } - - } - @Override public void onPause() { super.onPause(); @@ -138,7 +117,7 @@ public void onResume() { @Override public void onDestroy() { super.onDestroy(); - GSYVideoPlayer.releaseAllVideos(); + GSYVideoManager.releaseAllVideos(); } diff --git a/app/src/main/java/com/example/gsyvideoplayer/holder/RecyclerItemBaseHolder.java b/app/src/main/java/com/example/gsyvideoplayer/holder/RecyclerItemBaseHolder.java index b1776f44b..c7626ccaa 100644 --- a/app/src/main/java/com/example/gsyvideoplayer/holder/RecyclerItemBaseHolder.java +++ b/app/src/main/java/com/example/gsyvideoplayer/holder/RecyclerItemBaseHolder.java @@ -1,10 +1,8 @@ package com.example.gsyvideoplayer.holder; -import android.support.v7.widget.RecyclerView; import android.view.View; -import com.example.gsyvideoplayer.adapter.RecyclerBaseAdapter; -import com.shuyu.gsyvideoplayer.utils.ListVideoUtil; +import androidx.recyclerview.widget.RecyclerView; /** * Created by shuyu on 2016/12/3. @@ -14,8 +12,6 @@ public class RecyclerItemBaseHolder extends RecyclerView.ViewHolder { RecyclerView.Adapter recyclerBaseAdapter; - ListVideoUtil listVideoUtil; - public RecyclerItemBaseHolder(View itemView) { super(itemView); } @@ -27,12 +23,4 @@ public RecyclerView.Adapter getRecyclerBaseAdapter() { public void setRecyclerBaseAdapter(RecyclerView.Adapter recyclerBaseAdapter) { this.recyclerBaseAdapter = recyclerBaseAdapter; } - - public ListVideoUtil getListVideoUtil() { - return listVideoUtil; - } - - public void setListVideoUtil(ListVideoUtil listVideoUtil) { - this.listVideoUtil = listVideoUtil; - } } diff --git a/app/src/main/java/com/example/gsyvideoplayer/holder/RecyclerItemNormalHolder.java b/app/src/main/java/com/example/gsyvideoplayer/holder/RecyclerItemNormalHolder.java index fb9d29e23..fe59a5c03 100644 --- a/app/src/main/java/com/example/gsyvideoplayer/holder/RecyclerItemNormalHolder.java +++ b/app/src/main/java/com/example/gsyvideoplayer/holder/RecyclerItemNormalHolder.java @@ -6,12 +6,15 @@ import android.widget.ImageView; import com.example.gsyvideoplayer.R; -import com.example.gsyvideoplayer.listener.SampleListener; import com.example.gsyvideoplayer.model.VideoModel; import com.shuyu.gsyvideoplayer.GSYVideoManager; -import com.shuyu.gsyvideoplayer.utils.Debuger; +import com.shuyu.gsyvideoplayer.builder.GSYVideoOptionBuilder; +import com.shuyu.gsyvideoplayer.listener.GSYSampleCallBack; import com.shuyu.gsyvideoplayer.video.StandardGSYVideoPlayer; +import java.util.HashMap; +import java.util.Map; + import butterknife.BindView; import butterknife.ButterKnife; @@ -24,17 +27,20 @@ public class RecyclerItemNormalHolder extends RecyclerItemBaseHolder { public final static String TAG = "RecyclerView2List"; protected Context context = null; - + @BindView(R.id.video_item_player) StandardGSYVideoPlayer gsyVideoPlayer; ImageView imageView; + GSYVideoOptionBuilder gsyVideoOptionBuilder; + public RecyclerItemNormalHolder(Context context, View v) { super(v); this.context = context; ButterKnife.bind(this, v); imageView = new ImageView(context); + gsyVideoOptionBuilder = new GSYVideoOptionBuilder(); } public void onBind(final int position, VideoModel videoModel) { @@ -47,15 +53,64 @@ public void onBind(final int position, VideoModel videoModel) { imageView.setImageResource(R.mipmap.xxx2); } if (imageView.getParent() != null) { - ViewGroup viewGroup = (ViewGroup)imageView.getParent(); + ViewGroup viewGroup = (ViewGroup) imageView.getParent(); viewGroup.removeView(imageView); } - gsyVideoPlayer.setThumbImageView(imageView); + String url; + String title; + if (position % 2 == 0) { + url = "https://res.exexm.com/cw_145225549855002"; + title = "这是title"; + } else { + url = "http://9890.vod.myqcloud.com/9890_4e292f9a3dd011e6b4078980237cc3d3.f20.mp4"; + title = "哦?Title?"; + } + - final String url = "http://baobab.wdjcdn.com/14564977406580.mp4"; + Map header = new HashMap<>(); + header.put("ee", "33"); + + //防止错位,离开释放 + //gsyVideoPlayer.initUIState(); + gsyVideoOptionBuilder + .setIsTouchWiget(false) + .setThumbImageView(imageView) + .setUrl(url) + .setVideoTitle(title) + .setCacheWithPlay(false) + .setRotateViewAuto(true) + .setLockLand(true) + .setPlayTag(TAG) + .setMapHeadData(header) + .setShowFullAnimation(true) + .setNeedLockFull(true) + .setPlayPosition(position) + .setVideoAllCallBack(new GSYSampleCallBack() { + @Override + public void onPrepared(String url, Object... objects) { + super.onPrepared(url, objects); + if (!gsyVideoPlayer.isIfCurrentIsFullscreen()) { + //静音 + GSYVideoManager.instance().setNeedMute(true); + } + + } + + @Override + public void onQuitFullscreen(String url, Object... objects) { + super.onQuitFullscreen(url, objects); + //全屏不静音 + GSYVideoManager.instance().setNeedMute(true); + } + + @Override + public void onEnterFullscreen(String url, Object... objects) { + super.onEnterFullscreen(url, objects); + GSYVideoManager.instance().setNeedMute(false); + gsyVideoPlayer.getCurrentPlayer().getTitleTextView().setText((String)objects[0]); + } + }).build(gsyVideoPlayer); - //默认缓存路径 - gsyVideoPlayer.setUp(url, true , null, "这是title"); //增加title gsyVideoPlayer.getTitleTextView().setVisibility(View.GONE); @@ -70,42 +125,6 @@ public void onClick(View v) { resolveFullBtn(gsyVideoPlayer); } }); - gsyVideoPlayer.setRotateViewAuto(true); - gsyVideoPlayer.setLockLand(true); - gsyVideoPlayer.setPlayTag(TAG); - gsyVideoPlayer.setShowFullAnimation(true); - //循环 - //gsyVideoPlayer.setLooping(true); - gsyVideoPlayer.setNeedLockFull(true); - - //gsyVideoPlayer.setSpeed(2); - - gsyVideoPlayer.setPlayPosition(position); - - gsyVideoPlayer.setStandardVideoAllCallBack(new SampleListener(){ - @Override - public void onPrepared(String url, Object... objects) { - super.onPrepared(url, objects); - if (!gsyVideoPlayer.isIfCurrentIsFullscreen()) { - //静音 - GSYVideoManager.instance().setNeedMute(true); - } - - } - - @Override - public void onQuitFullscreen(String url, Object... objects) { - super.onQuitFullscreen(url, objects); - //全屏不静音 - GSYVideoManager.instance().setNeedMute(true); - } - - @Override - public void onEnterFullscreen(String url, Object... objects) { - super.onEnterFullscreen(url, objects); - GSYVideoManager.instance().setNeedMute(false); - } - }); } /** diff --git a/app/src/main/java/com/example/gsyvideoplayer/holder/RecyclerItemViewHolder.java b/app/src/main/java/com/example/gsyvideoplayer/holder/RecyclerItemViewHolder.java index cce91256f..6d922c464 100644 --- a/app/src/main/java/com/example/gsyvideoplayer/holder/RecyclerItemViewHolder.java +++ b/app/src/main/java/com/example/gsyvideoplayer/holder/RecyclerItemViewHolder.java @@ -1,14 +1,13 @@ package com.example.gsyvideoplayer.holder; import android.content.Context; -import android.support.v7.widget.RecyclerView; import android.view.View; import android.widget.FrameLayout; import android.widget.ImageView; import com.example.gsyvideoplayer.R; -import com.example.gsyvideoplayer.adapter.ListVideoAdapter; import com.example.gsyvideoplayer.model.VideoModel; +import com.shuyu.gsyvideoplayer.utils.GSYVideoHelper; import butterknife.BindView; import butterknife.ButterKnife; @@ -21,13 +20,19 @@ public class RecyclerItemViewHolder extends RecyclerItemBaseHolder { public final static String TAG = "RecyclerView2List"; protected Context context = null; + @BindView(R.id.list_item_container) FrameLayout listItemContainer; + @BindView(R.id.list_item_btn) ImageView listItemBtn; ImageView imageView; + private GSYVideoHelper smallVideoHelper; + + private GSYVideoHelper.GSYVideoHelperBuilder gsySmallVideoHelperBuilder; + public RecyclerItemViewHolder(Context context, View v) { super(v); this.context = context; @@ -41,18 +46,25 @@ public void onBind(final int position, VideoModel videoModel) { imageView.setScaleType(ImageView.ScaleType.CENTER_CROP); imageView.setImageResource(R.mipmap.xxx1); - listVideoUtil.addVideoPlayer(position, imageView, TAG, listItemContainer, listItemBtn); + smallVideoHelper.addVideoPlayer(position, imageView, TAG, listItemContainer, listItemBtn); listItemBtn.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { + smallVideoHelper.setPlayPositionAndTag(position, TAG); getRecyclerBaseAdapter().notifyDataSetChanged(); //listVideoUtil.setLoop(true); - listVideoUtil.setPlayPositionAndTag(position, TAG); - listVideoUtil.setTitle("title " + position); - final String url = "http://baobab.wdjcdn.com/14564977406580.mp4"; + String url; + if (position % 2 == 0) { + url = "https://res.exexm.com/cw_145225549855002"; + } else { + url = "http://7xjmzj.com1.z0.glb.clouddn.com/20171026175005_JObCxCE2.mp4"; + } //listVideoUtil.setCachePath(new File(FileUtils.getPath())); - listVideoUtil.startPlay(url); + + gsySmallVideoHelperBuilder.setVideoTitle("title " + position).setUrl(url); + + smallVideoHelper.startPlay(); //必须在startPlay之后设置才能生效 //listVideoUtil.getGsyVideoPlayer().getTitleTextView().setVisibility(View.VISIBLE); @@ -60,6 +72,11 @@ public void onClick(View v) { }); } + + public void setVideoHelper(GSYVideoHelper smallVideoHelper, GSYVideoHelper.GSYVideoHelperBuilder gsySmallVideoHelperBuilder) { + this.smallVideoHelper = smallVideoHelper; + this.gsySmallVideoHelperBuilder = gsySmallVideoHelperBuilder; + } } diff --git a/app/src/main/java/com/example/gsyvideoplayer/listener/AppBarStateChangeListener.java b/app/src/main/java/com/example/gsyvideoplayer/listener/AppBarStateChangeListener.java new file mode 100644 index 000000000..b8140123f --- /dev/null +++ b/app/src/main/java/com/example/gsyvideoplayer/listener/AppBarStateChangeListener.java @@ -0,0 +1,39 @@ +package com.example.gsyvideoplayer.listener; + + +import com.google.android.material.appbar.AppBarLayout; +import com.shuyu.gsyvideoplayer.utils.Debuger; + +public abstract class AppBarStateChangeListener implements AppBarLayout.OnOffsetChangedListener { + + public enum State { + EXPANDED, + COLLAPSED, + IDLE + } + + private State mCurrentState = State.IDLE; + + @Override + public final void onOffsetChanged(AppBarLayout appBarLayout, int i) { + if (i == 0) { + if (mCurrentState != State.EXPANDED) { + onStateChanged(appBarLayout, State.EXPANDED); + } + mCurrentState = State.EXPANDED; + } else if (Math.abs(i) >= appBarLayout.getTotalScrollRange()) { + if (mCurrentState != State.COLLAPSED) { + onStateChanged(appBarLayout, State.COLLAPSED); + } + mCurrentState = State.COLLAPSED; + } else { + if (mCurrentState != State.IDLE) { + onStateChanged(appBarLayout, State.IDLE); + } + mCurrentState = State.IDLE; + } + appBarLayout.addOnOffsetChangedListener(this); + } + + public abstract void onStateChanged(AppBarLayout appBarLayout, State state); +} \ No newline at end of file diff --git a/app/src/main/java/com/example/gsyvideoplayer/listener/SampleListener.java b/app/src/main/java/com/example/gsyvideoplayer/listener/SampleListener.java index 24a9f8623..e97bb7422 100644 --- a/app/src/main/java/com/example/gsyvideoplayer/listener/SampleListener.java +++ b/app/src/main/java/com/example/gsyvideoplayer/listener/SampleListener.java @@ -1,12 +1,18 @@ package com.example.gsyvideoplayer.listener; -import com.shuyu.gsyvideoplayer.listener.StandardVideoAllCallBack; +import com.shuyu.gsyvideoplayer.listener.VideoAllCallBack; /** * Created by shuyu on 2016/11/23. */ -public class SampleListener implements StandardVideoAllCallBack { +public class SampleListener implements VideoAllCallBack { + + //开始加载,objects[0]是title,object[1]是当前所处播放器(全屏或非全屏) + @Override + public void onStartPrepared(String url, Object... objects) { + + } //加载成功,objects[0]是title,object[1]是当前所处播放器(全屏或非全屏) @Override diff --git a/app/src/main/java/com/example/gsyvideoplayer/mediacodec/MediaCodecRenderView.java b/app/src/main/java/com/example/gsyvideoplayer/mediacodec/MediaCodecRenderView.java new file mode 100644 index 000000000..3cef5990b --- /dev/null +++ b/app/src/main/java/com/example/gsyvideoplayer/mediacodec/MediaCodecRenderView.java @@ -0,0 +1,24 @@ +package com.example.gsyvideoplayer.mediacodec; + +import android.content.Context; +import android.view.ViewGroup; + +import com.example.gsyvideoplayer.view.CustomTextureSurface; +import com.shuyu.gsyvideoplayer.render.GSYRenderView; +import com.shuyu.gsyvideoplayer.render.glrender.GSYVideoGLViewBaseRender; +import com.shuyu.gsyvideoplayer.render.view.GSYVideoGLView; +import com.shuyu.gsyvideoplayer.render.view.listener.IGSYSurfaceListener; +import com.shuyu.gsyvideoplayer.utils.MeasureHelper; + +/** + * 自定义代理渲染层 + * Created by guoshuyu on 2018/1/30. + */ + +public class MediaCodecRenderView extends GSYRenderView { + + @Override + public void addView(Context context, ViewGroup textureViewContainer, int rotate, IGSYSurfaceListener gsySurfaceListener, MeasureHelper.MeasureFormVideoParamsListener videoParamsListener, GSYVideoGLView.ShaderInterface effect, float[] transform, GSYVideoGLViewBaseRender customRender, int mode) { + mShowView = MediaCodecTextureView.addTextureView(context, textureViewContainer, rotate, gsySurfaceListener, videoParamsListener); + } +} diff --git a/app/src/main/java/com/example/gsyvideoplayer/mediacodec/MediaCodecTextureView.java b/app/src/main/java/com/example/gsyvideoplayer/mediacodec/MediaCodecTextureView.java new file mode 100644 index 000000000..cca880dd3 --- /dev/null +++ b/app/src/main/java/com/example/gsyvideoplayer/mediacodec/MediaCodecTextureView.java @@ -0,0 +1,295 @@ +package com.example.gsyvideoplayer.mediacodec; + +import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.Matrix; +import android.graphics.SurfaceTexture; +import android.util.AttributeSet; +import android.view.Surface; +import android.view.TextureView; +import android.view.View; +import android.view.ViewGroup; + +import com.shuyu.gsyvideoplayer.listener.GSYVideoShotListener; +import com.shuyu.gsyvideoplayer.listener.GSYVideoShotSaveListener; +import com.shuyu.gsyvideoplayer.render.GSYRenderView; +import com.shuyu.gsyvideoplayer.render.glrender.GSYVideoGLViewBaseRender; +import com.shuyu.gsyvideoplayer.render.view.GSYVideoGLView; +import com.shuyu.gsyvideoplayer.render.view.IGSYRenderView; +import com.shuyu.gsyvideoplayer.render.view.listener.IGSYSurfaceListener; +import com.shuyu.gsyvideoplayer.utils.Debuger; +import com.shuyu.gsyvideoplayer.utils.FileUtils; +import com.shuyu.gsyvideoplayer.utils.MeasureHelper; + +import java.io.File; + +/** + * 用于硬解码 + * Created by shuyu on 2016/11/11. + */ + +public class MediaCodecTextureView extends TextureView implements TextureView.SurfaceTextureListener, IGSYRenderView, MeasureHelper.MeasureFormVideoParamsListener { + + private IGSYSurfaceListener mIGSYSurfaceListener; + + private MeasureHelper.MeasureFormVideoParamsListener mVideoParamsListener; + + private MeasureHelper measureHelper; + + private SurfaceTexture mSaveTexture; + + private Surface mSurface; + + public MediaCodecTextureView(Context context) { + super(context); + init(); + } + + public MediaCodecTextureView(Context context, AttributeSet attrs) { + super(context, attrs); + init(); + } + + private void init() { + measureHelper = new MeasureHelper(this, this); + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + measureHelper.prepareMeasure(widthMeasureSpec, heightMeasureSpec, (int) getRotation()); + setMeasuredDimension(measureHelper.getMeasuredWidth(), measureHelper.getMeasuredHeight()); + } + + @Override + public void onSurfaceTextureAvailable(SurfaceTexture texture, int width, int height) { + if (mSaveTexture == null) { + mSaveTexture = texture; + mSurface = new Surface(texture); + if (mIGSYSurfaceListener != null) { + mIGSYSurfaceListener.onSurfaceAvailable(mSurface); + } + } else { + setSurfaceTexture(mSaveTexture); + if (mIGSYSurfaceListener != null) { + mIGSYSurfaceListener.onSurfaceAvailable(mSurface); + } + } + } + + @Override + public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) { + if (mIGSYSurfaceListener != null) { + mIGSYSurfaceListener.onSurfaceSizeChanged(mSurface, width, height); + } + } + + @Override + public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) { + //清空释放 + if (mIGSYSurfaceListener != null) { + mIGSYSurfaceListener.onSurfaceDestroyed(mSurface); + } + return (mSaveTexture == null); + } + + @Override + public void onSurfaceTextureUpdated(SurfaceTexture surface) { + //如果播放的是暂停全屏了 + if (mIGSYSurfaceListener != null) { + mIGSYSurfaceListener.onSurfaceUpdated(mSurface); + } + } + + @Override + public IGSYSurfaceListener getIGSYSurfaceListener() { + return mIGSYSurfaceListener; + } + + @Override + public void setIGSYSurfaceListener(IGSYSurfaceListener surfaceListener) { + setSurfaceTextureListener(this); + mIGSYSurfaceListener = surfaceListener; + } + + @Override + public int getSizeH() { + return getHeight(); + } + + @Override + public int getSizeW() { + return getWidth(); + } + + /** + * 暂停时初始化位图 + */ + @Override + public Bitmap initCover() { + Bitmap bitmap = Bitmap.createBitmap( + getSizeW(), getSizeH(), Bitmap.Config.RGB_565); + return getBitmap(bitmap); + + } + + /** + * 暂停时初始化位图 + */ + @Override + public Bitmap initCoverHigh() { + Bitmap bitmap = Bitmap.createBitmap( + getSizeW(), getSizeH(), Bitmap.Config.ARGB_8888); + return getBitmap(bitmap); + + } + + + /** + * 获取截图 + * + * @param shotHigh 是否需要高清的 + */ + @Override + public void taskShotPic(GSYVideoShotListener gsyVideoShotListener, boolean shotHigh) { + if (shotHigh) { + gsyVideoShotListener.getBitmap(initCoverHigh()); + } else { + gsyVideoShotListener.getBitmap(initCover()); + } + } + + /** + * 保存截图 + * + * @param high 是否需要高清的 + */ + @Override + public void saveFrame(final File file, final boolean high, final GSYVideoShotSaveListener gsyVideoShotSaveListener) { + GSYVideoShotListener gsyVideoShotListener = new GSYVideoShotListener() { + @Override + public void getBitmap(Bitmap bitmap) { + if (bitmap == null) { + gsyVideoShotSaveListener.result(false, file); + } else { + FileUtils.saveBitmap(bitmap, file); + gsyVideoShotSaveListener.result(true, file); + } + } + }; + if (high) { + gsyVideoShotListener.getBitmap(initCoverHigh()); + } else { + gsyVideoShotListener.getBitmap(initCover()); + } + + } + + + @Override + public View getRenderView() { + return this; + } + + @Override + public void onRenderResume() { + Debuger.printfLog(getClass().getSimpleName() + " not support onRenderResume now"); + } + + @Override + public void onRenderPause() { + Debuger.printfLog(getClass().getSimpleName() + " not support onRenderPause now"); + } + + @Override + public void releaseRenderAll() { + Debuger.printfLog(getClass().getSimpleName() + " not support releaseRenderAll now"); + } + + @Override + public void setRenderMode(int mode) { + Debuger.printfLog(getClass().getSimpleName() + " not support setRenderMode now"); + } + + @Override + public void setRenderTransform(Matrix transform) { + setTransform(transform); + } + + @Override + public void setGLRenderer(GSYVideoGLViewBaseRender renderer) { + Debuger.printfLog(getClass().getSimpleName() + " not support setGLRenderer now"); + } + + @Override + public void setGLMVPMatrix(float[] MVPMatrix) { + Debuger.printfLog(getClass().getSimpleName() + " not support setGLMVPMatrix now"); + } + + /** + * 设置滤镜效果 + */ + @Override + public void setGLEffectFilter(GSYVideoGLView.ShaderInterface effectFilter) { + Debuger.printfLog(getClass().getSimpleName() + " not support setGLEffectFilter now"); + } + + + @Override + public void setVideoParamsListener(MeasureHelper.MeasureFormVideoParamsListener listener) { + mVideoParamsListener = listener; + } + + @Override + public int getCurrentVideoWidth() { + if (mVideoParamsListener != null) { + return mVideoParamsListener.getCurrentVideoWidth(); + } + return 0; + } + + @Override + public int getCurrentVideoHeight() { + if (mVideoParamsListener != null) { + return mVideoParamsListener.getCurrentVideoHeight(); + } + return 0; + } + + @Override + public int getVideoSarNum() { + if (mVideoParamsListener != null) { + return mVideoParamsListener.getVideoSarNum(); + } + return 0; + } + + @Override + public int getVideoSarDen() { + if (mVideoParamsListener != null) { + return mVideoParamsListener.getVideoSarDen(); + } + return 0; + } + + + public void release() { + mSaveTexture = null; + } + + /** + * 添加播放的view + */ + public static MediaCodecTextureView addTextureView(Context context, ViewGroup textureViewContainer, int rotate, + final IGSYSurfaceListener gsySurfaceListener, + final MeasureHelper.MeasureFormVideoParamsListener videoParamsListener) { + if (textureViewContainer.getChildCount() > 0) { + textureViewContainer.removeAllViews(); + } + MediaCodecTextureView gsyTextureView = new MediaCodecTextureView(context); + gsyTextureView.setIGSYSurfaceListener(gsySurfaceListener); + gsyTextureView.setVideoParamsListener(videoParamsListener); + gsyTextureView.setRotation(rotate); + GSYRenderView.addToParent(textureViewContainer, gsyTextureView); + return gsyTextureView; + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/gsyvideoplayer/mediacodec/MediaCodecVideo.java b/app/src/main/java/com/example/gsyvideoplayer/mediacodec/MediaCodecVideo.java new file mode 100644 index 000000000..32935f531 --- /dev/null +++ b/app/src/main/java/com/example/gsyvideoplayer/mediacodec/MediaCodecVideo.java @@ -0,0 +1,51 @@ +package com.example.gsyvideoplayer.mediacodec; + +import android.content.Context; +import android.util.AttributeSet; +import android.view.Surface; + +import com.shuyu.gsyvideoplayer.video.StandardGSYVideoPlayer; +import com.shuyu.gsyvideoplayer.video.base.GSYBaseVideoPlayer; + +public class MediaCodecVideo extends StandardGSYVideoPlayer { + + + public MediaCodecVideo(Context context, Boolean fullFlag) { + super(context, fullFlag); + } + + public MediaCodecVideo(Context context) { + super(context); + } + + public MediaCodecVideo(Context context, AttributeSet attrs) { + super(context, attrs); + } + + + @Override + public GSYBaseVideoPlayer startWindowFullscreen(Context context, boolean actionBar, boolean statusBar) { + return super.startWindowFullscreen(context, actionBar, statusBar); + } + + @Override + public void onSurfaceAvailable(Surface surface) { + super.onSurfaceAvailable(surface); + } + + @Override + public boolean onSurfaceDestroyed(Surface surface) { + //清空释放 + //setDisplay(null); + //同一消息队列中去release + //releaseSurface(surface); + return true; + } + + @Override + protected void addTextureView() { + mTextureView = new MediaCodecRenderView(); + mTextureView.addView(getContext(), mTextureViewContainer, mRotate, this, this, mEffectFilter, mMatrixGL, mRenderer, mMode); + } + +} diff --git a/app/src/main/java/com/example/gsyvideoplayer/simple/SimpleActivity.java b/app/src/main/java/com/example/gsyvideoplayer/simple/SimpleActivity.java new file mode 100644 index 000000000..d0ecc2112 --- /dev/null +++ b/app/src/main/java/com/example/gsyvideoplayer/simple/SimpleActivity.java @@ -0,0 +1,63 @@ +package com.example.gsyvideoplayer.simple; + +import android.Manifest; +import android.content.Intent; +import android.os.Build; +import android.os.Bundle; +import androidx.annotation.NonNull; +import androidx.appcompat.app.AppCompatActivity; +import android.view.View; +import android.widget.Toast; + +import com.example.gsyvideoplayer.R; + +import butterknife.ButterKnife; +import butterknife.OnClick; +import permissions.dispatcher.PermissionUtils; + +public class SimpleActivity extends AppCompatActivity { + + final String[] permissions = {Manifest.permission.WRITE_EXTERNAL_STORAGE}; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_simple); + ButterKnife.bind(this); + boolean hadPermission = PermissionUtils.hasSelfPermissions(this, permissions); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && !hadPermission) { + String[] permissions = {Manifest.permission.WRITE_EXTERNAL_STORAGE}; + requestPermissions(permissions, 1110); + } + } + + @Override + public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { + super.onRequestPermissionsResult(requestCode, permissions, grantResults); + boolean sdPermissionResult = PermissionUtils.verifyPermissions(grantResults); + if (!sdPermissionResult) { + Toast.makeText(this, "没获取到sd卡权限,无法播放本地视频哦", Toast.LENGTH_LONG).show(); + } + } + + @OnClick({R.id.simple_list_1, R.id.simple_list_2, R.id.simple_detail_1, R.id.simple_detail_2, R.id.simple_player}) + public void onClick(View view) { + switch (view.getId()) { + case R.id.simple_player: + startActivity(new Intent(this, SimplePlayer.class)); + break; + case R.id.simple_list_1: + startActivity(new Intent(this, SimpleListVideoActivityMode1.class)); + break; + case R.id.simple_list_2: + startActivity(new Intent(this, SimpleListVideoActivityMode2.class)); + break; + case R.id.simple_detail_1: + startActivity(new Intent(this, SimpleDetailActivityMode1.class)); + break; + case R.id.simple_detail_2: + startActivity(new Intent(this, SimpleDetailActivityMode2.class)); + break; + } + } +} diff --git a/app/src/main/java/com/example/gsyvideoplayer/simple/SimpleDetailActivityMode1.java b/app/src/main/java/com/example/gsyvideoplayer/simple/SimpleDetailActivityMode1.java new file mode 100644 index 000000000..65811e8f7 --- /dev/null +++ b/app/src/main/java/com/example/gsyvideoplayer/simple/SimpleDetailActivityMode1.java @@ -0,0 +1,89 @@ +package com.example.gsyvideoplayer.simple; + +import android.os.Bundle; +import android.view.View; +import android.widget.ImageView; +import com.bumptech.glide.Glide; +import com.bumptech.glide.request.RequestOptions; +import com.example.gsyvideoplayer.R; +import com.shuyu.gsyvideoplayer.GSYBaseActivityDetail; +import com.shuyu.gsyvideoplayer.builder.GSYVideoOptionBuilder; +import com.shuyu.gsyvideoplayer.video.StandardGSYVideoPlayer; + +/** + * 简单详情实现模式1 + */ +public class SimpleDetailActivityMode1 extends GSYBaseActivityDetail { + + StandardGSYVideoPlayer detailPlayer; + +// private String url = "http://7xjmzj.com1.z0.glb.clouddn.com/20171026175005_JObCxCE2.mp4"; + private String url = "http://alvideo.ippzone.com/zyvd/98/90/b753-55fe-11e9-b0d8-00163e0c0248"; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_simple_detail_player); + + detailPlayer = (StandardGSYVideoPlayer) findViewById(R.id.detail_player); + //增加title + detailPlayer.getTitleTextView().setVisibility(View.GONE); + detailPlayer.getBackButton().setVisibility(View.GONE); + + initVideoBuilderMode(); + + } + + @Override + public StandardGSYVideoPlayer getGSYVideoPlayer() { + return detailPlayer; + } + + @Override + public GSYVideoOptionBuilder getGSYVideoOptionBuilder() { + //内置封面可参考SampleCoverVideo + ImageView imageView = new ImageView(this); + //loadCover(imageView, url); + return new GSYVideoOptionBuilder() + .setThumbImageView(imageView) + .setUrl(url) + .setCacheWithPlay(true) + .setVideoTitle("这里是一个竖直方向的视频") + .setIsTouchWiget(true) + //.setAutoFullWithSize(true) + .setRotateViewAuto(false) + .setLockLand(false) + .setShowFullAnimation(false)//打开动画 + .setNeedLockFull(true) + .setSeekRatio(1); + } + + @Override + public void clickForFullScreen() { + + } + + + /** + * 是否启动旋转横屏,true表示启动 + */ + @Override + public boolean getDetailOrientationRotateAuto() { + return true; + } + + private void loadCover(ImageView imageView, String url) { + imageView.setScaleType(ImageView.ScaleType.CENTER_CROP); + imageView.setImageResource(R.mipmap.xxx1); + Glide.with(this.getApplicationContext()) + .setDefaultRequestOptions( + new RequestOptions() + .frame(3000000) + .centerCrop() + .error(R.mipmap.xxx2) + .placeholder(R.mipmap.xxx1)) + .load(url) + .into(imageView); + } + +} diff --git a/app/src/main/java/com/example/gsyvideoplayer/simple/SimpleDetailActivityMode2.java b/app/src/main/java/com/example/gsyvideoplayer/simple/SimpleDetailActivityMode2.java new file mode 100644 index 000000000..99b6c3921 --- /dev/null +++ b/app/src/main/java/com/example/gsyvideoplayer/simple/SimpleDetailActivityMode2.java @@ -0,0 +1,148 @@ +package com.example.gsyvideoplayer.simple; + +import android.content.res.Configuration; +import android.os.Bundle; +import androidx.appcompat.app.AppCompatActivity; +import android.view.View; +import android.widget.ImageView; + +import com.example.gsyvideoplayer.R; +import com.shuyu.gsyvideoplayer.GSYVideoManager; +import com.shuyu.gsyvideoplayer.builder.GSYVideoOptionBuilder; +import com.shuyu.gsyvideoplayer.listener.GSYSampleCallBack; +import com.shuyu.gsyvideoplayer.listener.LockClickListener; +import com.shuyu.gsyvideoplayer.utils.OrientationUtils; +import com.shuyu.gsyvideoplayer.video.StandardGSYVideoPlayer; + +/** + * 简单详情实现模式2 + */ +public class SimpleDetailActivityMode2 extends AppCompatActivity { + + + StandardGSYVideoPlayer detailPlayer; + + private boolean isPlay; + private boolean isPause; + + private OrientationUtils orientationUtils; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_simple_detail_player); + + detailPlayer = (StandardGSYVideoPlayer) findViewById(R.id.detail_player); + + String url = "http://7xjmzj.com1.z0.glb.clouddn.com/20171026175005_JObCxCE2.mp4"; + + //增加封面 + ImageView imageView = new ImageView(this); + imageView.setScaleType(ImageView.ScaleType.CENTER_CROP); + imageView.setImageResource(R.mipmap.xxx1); + + //增加title + detailPlayer.getTitleTextView().setVisibility(View.GONE); + detailPlayer.getBackButton().setVisibility(View.GONE); + + //外部辅助的旋转,帮助全屏 + orientationUtils = new OrientationUtils(this, detailPlayer); + //初始化不打开外部的旋转 + orientationUtils.setEnable(false); + + GSYVideoOptionBuilder gsyVideoOption = new GSYVideoOptionBuilder(); + gsyVideoOption.setThumbImageView(imageView) + .setIsTouchWiget(true) + .setRotateViewAuto(false) + .setLockLand(false) + .setAutoFullWithSize(false) + .setShowFullAnimation(false) + .setNeedLockFull(true) + .setUrl(url) + .setCacheWithPlay(false) + .setVideoTitle("测试视频") + .setVideoAllCallBack(new GSYSampleCallBack() { + @Override + public void onPrepared(String url, Object... objects) { + super.onPrepared(url, objects); + //开始播放了才能旋转和全屏 + orientationUtils.setEnable(true); + isPlay = true; + } + + @Override + public void onQuitFullscreen(String url, Object... objects) { + super.onQuitFullscreen(url, objects); + if (orientationUtils != null) { + orientationUtils.backToProtVideo(); + } + } + }).setLockClickListener(new LockClickListener() { + @Override + public void onClick(View view, boolean lock) { + if (orientationUtils != null) { + //配合下方的onConfigurationChanged + orientationUtils.setEnable(!lock); + } + } + }).build(detailPlayer); + + detailPlayer.getFullscreenButton().setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + //直接横屏 + orientationUtils.resolveByClick(); + //第一个true是否需要隐藏actionbar,第二个true是否需要隐藏statusbar + detailPlayer.startWindowFullscreen(SimpleDetailActivityMode2.this, true, true); + } + }); + } + + @Override + public void onBackPressed() { + if (orientationUtils != null) { + orientationUtils.backToProtVideo(); + } + if (GSYVideoManager.backFromWindowFull(this)) { + return; + } + super.onBackPressed(); + } + + + @Override + protected void onPause() { + detailPlayer.getCurrentPlayer().onVideoPause(); + super.onPause(); + isPause = true; + } + + @Override + protected void onResume() { + detailPlayer.getCurrentPlayer().onVideoResume(false); + super.onResume(); + isPause = false; + } + + @Override + protected void onDestroy() { + super.onDestroy(); + if (isPlay) { + detailPlayer.getCurrentPlayer().release(); + } + if (orientationUtils != null) + orientationUtils.releaseListener(); + } + + + + @Override + public void onConfigurationChanged(Configuration newConfig) { + super.onConfigurationChanged(newConfig); + //如果旋转了就全屏 + if (isPlay && !isPause) { + detailPlayer.onConfigurationChanged(this, newConfig, orientationUtils, true, true); + } + } + +} diff --git a/app/src/main/java/com/example/gsyvideoplayer/simple/SimpleListVideoActivityMode1.java b/app/src/main/java/com/example/gsyvideoplayer/simple/SimpleListVideoActivityMode1.java new file mode 100644 index 000000000..cd39bbd38 --- /dev/null +++ b/app/src/main/java/com/example/gsyvideoplayer/simple/SimpleListVideoActivityMode1.java @@ -0,0 +1,84 @@ +package com.example.gsyvideoplayer.simple; + +import android.os.Bundle; +import androidx.appcompat.app.AppCompatActivity; +import android.widget.AbsListView; +import android.widget.ListView; + +import com.example.gsyvideoplayer.R; +import com.example.gsyvideoplayer.simple.adapter.SimpleListVideoModeAdapter; +import com.shuyu.gsyvideoplayer.GSYVideoManager; + +/** + * 简单列表实现模式1 + */ +public class SimpleListVideoActivityMode1 extends AppCompatActivity { + + ListView videoList; + + SimpleListVideoModeAdapter listNormalAdapter; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_list_video); + + videoList = (ListView)findViewById(R.id.video_list); + + listNormalAdapter = new SimpleListVideoModeAdapter(this); + videoList.setAdapter(listNormalAdapter); + + videoList.setOnScrollListener(new AbsListView.OnScrollListener() { + @Override + public void onScrollStateChanged(AbsListView view, int scrollState) { + } + + @Override + public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { + int lastVisibleItem = firstVisibleItem + visibleItemCount; + //大于0说明有播放 + if (GSYVideoManager.instance().getPlayPosition() >= 0) { + //当前播放的位置 + int position = GSYVideoManager.instance().getPlayPosition(); + //对应的播放列表TAG + if (GSYVideoManager.instance().getPlayTag().equals(SimpleListVideoModeAdapter.TAG) + && (position < firstVisibleItem || position > lastVisibleItem)) { + if(GSYVideoManager.isFullState(SimpleListVideoActivityMode1.this)) { + return; + } + //如果滑出去了上面和下面就是否,和今日头条一样 + GSYVideoManager.releaseAllVideos(); + listNormalAdapter.notifyDataSetChanged(); + } + } + } + }); + + } + + @Override + public void onBackPressed() { + if (GSYVideoManager.backFromWindowFull(this)) { + return; + } + super.onBackPressed(); + } + + @Override + protected void onPause() { + super.onPause(); + GSYVideoManager.onPause(); + } + + @Override + protected void onResume() { + super.onResume(); + GSYVideoManager.onResume(); + } + + @Override + protected void onDestroy() { + super.onDestroy(); + GSYVideoManager.releaseAllVideos(); + } +} diff --git a/app/src/main/java/com/example/gsyvideoplayer/simple/SimpleListVideoActivityMode2.java b/app/src/main/java/com/example/gsyvideoplayer/simple/SimpleListVideoActivityMode2.java new file mode 100644 index 000000000..7fc90f78b --- /dev/null +++ b/app/src/main/java/com/example/gsyvideoplayer/simple/SimpleListVideoActivityMode2.java @@ -0,0 +1,131 @@ +package com.example.gsyvideoplayer.simple; + +import android.graphics.Point; +import android.os.Bundle; +import androidx.appcompat.app.AppCompatActivity; +import android.widget.AbsListView; +import android.widget.ListView; +import android.widget.RelativeLayout; + +import com.example.gsyvideoplayer.R; +import com.example.gsyvideoplayer.simple.adapter.SimpleListVideoMode2Adapter; +import com.shuyu.gsyvideoplayer.GSYVideoManager; +import com.shuyu.gsyvideoplayer.listener.GSYSampleCallBack; +import com.shuyu.gsyvideoplayer.utils.CommonUtil; +import com.shuyu.gsyvideoplayer.utils.GSYVideoHelper; + +/** + * 简单列表实现模式2 + */ +public class SimpleListVideoActivityMode2 extends AppCompatActivity { + + + ListView videoList; + + RelativeLayout activityListVideo; + + GSYVideoHelper smallVideoHelper; + + SimpleListVideoMode2Adapter listVideoAdapter; + + GSYVideoHelper.GSYVideoHelperBuilder gsySmallVideoHelperBuilder; + + int lastVisibleItem; + + int firstVisibleItem; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_list_video2); + + videoList = (ListView)findViewById(R.id.video_list) ; + activityListVideo = (RelativeLayout)findViewById(R.id.activity_list_video) ; + + //创建小窗口帮助类 + smallVideoHelper = new GSYVideoHelper(this); + + //配置 + gsySmallVideoHelperBuilder = new GSYVideoHelper.GSYVideoHelperBuilder(); + gsySmallVideoHelperBuilder + .setHideStatusBar(true) + .setNeedLockFull(true) + .setCacheWithPlay(true) + .setShowFullAnimation(false) + .setRotateViewAuto(false) + .setLockLand(true) + .setVideoAllCallBack(new GSYSampleCallBack(){ + @Override + public void onQuitSmallWidget(String url, Object... objects) { + super.onQuitSmallWidget(url, objects); + //大于0说明有播放,//对应的播放列表TAG + if (smallVideoHelper.getPlayPosition() >= 0 && smallVideoHelper.getPlayTAG().equals(SimpleListVideoMode2Adapter.TAG)) { + //当前播放的位置 + int position = smallVideoHelper.getPlayPosition(); + //不可视的是时候 + if ((position < firstVisibleItem || position > lastVisibleItem)) { + //释放掉视频 + smallVideoHelper.releaseVideoPlayer(); + listVideoAdapter.notifyDataSetChanged(); + } + } + + } + }); + + smallVideoHelper.setGsyVideoOptionBuilder(gsySmallVideoHelperBuilder); + + listVideoAdapter = new SimpleListVideoMode2Adapter(this, smallVideoHelper, gsySmallVideoHelperBuilder); + listVideoAdapter.setRootView(activityListVideo); + videoList.setAdapter(listVideoAdapter); + + videoList.setOnScrollListener(new AbsListView.OnScrollListener() { + @Override + public void onScrollStateChanged(AbsListView view, int scrollState) { + } + + @Override + public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { + SimpleListVideoActivityMode2.this.firstVisibleItem = firstVisibleItem; + lastVisibleItem = firstVisibleItem + visibleItemCount; + //大于0说明有播放,//对应的播放列表TAG + if (smallVideoHelper.getPlayPosition() >= 0 && smallVideoHelper.getPlayTAG().equals(SimpleListVideoMode2Adapter.TAG)) { + //当前播放的位置 + int position = smallVideoHelper.getPlayPosition(); + //不可视的是时候 + if ((position < firstVisibleItem || position > lastVisibleItem)) { + //如果是小窗口就不需要处理 + if (!smallVideoHelper.isSmall()) { + //小窗口 + int size = CommonUtil.dip2px(SimpleListVideoActivityMode2.this, 150); + smallVideoHelper.showSmallVideo(new Point(size, size), false, true); + } + } else { + if (smallVideoHelper.isSmall()) { + smallVideoHelper.smallVideoToNormal(); + } + } + } + } + + }); + } + + + @Override + public void onBackPressed() { + if (smallVideoHelper.backFromFull()) { + return; + } + super.onBackPressed(); + } + + + @Override + protected void onDestroy() { + super.onDestroy(); + smallVideoHelper.releaseVideoPlayer(); + GSYVideoManager.releaseAllVideos(); + } + +} diff --git a/app/src/main/java/com/example/gsyvideoplayer/simple/SimplePlayer.java b/app/src/main/java/com/example/gsyvideoplayer/simple/SimplePlayer.java new file mode 100644 index 000000000..6d24ff09b --- /dev/null +++ b/app/src/main/java/com/example/gsyvideoplayer/simple/SimplePlayer.java @@ -0,0 +1,96 @@ +package com.example.gsyvideoplayer.simple; + +import android.content.pm.ActivityInfo; +import android.os.Bundle; +import androidx.appcompat.app.AppCompatActivity; +import android.view.View; +import android.widget.ImageView; + +import com.example.gsyvideoplayer.R; +import com.shuyu.gsyvideoplayer.GSYVideoManager; +import com.shuyu.gsyvideoplayer.utils.OrientationUtils; +import com.shuyu.gsyvideoplayer.video.StandardGSYVideoPlayer; + + +public class SimplePlayer extends AppCompatActivity { + + StandardGSYVideoPlayer videoPlayer; + + OrientationUtils orientationUtils; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_simple_play); + init(); + } + + private void init() { + videoPlayer = (StandardGSYVideoPlayer)findViewById(R.id.video_player); + + String source1 = "http://9890.vod.myqcloud.com/9890_4e292f9a3dd011e6b4078980237cc3d3.f20.mp4"; + videoPlayer.setUp(source1, true, "测试视频"); + + //增加封面 + ImageView imageView = new ImageView(this); + imageView.setScaleType(ImageView.ScaleType.CENTER_CROP); + imageView.setImageResource(R.mipmap.xxx1); + videoPlayer.setThumbImageView(imageView); + //增加title + videoPlayer.getTitleTextView().setVisibility(View.VISIBLE); + //设置返回键 + videoPlayer.getBackButton().setVisibility(View.VISIBLE); + //设置旋转 + orientationUtils = new OrientationUtils(this, videoPlayer); + //设置全屏按键功能,这是使用的是选择屏幕,而不是全屏 + videoPlayer.getFullscreenButton().setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + orientationUtils.resolveByClick(); + } + }); + //是否可以滑动调整 + videoPlayer.setIsTouchWiget(true); + //设置返回按键功能 + videoPlayer.getBackButton().setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + onBackPressed(); + } + }); + videoPlayer.startPlayLogic(); + } + + + @Override + protected void onPause() { + super.onPause(); + videoPlayer.onVideoPause(); + } + + @Override + protected void onResume() { + super.onResume(); + videoPlayer.onVideoResume(); + } + + @Override + protected void onDestroy() { + super.onDestroy(); + GSYVideoManager.releaseAllVideos(); + if (orientationUtils != null) + orientationUtils.releaseListener(); + } + + @Override + public void onBackPressed() { + //先返回正常状态 + if (orientationUtils.getScreenType() == ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE) { + videoPlayer.getFullscreenButton().performClick(); + return; + } + //释放所有 + videoPlayer.setVideoAllCallBack(null); + super.onBackPressed(); + } +} diff --git a/app/src/main/java/com/example/gsyvideoplayer/simple/adapter/SimpleListVideoMode2Adapter.java b/app/src/main/java/com/example/gsyvideoplayer/simple/adapter/SimpleListVideoMode2Adapter.java new file mode 100644 index 000000000..3184d5bf0 --- /dev/null +++ b/app/src/main/java/com/example/gsyvideoplayer/simple/adapter/SimpleListVideoMode2Adapter.java @@ -0,0 +1,110 @@ +package com.example.gsyvideoplayer.simple.adapter; + +import android.content.Context; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.BaseAdapter; +import android.widget.FrameLayout; +import android.widget.ImageView; + +import com.example.gsyvideoplayer.R; +import com.example.gsyvideoplayer.model.VideoModel; +import com.shuyu.gsyvideoplayer.utils.GSYVideoHelper; +import com.shuyu.gsyvideoplayer.utils.OrientationUtils; + +import java.util.ArrayList; +import java.util.List; + +public class SimpleListVideoMode2Adapter extends BaseAdapter { + + public final static String TAG = "TT22"; + + private List list = new ArrayList<>(); + private LayoutInflater inflater = null; + private Context context; + + private ViewGroup rootView; + + private OrientationUtils orientationUtils; + + private boolean isFullVideo; + + private GSYVideoHelper smallVideoHelper; + + private GSYVideoHelper.GSYVideoHelperBuilder gsySmallVideoHelperBuilder; + + public SimpleListVideoMode2Adapter(Context context, GSYVideoHelper smallVideoHelper, GSYVideoHelper.GSYVideoHelperBuilder gsySmallVideoHelperBuilder) { + super(); + this.context = context; + this.smallVideoHelper = smallVideoHelper; + this.gsySmallVideoHelperBuilder = gsySmallVideoHelperBuilder; + + inflater = LayoutInflater.from(context); + for (int i = 0; i < 40; i++) { + list.add(new VideoModel()); + } + + } + + @Override + public int getCount() { + return list.size(); + } + + @Override + public Object getItem(int position) { + return null; + } + + @Override + public long getItemId(int position) { + return 0; + } + + @Override + public View getView(final int position, View convertView, ViewGroup parent) { + final ViewHolder holder; + if (convertView == null) { + holder = new ViewHolder(); + convertView = inflater.inflate(R.layout.list_video_simple_mode2, null); + holder.videoContainer = (FrameLayout) convertView.findViewById(R.id.list_item_container); + holder.playerBtn = (ImageView) convertView.findViewById(R.id.list_item_btn); + holder.imageView = new ImageView(context); + convertView.setTag(holder); + } else { + holder = (ViewHolder) convertView.getTag(); + } + + //增加封面 + holder.imageView.setScaleType(ImageView.ScaleType.CENTER_CROP); + holder.imageView.setImageResource(R.mipmap.xxx1); + + + smallVideoHelper.addVideoPlayer(position, holder.imageView, TAG, holder.videoContainer, holder.playerBtn); + holder.playerBtn.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + notifyDataSetChanged(); + smallVideoHelper.setPlayPositionAndTag(position, TAG); + final String url = "http://9890.vod.myqcloud.com/9890_4e292f9a3dd011e6b4078980237cc3d3.f20.mp4"; + gsySmallVideoHelperBuilder.setVideoTitle("title " + position) + .setUrl(url); + smallVideoHelper.startPlay(); + } + }); + + return convertView; + } + + + class ViewHolder { + FrameLayout videoContainer; + ImageView playerBtn; + ImageView imageView; + } + + public void setRootView(ViewGroup rootView) { + this.rootView = rootView; + } +} diff --git a/app/src/main/java/com/example/gsyvideoplayer/simple/adapter/SimpleListVideoModeAdapter.java b/app/src/main/java/com/example/gsyvideoplayer/simple/adapter/SimpleListVideoModeAdapter.java new file mode 100644 index 000000000..5c9e4d951 --- /dev/null +++ b/app/src/main/java/com/example/gsyvideoplayer/simple/adapter/SimpleListVideoModeAdapter.java @@ -0,0 +1,113 @@ +package com.example.gsyvideoplayer.simple.adapter; + +import android.content.Context; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.BaseAdapter; + +import com.example.gsyvideoplayer.R; +import com.example.gsyvideoplayer.model.VideoModel; +import com.example.gsyvideoplayer.video.SampleCoverVideo; +import com.shuyu.gsyvideoplayer.utils.OrientationUtils; +import com.shuyu.gsyvideoplayer.video.StandardGSYVideoPlayer; + +import java.util.ArrayList; +import java.util.List; + +public class SimpleListVideoModeAdapter extends BaseAdapter { + + public static final String TAG = "ListNormalAdapter22"; + + private List list = new ArrayList<>(); + private LayoutInflater inflater; + private Context context; + + private StandardGSYVideoPlayer curPlayer; + + protected OrientationUtils orientationUtils; + + protected boolean isPlay; + + protected boolean isFull; + + public SimpleListVideoModeAdapter(Context context) { + super(); + this.context = context; + inflater = LayoutInflater.from(context); + for (int i = 0; i < 40; i++) { + list.add(new VideoModel()); + } + + } + + @Override + public int getCount() { + return list.size(); + } + + @Override + public Object getItem(int position) { + return null; + } + + @Override + public long getItemId(int position) { + return 0; + } + + @Override + public View getView(final int position, View convertView, ViewGroup parent) { + final ViewHolder holder; + if (convertView == null) { + holder = new ViewHolder(); + convertView = inflater.inflate(R.layout.list_video_simple_mode1, null); + holder.gsyVideoPlayer = (SampleCoverVideo) convertView.findViewById(R.id.video_item_player); + convertView.setTag(holder); + } else { + holder = (ViewHolder) convertView.getTag(); + } + + final String urlH = "http://9890.vod.myqcloud.com/9890_4e292f9a3dd011e6b4078980237cc3d3.f20.mp4"; + final String urlV = "http://7xjmzj.com1.z0.glb.clouddn.com/20171026175005_JObCxCE2.mp4"; + final String url = (position % 2 == 0) ? urlH : urlV; + + if (position % 2 == 0) { + holder.gsyVideoPlayer.loadCoverImage(url, R.mipmap.xxx1); + } else { + holder.gsyVideoPlayer.loadCoverImage(url, R.mipmap.xxx2); + } + + holder.gsyVideoPlayer.setUpLazy(url, true, null, null, "这是title"); + //增加title + holder.gsyVideoPlayer.getTitleTextView().setVisibility(View.GONE); + //设置返回键 + holder.gsyVideoPlayer.getBackButton().setVisibility(View.GONE); + //设置全屏按键功能 + holder.gsyVideoPlayer.getFullscreenButton().setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + holder.gsyVideoPlayer.startWindowFullscreen(context, false, true); + } + }); + //防止错位设置 + holder.gsyVideoPlayer.setPlayTag(TAG); + holder.gsyVideoPlayer.setPlayPosition(position); + //是否根据视频尺寸,自动选择竖屏全屏或者横屏全屏 + holder.gsyVideoPlayer.setAutoFullWithSize(true); + //音频焦点冲突时是否释放 + holder.gsyVideoPlayer.setReleaseWhenLossAudio(false); + //全屏动画 + holder.gsyVideoPlayer.setShowFullAnimation(true); + //小屏时不触摸滑动 + holder.gsyVideoPlayer.setIsTouchWiget(false); + //全屏是否需要lock功能 + return convertView; + + } + + class ViewHolder { + SampleCoverVideo gsyVideoPlayer; + } + +} diff --git a/app/src/main/java/com/example/gsyvideoplayer/source/CustomSourceTag.java b/app/src/main/java/com/example/gsyvideoplayer/source/CustomSourceTag.java new file mode 100644 index 000000000..d733e42a7 --- /dev/null +++ b/app/src/main/java/com/example/gsyvideoplayer/source/CustomSourceTag.java @@ -0,0 +1,25 @@ +package com.example.gsyvideoplayer.source; + +import android.content.Context; + +import com.google.android.exoplayer2.upstream.DataSource; +import com.google.android.exoplayer2.upstream.DefaultBandwidthMeter; +import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory; +import com.google.android.exoplayer2.util.Util; + +public class CustomSourceTag { + + /** + * 获取SourceFactory + */ + static public DataSource.Factory getDataSourceFactory(Context context, boolean preview) { + return new DefaultDataSourceFactory(context, preview ? null : new DefaultBandwidthMeter(), + getHttpDataSourceFactory(context, preview)); + } + + static public DataSource.Factory getHttpDataSourceFactory(Context context, boolean preview) { + DefaultHttpDataSourceFactory dataSourceFactory = new DefaultHttpDataSourceFactory(Util.getUserAgent(context, + "yout tag"), preview ? null : new DefaultBandwidthMeter()); + return dataSourceFactory; + } +} diff --git a/app/src/main/java/com/example/gsyvideoplayer/source/DefaultHttpDataSource.java b/app/src/main/java/com/example/gsyvideoplayer/source/DefaultHttpDataSource.java new file mode 100644 index 000000000..2edb85bab --- /dev/null +++ b/app/src/main/java/com/example/gsyvideoplayer/source/DefaultHttpDataSource.java @@ -0,0 +1,817 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.example.gsyvideoplayer.source; + +import android.net.Uri; +import android.text.TextUtils; + +import com.google.android.exoplayer2.C; +import com.google.android.exoplayer2.upstream.BaseDataSource; +import com.google.android.exoplayer2.upstream.DataSourceException; +import com.google.android.exoplayer2.upstream.DataSpec; +import com.google.android.exoplayer2.upstream.DataSpec.HttpMethod; +import com.google.android.exoplayer2.upstream.HttpDataSource; +import com.google.android.exoplayer2.upstream.TransferListener; +import com.google.android.exoplayer2.util.Assertions; +import com.google.android.exoplayer2.util.Log; +import com.google.android.exoplayer2.util.Predicate; +import com.google.android.exoplayer2.util.Util; + +import java.io.EOFException; +import java.io.IOException; +import java.io.InputStream; +import java.io.InterruptedIOException; +import java.io.OutputStream; +import java.lang.reflect.Method; +import java.net.HttpURLConnection; +import java.net.NoRouteToHostException; +import java.net.ProtocolException; +import java.net.URL; +import java.security.KeyManagementException; +import java.security.NoSuchAlgorithmException; +import java.security.cert.CertificateException; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.atomic.AtomicReference; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import javax.net.ssl.HostnameVerifier; +import javax.net.ssl.HttpsURLConnection; +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLSession; +import javax.net.ssl.SSLSocketFactory; +import javax.net.ssl.TrustManager; +import javax.net.ssl.X509TrustManager; + +import androidx.annotation.Nullable; + +/** + * An {@link HttpDataSource} that uses Android's {@link HttpURLConnection}. + * + *

By default this implementation will not follow cross-protocol redirects (i.e. redirects from + * HTTP to HTTPS or vice versa). Cross-protocol redirects can be enabled by using the {@link + * #DefaultHttpDataSource(String, Predicate, TransferListener, int, int, boolean, + * RequestProperties)} constructor and passing {@code true} as the second last argument. + */ +public class DefaultHttpDataSource extends BaseDataSource implements HttpDataSource { + + /** + * The default connection timeout, in milliseconds. + */ + public static final int DEFAULT_CONNECT_TIMEOUT_MILLIS = 8 * 1000; + /** + * The default read timeout, in milliseconds. + */ + public static final int DEFAULT_READ_TIMEOUT_MILLIS = 8 * 1000; + + private static final String TAG = "DefaultHttpDataSource"; + private static final int MAX_REDIRECTS = 20; // Same limit as okhttp. + private static final int HTTP_STATUS_TEMPORARY_REDIRECT = 307; + private static final int HTTP_STATUS_PERMANENT_REDIRECT = 308; + private static final long MAX_BYTES_TO_DRAIN = 2048; + private static final Pattern CONTENT_RANGE_HEADER = + Pattern.compile("^bytes (\\d+)-(\\d+)/(\\d+)$"); + private static final AtomicReference skipBufferReference = new AtomicReference<>(); + + private final boolean allowCrossProtocolRedirects; + private final int connectTimeoutMillis; + private final int readTimeoutMillis; + private final String userAgent; + private final @Nullable + Predicate contentTypePredicate; + private final @Nullable + RequestProperties defaultRequestProperties; + private final RequestProperties requestProperties; + + private @Nullable + DataSpec dataSpec; + private @Nullable + HttpURLConnection connection; + private @Nullable + InputStream inputStream; + private boolean opened; + + private long bytesToSkip; + private long bytesToRead; + + private long bytesSkipped; + private long bytesRead; + + /** + * @param userAgent The User-Agent string that should be used. + * @param contentTypePredicate An optional {@link Predicate}. If a content type is rejected by the + * predicate then a {@link HttpDataSource.InvalidContentTypeException} is thrown from {@link + * #open(DataSpec)}. + */ + public DefaultHttpDataSource(String userAgent, @Nullable Predicate contentTypePredicate) { + this( + userAgent, + contentTypePredicate, + DEFAULT_CONNECT_TIMEOUT_MILLIS, + DEFAULT_READ_TIMEOUT_MILLIS); + } + + /** + * @param userAgent The User-Agent string that should be used. + * @param contentTypePredicate An optional {@link Predicate}. If a content type is rejected by the + * predicate then a {@link HttpDataSource.InvalidContentTypeException} is thrown from {@link + * #open(DataSpec)}. + * @param connectTimeoutMillis The connection timeout, in milliseconds. A timeout of zero is + * interpreted as an infinite timeout. + * @param readTimeoutMillis The read timeout, in milliseconds. A timeout of zero is interpreted as + * an infinite timeout. + */ + public DefaultHttpDataSource( + String userAgent, + @Nullable Predicate contentTypePredicate, + int connectTimeoutMillis, + int readTimeoutMillis) { + this( + userAgent, + contentTypePredicate, + connectTimeoutMillis, + readTimeoutMillis, + /* allowCrossProtocolRedirects= */ false, + /* defaultRequestProperties= */ null); + } + + /** + * @param userAgent The User-Agent string that should be used. + * @param contentTypePredicate An optional {@link Predicate}. If a content type is rejected by the + * predicate then a {@link HttpDataSource.InvalidContentTypeException} is thrown from {@link + * #open(DataSpec)}. + * @param connectTimeoutMillis The connection timeout, in milliseconds. A timeout of zero is + * interpreted as an infinite timeout. Pass {@link #DEFAULT_CONNECT_TIMEOUT_MILLIS} to use the + * default value. + * @param readTimeoutMillis The read timeout, in milliseconds. A timeout of zero is interpreted as + * an infinite timeout. Pass {@link #DEFAULT_READ_TIMEOUT_MILLIS} to use the default value. + * @param allowCrossProtocolRedirects Whether cross-protocol redirects (i.e. redirects from HTTP + * to HTTPS and vice versa) are enabled. + * @param defaultRequestProperties The default request properties to be sent to the server as HTTP + * headers or {@code null} if not required. + */ + public DefaultHttpDataSource( + String userAgent, + @Nullable Predicate contentTypePredicate, + int connectTimeoutMillis, + int readTimeoutMillis, + boolean allowCrossProtocolRedirects, + @Nullable RequestProperties defaultRequestProperties) { + super(/* isNetwork= */ true); + this.userAgent = Assertions.checkNotEmpty(userAgent); + this.contentTypePredicate = contentTypePredicate; + this.requestProperties = new RequestProperties(); + this.connectTimeoutMillis = connectTimeoutMillis; + this.readTimeoutMillis = readTimeoutMillis; + this.allowCrossProtocolRedirects = allowCrossProtocolRedirects; + this.defaultRequestProperties = defaultRequestProperties; + } + + /** + * @param userAgent The User-Agent string that should be used. + * @param contentTypePredicate An optional {@link Predicate}. If a content type is rejected by the + * predicate then a {@link HttpDataSource.InvalidContentTypeException} is thrown from {@link + * #open(DataSpec)}. + * @param listener An optional listener. + * @deprecated Use {@link #DefaultHttpDataSource(String, Predicate)} and {@link + * #addTransferListener(TransferListener)}. + */ + @Deprecated + @SuppressWarnings("deprecation") + public DefaultHttpDataSource( + String userAgent, + @Nullable Predicate contentTypePredicate, + @Nullable TransferListener listener) { + this(userAgent, contentTypePredicate, listener, DEFAULT_CONNECT_TIMEOUT_MILLIS, + DEFAULT_READ_TIMEOUT_MILLIS); + } + + /** + * @param userAgent The User-Agent string that should be used. + * @param contentTypePredicate An optional {@link Predicate}. If a content type is rejected by the + * predicate then a {@link HttpDataSource.InvalidContentTypeException} is thrown from {@link + * #open(DataSpec)}. + * @param listener An optional listener. + * @param connectTimeoutMillis The connection timeout, in milliseconds. A timeout of zero is + * interpreted as an infinite timeout. + * @param readTimeoutMillis The read timeout, in milliseconds. A timeout of zero is interpreted as + * an infinite timeout. + * @deprecated Use {@link #DefaultHttpDataSource(String, Predicate, int, int)} and {@link + * #addTransferListener(TransferListener)}. + */ + @Deprecated + @SuppressWarnings("deprecation") + public DefaultHttpDataSource( + String userAgent, + @Nullable Predicate contentTypePredicate, + @Nullable TransferListener listener, + int connectTimeoutMillis, + int readTimeoutMillis) { + this(userAgent, contentTypePredicate, listener, connectTimeoutMillis, readTimeoutMillis, false, + null); + } + + /** + * @param userAgent The User-Agent string that should be used. + * @param contentTypePredicate An optional {@link Predicate}. If a content type is rejected by the + * predicate then a {@link HttpDataSource.InvalidContentTypeException} is thrown from {@link + * #open(DataSpec)}. + * @param listener An optional listener. + * @param connectTimeoutMillis The connection timeout, in milliseconds. A timeout of zero is + * interpreted as an infinite timeout. Pass {@link #DEFAULT_CONNECT_TIMEOUT_MILLIS} to use the + * default value. + * @param readTimeoutMillis The read timeout, in milliseconds. A timeout of zero is interpreted as + * an infinite timeout. Pass {@link #DEFAULT_READ_TIMEOUT_MILLIS} to use the default value. + * @param allowCrossProtocolRedirects Whether cross-protocol redirects (i.e. redirects from HTTP + * to HTTPS and vice versa) are enabled. + * @param defaultRequestProperties The default request properties to be sent to the server as HTTP + * headers or {@code null} if not required. + * @deprecated Use {@link #DefaultHttpDataSource(String, Predicate, int, int, boolean, + * RequestProperties)} and {@link #addTransferListener(TransferListener)}. + */ + @Deprecated + public DefaultHttpDataSource( + String userAgent, + @Nullable Predicate contentTypePredicate, + @Nullable TransferListener listener, + int connectTimeoutMillis, + int readTimeoutMillis, + boolean allowCrossProtocolRedirects, + @Nullable RequestProperties defaultRequestProperties) { + this( + userAgent, + contentTypePredicate, + connectTimeoutMillis, + readTimeoutMillis, + allowCrossProtocolRedirects, + defaultRequestProperties); + if (listener != null) { + addTransferListener(listener); + } + } + + @Override + public @Nullable + Uri getUri() { + return connection == null ? null : Uri.parse(connection.getURL().toString()); + } + + @Override + public Map> getResponseHeaders() { + return connection == null ? new HashMap>() : connection.getHeaderFields(); + } + + @Override + public void setRequestProperty(String name, String value) { + Assertions.checkNotNull(name); + Assertions.checkNotNull(value); + requestProperties.set(name, value); + } + + @Override + public void clearRequestProperty(String name) { + Assertions.checkNotNull(name); + requestProperties.remove(name); + } + + @Override + public void clearAllRequestProperties() { + requestProperties.clear(); + } + + @Override + public long open(DataSpec dataSpec) throws HttpDataSourceException { + this.dataSpec = dataSpec; + this.bytesRead = 0; + this.bytesSkipped = 0; + transferInitializing(dataSpec); + try { + connection = makeConnection(dataSpec); + } catch (IOException e) { + throw new HttpDataSourceException("Unable to connect to " + dataSpec.uri.toString(), e, + dataSpec, HttpDataSourceException.TYPE_OPEN); + } + + int responseCode; + String responseMessage; + try { + responseCode = connection.getResponseCode(); + responseMessage = connection.getResponseMessage(); + } catch (IOException e) { + closeConnectionQuietly(); + throw new HttpDataSourceException("Unable to connect to " + dataSpec.uri.toString(), e, + dataSpec, HttpDataSourceException.TYPE_OPEN); + } + + // Check for a valid response code. + if (responseCode < 200 || responseCode > 299) { + Map> headers = connection.getHeaderFields(); + closeConnectionQuietly(); + InvalidResponseCodeException exception = + new InvalidResponseCodeException(responseCode, responseMessage, headers, dataSpec); + if (responseCode == 416) { + exception.initCause(new DataSourceException(DataSourceException.POSITION_OUT_OF_RANGE)); + } + throw exception; + } + + // Check for a valid content type. + String contentType = connection.getContentType(); + if (contentTypePredicate != null && !contentTypePredicate.evaluate(contentType)) { + closeConnectionQuietly(); + throw new InvalidContentTypeException(contentType, dataSpec); + } + + // If we requested a range starting from a non-zero position and received a 200 rather than a + // 206, then the server does not support partial requests. We'll need to manually skip to the + // requested position. + bytesToSkip = responseCode == 200 && dataSpec.position != 0 ? dataSpec.position : 0; + + // Determine the length of the data to be read, after skipping. + if (!dataSpec.isFlagSet(DataSpec.FLAG_ALLOW_GZIP)) { + if (dataSpec.length != C.LENGTH_UNSET) { + bytesToRead = dataSpec.length; + } else { + long contentLength = getContentLength(connection); + bytesToRead = contentLength != C.LENGTH_UNSET ? (contentLength - bytesToSkip) + : C.LENGTH_UNSET; + } + } else { + // Gzip is enabled. If the server opts to use gzip then the content length in the response + // will be that of the compressed data, which isn't what we want. Furthermore, there isn't a + // reliable way to determine whether the gzip was used or not. Always use the dataSpec length + // in this case. + bytesToRead = dataSpec.length; + } + + try { + inputStream = connection.getInputStream(); + } catch (IOException e) { + closeConnectionQuietly(); + throw new HttpDataSourceException(e, dataSpec, HttpDataSourceException.TYPE_OPEN); + } + + opened = true; + transferStarted(dataSpec); + + return bytesToRead; + } + + @Override + public int read(byte[] buffer, int offset, int readLength) throws HttpDataSourceException { + try { + skipInternal(); + return readInternal(buffer, offset, readLength); + } catch (IOException e) { + throw new HttpDataSourceException(e, dataSpec, HttpDataSourceException.TYPE_READ); + } + } + + @Override + public void close() throws HttpDataSourceException { + try { + if (inputStream != null) { + maybeTerminateInputStream(connection, bytesRemaining()); + try { + inputStream.close(); + } catch (IOException e) { + throw new HttpDataSourceException(e, dataSpec, HttpDataSourceException.TYPE_CLOSE); + } + } + } finally { + inputStream = null; + closeConnectionQuietly(); + if (opened) { + opened = false; + transferEnded(); + } + } + } + + /** + * Returns the current connection, or null if the source is not currently opened. + * + * @return The current open connection, or null. + */ + protected final @Nullable + HttpURLConnection getConnection() { + return connection; + } + + /** + * Returns the number of bytes that have been skipped since the most recent call to + * {@link #open(DataSpec)}. + * + * @return The number of bytes skipped. + */ + protected final long bytesSkipped() { + return bytesSkipped; + } + + /** + * Returns the number of bytes that have been read since the most recent call to + * {@link #open(DataSpec)}. + * + * @return The number of bytes read. + */ + protected final long bytesRead() { + return bytesRead; + } + + /** + * Returns the number of bytes that are still to be read for the current {@link DataSpec}. + *

+ * If the total length of the data being read is known, then this length minus {@code bytesRead()} + * is returned. If the total length is unknown, {@link C#LENGTH_UNSET} is returned. + * + * @return The remaining length, or {@link C#LENGTH_UNSET}. + */ + protected final long bytesRemaining() { + return bytesToRead == C.LENGTH_UNSET ? bytesToRead : bytesToRead - bytesRead; + } + + /** + * Establishes a connection, following redirects to do so where permitted. + */ + private HttpURLConnection makeConnection(DataSpec dataSpec) throws IOException { + URL url = new URL(dataSpec.uri.toString()); + @HttpMethod int httpMethod = dataSpec.httpMethod; + byte[] httpBody = dataSpec.httpBody; + long position = dataSpec.position; + long length = dataSpec.length; + boolean allowGzip = dataSpec.isFlagSet(DataSpec.FLAG_ALLOW_GZIP); + + if (!allowCrossProtocolRedirects) { + // HttpURLConnection disallows cross-protocol redirects, but otherwise performs redirection + // automatically. This is the behavior we want, so use it. + return makeConnection( + url, httpMethod, httpBody, position, length, allowGzip, true /* followRedirects */); + } + + // We need to handle redirects ourselves to allow cross-protocol redirects. + int redirectCount = 0; + while (redirectCount++ <= MAX_REDIRECTS) { + HttpURLConnection connection = + makeConnection( + url, httpMethod, httpBody, position, length, allowGzip, false /* followRedirects */); + int responseCode = connection.getResponseCode(); + String location = connection.getHeaderField("Location"); + if ((httpMethod == DataSpec.HTTP_METHOD_GET || httpMethod == DataSpec.HTTP_METHOD_HEAD) + && (responseCode == HttpURLConnection.HTTP_MULT_CHOICE + || responseCode == HttpURLConnection.HTTP_MOVED_PERM + || responseCode == HttpURLConnection.HTTP_MOVED_TEMP + || responseCode == HttpURLConnection.HTTP_SEE_OTHER + || responseCode == HTTP_STATUS_TEMPORARY_REDIRECT + || responseCode == HTTP_STATUS_PERMANENT_REDIRECT)) { + connection.disconnect(); + url = handleRedirect(url, location); + } else if (httpMethod == DataSpec.HTTP_METHOD_POST + && (responseCode == HttpURLConnection.HTTP_MULT_CHOICE + || responseCode == HttpURLConnection.HTTP_MOVED_PERM + || responseCode == HttpURLConnection.HTTP_MOVED_TEMP + || responseCode == HttpURLConnection.HTTP_SEE_OTHER)) { + // POST request follows the redirect and is transformed into a GET request. + connection.disconnect(); + httpMethod = DataSpec.HTTP_METHOD_GET; + httpBody = null; + url = handleRedirect(url, location); + } else { + return connection; + } + } + + // If we get here we've been redirected more times than are permitted. + throw new NoRouteToHostException("Too many redirects: " + redirectCount); + } + + /** + * Configures a connection and opens it. + * + * @param url The url to connect to. + * @param httpMethod The http method. + * @param httpBody The body data. + * @param position The byte offset of the requested data. + * @param length The length of the requested data, or {@link C#LENGTH_UNSET}. + * @param allowGzip Whether to allow the use of gzip. + * @param followRedirects Whether to follow redirects. + */ + private HttpURLConnection makeConnection( + URL url, + @HttpMethod int httpMethod, + byte[] httpBody, + long position, + long length, + boolean allowGzip, + boolean followRedirects) + throws IOException { + HttpURLConnection connection; + if(url.getProtocol().endsWith("https")) { + /**去除证书限制**/ + connection = (HttpsURLConnection) url.openConnection(); + ((HttpsURLConnection) connection).setHostnameVerifier(new HostnameVerifier() { + @Override + public boolean verify(String hostname, SSLSession session) { + return true; + } + }); + // Create a trust manager that does not validate certificate chains + final TrustManager[] trustAllCerts = new TrustManager[]{ + new X509TrustManager() { + @Override + public void checkClientTrusted(java.security.cert.X509Certificate[] chain, String authType) throws CertificateException { + } + + @Override + public void checkServerTrusted(java.security.cert.X509Certificate[] chain, String authType) throws CertificateException { + } + + @Override + public java.security.cert.X509Certificate[] getAcceptedIssuers() { + return null; + } + } + }; + // Install the all-trusting trust manager + final SSLContext sslContext; + try { + sslContext = SSLContext.getInstance("SSL"); + sslContext.init(null, trustAllCerts, new java.security.SecureRandom()); + // Create an ssl socket factory with our all-trusting manager + final SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory(); + ((HttpsURLConnection) connection).setSSLSocketFactory(sslSocketFactory); + ((HttpsURLConnection) connection).setHostnameVerifier(new HostnameVerifier() { + @Override + public boolean verify(String hostname, SSLSession session) { + return true; + } + }); + } catch (NoSuchAlgorithmException e) { + e.printStackTrace(); + } catch (KeyManagementException e) { + e.printStackTrace(); + } + /**去除证书限制**/ + } else { + connection = (HttpURLConnection) url.openConnection(); + } + connection.setConnectTimeout(connectTimeoutMillis); + connection.setReadTimeout(readTimeoutMillis); + if (defaultRequestProperties != null) { + for (Map.Entry property : defaultRequestProperties.getSnapshot().entrySet()) { + connection.setRequestProperty(property.getKey(), property.getValue()); + } + } + for (Map.Entry property : requestProperties.getSnapshot().entrySet()) { + connection.setRequestProperty(property.getKey(), property.getValue()); + } + if (!(position == 0 && length == C.LENGTH_UNSET)) { + String rangeRequest = "bytes=" + position + "-"; + if (length != C.LENGTH_UNSET) { + rangeRequest += (position + length - 1); + } + connection.setRequestProperty("Range", rangeRequest); + } + connection.setRequestProperty("User-Agent", userAgent); + if (!allowGzip) { + connection.setRequestProperty("Accept-Encoding", "identity"); + } + connection.setInstanceFollowRedirects(followRedirects); + connection.setDoOutput(httpBody != null); + connection.setRequestMethod(DataSpec.getStringForHttpMethod(httpMethod)); + if (httpBody != null) { + connection.setFixedLengthStreamingMode(httpBody.length); + connection.connect(); + OutputStream os = connection.getOutputStream(); + os.write(httpBody); + os.close(); + } else { + connection.connect(); + } + return connection; + } + + /** + * Handles a redirect. + * + * @param originalUrl The original URL. + * @param location The Location header in the response. + * @return The next URL. + * @throws IOException If redirection isn't possible. + */ + private static URL handleRedirect(URL originalUrl, String location) throws IOException { + if (location == null) { + throw new ProtocolException("Null location redirect"); + } + // Form the new url. + URL url = new URL(originalUrl, location); + // Check that the protocol of the new url is supported. + String protocol = url.getProtocol(); + if (!"https".equals(protocol) && !"http".equals(protocol)) { + throw new ProtocolException("Unsupported protocol redirect: " + protocol); + } + // Currently this method is only called if allowCrossProtocolRedirects is true, and so the code + // below isn't required. If we ever decide to handle redirects ourselves when cross-protocol + // redirects are disabled, we'll need to uncomment this block of code. + // if (!allowCrossProtocolRedirects && !protocol.equals(originalUrl.getProtocol())) { + // throw new ProtocolException("Disallowed cross-protocol redirect (" + // + originalUrl.getProtocol() + " to " + protocol + ")"); + // } + return url; + } + + /** + * Attempts to extract the length of the content from the response headers of an open connection. + * + * @param connection The open connection. + * @return The extracted length, or {@link C#LENGTH_UNSET}. + */ + private static long getContentLength(HttpURLConnection connection) { + long contentLength = C.LENGTH_UNSET; + String contentLengthHeader = connection.getHeaderField("Content-Length"); + if (!TextUtils.isEmpty(contentLengthHeader)) { + try { + contentLength = Long.parseLong(contentLengthHeader); + } catch (NumberFormatException e) { + Log.e(TAG, "Unexpected Content-Length [" + contentLengthHeader + "]"); + } + } + String contentRangeHeader = connection.getHeaderField("Content-Range"); + if (!TextUtils.isEmpty(contentRangeHeader)) { + Matcher matcher = CONTENT_RANGE_HEADER.matcher(contentRangeHeader); + if (matcher.find()) { + try { + long contentLengthFromRange = + Long.parseLong(matcher.group(2)) - Long.parseLong(matcher.group(1)) + 1; + if (contentLength < 0) { + // Some proxy servers strip the Content-Length header. Fall back to the length + // calculated here in this case. + contentLength = contentLengthFromRange; + } else if (contentLength != contentLengthFromRange) { + // If there is a discrepancy between the Content-Length and Content-Range headers, + // assume the one with the larger value is correct. We have seen cases where carrier + // change one of them to reduce the size of a request, but it is unlikely anybody would + // increase it. + Log.w(TAG, "Inconsistent headers [" + contentLengthHeader + "] [" + contentRangeHeader + + "]"); + contentLength = Math.max(contentLength, contentLengthFromRange); + } + } catch (NumberFormatException e) { + Log.e(TAG, "Unexpected Content-Range [" + contentRangeHeader + "]"); + } + } + } + return contentLength; + } + + /** + * Skips any bytes that need skipping. Else does nothing. + *

+ * This implementation is based roughly on {@code libcore.io.Streams.skipByReading()}. + * + * @throws InterruptedIOException If the thread is interrupted during the operation. + * @throws EOFException If the end of the input stream is reached before the bytes are skipped. + */ + private void skipInternal() throws IOException { + if (bytesSkipped == bytesToSkip) { + return; + } + + // Acquire the shared skip buffer. + byte[] skipBuffer = skipBufferReference.getAndSet(null); + if (skipBuffer == null) { + skipBuffer = new byte[4096]; + } + + while (bytesSkipped != bytesToSkip) { + int readLength = (int) Math.min(bytesToSkip - bytesSkipped, skipBuffer.length); + int read = inputStream.read(skipBuffer, 0, readLength); + if (Thread.currentThread().isInterrupted()) { + throw new InterruptedIOException(); + } + if (read == -1) { + throw new EOFException(); + } + bytesSkipped += read; + bytesTransferred(read); + } + + // Release the shared skip buffer. + skipBufferReference.set(skipBuffer); + } + + /** + * Reads up to {@code length} bytes of data and stores them into {@code buffer}, starting at + * index {@code offset}. + *

+ * This method blocks until at least one byte of data can be read, the end of the opened range is + * detected, or an exception is thrown. + * + * @param buffer The buffer into which the read data should be stored. + * @param offset The start offset into {@code buffer} at which data should be written. + * @param readLength The maximum number of bytes to read. + * @return The number of bytes read, or {@link C#RESULT_END_OF_INPUT} if the end of the opened + * range is reached. + * @throws IOException If an error occurs reading from the source. + */ + private int readInternal(byte[] buffer, int offset, int readLength) throws IOException { + if (readLength == 0) { + return 0; + } + if (bytesToRead != C.LENGTH_UNSET) { + long bytesRemaining = bytesToRead - bytesRead; + if (bytesRemaining == 0) { + return C.RESULT_END_OF_INPUT; + } + readLength = (int) Math.min(readLength, bytesRemaining); + } + + int read = inputStream.read(buffer, offset, readLength); + if (read == -1) { + if (bytesToRead != C.LENGTH_UNSET) { + // End of stream reached having not read sufficient data. + throw new EOFException(); + } + return C.RESULT_END_OF_INPUT; + } + + bytesRead += read; + bytesTransferred(read); + return read; + } + + /** + * On platform API levels 19 and 20, okhttp's implementation of {@link InputStream#close} can + * block for a long time if the stream has a lot of data remaining. Call this method before + * closing the input stream to make a best effort to cause the input stream to encounter an + * unexpected end of input, working around this issue. On other platform API levels, the method + * does nothing. + * + * @param connection The connection whose {@link InputStream} should be terminated. + * @param bytesRemaining The number of bytes remaining to be read from the input stream if its + * length is known. {@link C#LENGTH_UNSET} otherwise. + */ + private static void maybeTerminateInputStream(HttpURLConnection connection, long bytesRemaining) { + if (Util.SDK_INT != 19 && Util.SDK_INT != 20) { + return; + } + + try { + InputStream inputStream = connection.getInputStream(); + if (bytesRemaining == C.LENGTH_UNSET) { + // If the input stream has already ended, do nothing. The socket may be re-used. + if (inputStream.read() == -1) { + return; + } + } else if (bytesRemaining <= MAX_BYTES_TO_DRAIN) { + // There isn't much data left. Prefer to allow it to drain, which may allow the socket to be + // re-used. + return; + } + String className = inputStream.getClass().getName(); + if ("com.android.okhttp.internal.http.HttpTransport$ChunkedInputStream".equals(className) + || "com.android.okhttp.internal.http.HttpTransport$FixedLengthInputStream" + .equals(className)) { + Class superclass = inputStream.getClass().getSuperclass(); + Method unexpectedEndOfInput = superclass.getDeclaredMethod("unexpectedEndOfInput"); + unexpectedEndOfInput.setAccessible(true); + unexpectedEndOfInput.invoke(inputStream); + } + } catch (Exception e) { + // If an IOException then the connection didn't ever have an input stream, or it was closed + // already. If another type of exception then something went wrong, most likely the device + // isn't using okhttp. + } + } + + + /** + * Closes the current connection quietly, if there is one. + */ + private void closeConnectionQuietly() { + if (connection != null) { + try { + connection.disconnect(); + } catch (Exception e) { + Log.e(TAG, "Unexpected error while disconnecting", e); + } + connection = null; + } + } + +} diff --git a/app/src/main/java/com/example/gsyvideoplayer/source/DefaultHttpDataSourceFactory.java b/app/src/main/java/com/example/gsyvideoplayer/source/DefaultHttpDataSourceFactory.java new file mode 100644 index 000000000..63e528519 --- /dev/null +++ b/app/src/main/java/com/example/gsyvideoplayer/source/DefaultHttpDataSourceFactory.java @@ -0,0 +1,123 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.example.gsyvideoplayer.source; + +import com.google.android.exoplayer2.upstream.HttpDataSource; +import com.google.android.exoplayer2.upstream.HttpDataSource.BaseFactory; +import com.google.android.exoplayer2.upstream.HttpDataSource.Factory; +import com.google.android.exoplayer2.upstream.TransferListener; + +import androidx.annotation.Nullable; + +/** A {@link Factory} that produces {@link DefaultHttpDataSource} instances. */ +public final class DefaultHttpDataSourceFactory extends BaseFactory { + + private final String userAgent; + private final @Nullable + TransferListener listener; + private final int connectTimeoutMillis; + private final int readTimeoutMillis; + private final boolean allowCrossProtocolRedirects; + + /** + * Constructs a DefaultHttpDataSourceFactory. Sets {@link + * DefaultHttpDataSource#DEFAULT_CONNECT_TIMEOUT_MILLIS} as the connection timeout, {@link + * DefaultHttpDataSource#DEFAULT_READ_TIMEOUT_MILLIS} as the read timeout and disables + * cross-protocol redirects. + * + * @param userAgent The User-Agent string that should be used. + */ + public DefaultHttpDataSourceFactory(String userAgent) { + this(userAgent, null); + } + + /** + * Constructs a DefaultHttpDataSourceFactory. Sets {@link + * DefaultHttpDataSource#DEFAULT_CONNECT_TIMEOUT_MILLIS} as the connection timeout, {@link + * DefaultHttpDataSource#DEFAULT_READ_TIMEOUT_MILLIS} as the read timeout and disables + * cross-protocol redirects. + * + * @param userAgent The User-Agent string that should be used. + * @param listener An optional listener. + * @see #DefaultHttpDataSourceFactory(String, TransferListener, int, int, boolean) + */ + public DefaultHttpDataSourceFactory(String userAgent, @Nullable TransferListener listener) { + this(userAgent, listener, DefaultHttpDataSource.DEFAULT_CONNECT_TIMEOUT_MILLIS, + DefaultHttpDataSource.DEFAULT_READ_TIMEOUT_MILLIS, false); + } + + /** + * @param userAgent The User-Agent string that should be used. + * @param connectTimeoutMillis The connection timeout that should be used when requesting remote + * data, in milliseconds. A timeout of zero is interpreted as an infinite timeout. + * @param readTimeoutMillis The read timeout that should be used when requesting remote data, in + * milliseconds. A timeout of zero is interpreted as an infinite timeout. + * @param allowCrossProtocolRedirects Whether cross-protocol redirects (i.e. redirects from HTTP + * to HTTPS and vice versa) are enabled. + */ + public DefaultHttpDataSourceFactory( + String userAgent, + int connectTimeoutMillis, + int readTimeoutMillis, + boolean allowCrossProtocolRedirects) { + this( + userAgent, + /* listener= */ null, + connectTimeoutMillis, + readTimeoutMillis, + allowCrossProtocolRedirects); + } + + /** + * @param userAgent The User-Agent string that should be used. + * @param listener An optional listener. + * @param connectTimeoutMillis The connection timeout that should be used when requesting remote + * data, in milliseconds. A timeout of zero is interpreted as an infinite timeout. + * @param readTimeoutMillis The read timeout that should be used when requesting remote data, in + * milliseconds. A timeout of zero is interpreted as an infinite timeout. + * @param allowCrossProtocolRedirects Whether cross-protocol redirects (i.e. redirects from HTTP + * to HTTPS and vice versa) are enabled. + */ + public DefaultHttpDataSourceFactory( + String userAgent, + @Nullable TransferListener listener, + int connectTimeoutMillis, + int readTimeoutMillis, + boolean allowCrossProtocolRedirects) { + this.userAgent = userAgent; + this.listener = listener; + this.connectTimeoutMillis = connectTimeoutMillis; + this.readTimeoutMillis = readTimeoutMillis; + this.allowCrossProtocolRedirects = allowCrossProtocolRedirects; + } + + @Override + protected DefaultHttpDataSource createDataSourceInternal( + HttpDataSource.RequestProperties defaultRequestProperties) { + DefaultHttpDataSource dataSource = + new DefaultHttpDataSource( + userAgent, + /* contentTypePredicate= */ null, + connectTimeoutMillis, + readTimeoutMillis, + allowCrossProtocolRedirects, + defaultRequestProperties); + if (listener != null) { + dataSource.addTransferListener(listener); + } + return dataSource; + } +} diff --git a/app/src/main/java/com/example/gsyvideoplayer/switchplay/SwitchDetailActivity.java b/app/src/main/java/com/example/gsyvideoplayer/switchplay/SwitchDetailActivity.java new file mode 100644 index 000000000..f29403cee --- /dev/null +++ b/app/src/main/java/com/example/gsyvideoplayer/switchplay/SwitchDetailActivity.java @@ -0,0 +1,146 @@ +package com.example.gsyvideoplayer.switchplay; + +import android.app.Activity; +import android.content.Intent; +import android.content.res.Configuration; +import android.os.Bundle; +import androidx.core.app.ActivityCompat; +import androidx.core.app.ActivityOptionsCompat; +import androidx.core.view.ViewCompat; +import androidx.appcompat.app.AppCompatActivity; +import android.view.View; + +import com.example.gsyvideoplayer.R; +import com.shuyu.gsyvideoplayer.GSYVideoManager; +import com.shuyu.gsyvideoplayer.listener.GSYSampleCallBack; +import com.shuyu.gsyvideoplayer.utils.OrientationUtils; + +/** + * 切换详情 + */ +public class SwitchDetailActivity extends AppCompatActivity { + + public static final String URL = "url"; + private static final String OPTION_VIEW = "view"; + + SwitchVideo detailPlayer; + + private boolean isPlay = true; + private boolean isPause; + + private OrientationUtils orientationUtils; + + public static void startTActivity(Activity activity, View transitionView) { + Intent intent = new Intent(activity, SwitchDetailActivity.class); + // 这里指定了共享的视图元素 + ActivityOptionsCompat options = ActivityOptionsCompat.makeSceneTransitionAnimation(activity, transitionView, OPTION_VIEW); + ActivityCompat.startActivity(activity, intent, options.toBundle()); + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.switch_activity_detail_player); + + detailPlayer = (SwitchVideo) findViewById(R.id.detail_player); + + + //增加title + detailPlayer.getTitleTextView().setVisibility(View.GONE); + detailPlayer.getBackButton().setVisibility(View.GONE); + + //外部辅助的旋转,帮助全屏 + orientationUtils = new OrientationUtils(this, detailPlayer); + //初始化不打开外部的旋转 + orientationUtils.setEnable(false); + + SwitchUtil.optionPlayer(detailPlayer, getIntent().getStringExtra(URL), true, "这是title"); + + SwitchUtil.clonePlayState(detailPlayer); + + detailPlayer.setIsTouchWiget(true); + + detailPlayer.setVideoAllCallBack(new GSYSampleCallBack() { + @Override + public void onPrepared(String url, Object... objects) { + super.onPrepared(url, objects); + //开始播放了才能旋转和全屏 + orientationUtils.setEnable(true); + } + + @Override + public void onQuitFullscreen(String url, Object... objects) { + super.onQuitFullscreen(url, objects); + if (orientationUtils != null) { + orientationUtils.backToProtVideo(); + } + } + }); + + detailPlayer.getFullscreenButton().setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + //直接横屏 + orientationUtils.resolveByClick(); + //第一个true是否需要隐藏actionbar,第二个true是否需要隐藏statusbar + detailPlayer.startWindowFullscreen(SwitchDetailActivity.this, true, true); + } + }); + + detailPlayer.setSurfaceToPlay(); + + + // 这里指定了被共享的视图元素 + ViewCompat.setTransitionName(detailPlayer, OPTION_VIEW); + } + + @Override + public void onBackPressed() { + if (orientationUtils != null) { + orientationUtils.backToProtVideo(); + } + if (GSYVideoManager.backFromWindowFull(this)) { + return; + } + super.onBackPressed(); + } + + + @Override + protected void onPause() { + detailPlayer.getCurrentPlayer().onVideoPause(); + super.onPause(); + isPause = true; + } + + @Override + protected void onResume() { + detailPlayer.getCurrentPlayer().onVideoResume(false); + super.onResume(); + isPause = false; + } + + @Override + protected void onDestroy() { + super.onDestroy(); + detailPlayer.getGSYVideoManager().setListener(detailPlayer.getGSYVideoManager().lastListener()); + detailPlayer.getGSYVideoManager().setLastListener(null); + GSYVideoManager.releaseAllVideos(); + if (orientationUtils != null) + orientationUtils.releaseListener(); + + SwitchUtil.release(); + } + + + + @Override + public void onConfigurationChanged(Configuration newConfig) { + super.onConfigurationChanged(newConfig); + //如果旋转了就全屏 + if (isPlay && !isPause) { + detailPlayer.onConfigurationChanged(this, newConfig, orientationUtils, true, true); + } + } + +} diff --git a/app/src/main/java/com/example/gsyvideoplayer/switchplay/SwitchListVideoActivity.java b/app/src/main/java/com/example/gsyvideoplayer/switchplay/SwitchListVideoActivity.java new file mode 100644 index 000000000..fd60cc510 --- /dev/null +++ b/app/src/main/java/com/example/gsyvideoplayer/switchplay/SwitchListVideoActivity.java @@ -0,0 +1,83 @@ +package com.example.gsyvideoplayer.switchplay; + +import android.os.Bundle; +import androidx.appcompat.app.AppCompatActivity; +import android.widget.AbsListView; +import android.widget.ListView; + +import com.example.gsyvideoplayer.R; +import com.shuyu.gsyvideoplayer.GSYVideoManager; + +/** + * 可切换列表 + */ +public class SwitchListVideoActivity extends AppCompatActivity { + + ListView videoList; + + SwitchListVideoAdapter listNormalAdapter; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_list_video); + + videoList = (ListView)findViewById(R.id.video_list); + + listNormalAdapter = new SwitchListVideoAdapter(this); + videoList.setAdapter(listNormalAdapter); + + videoList.setOnScrollListener(new AbsListView.OnScrollListener() { + @Override + public void onScrollStateChanged(AbsListView view, int scrollState) { + } + + @Override + public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { + int lastVisibleItem = firstVisibleItem + visibleItemCount; + //大于0说明有播放 + if (GSYVideoManager.instance().getPlayPosition() >= 0) { + //当前播放的位置 + int position = GSYVideoManager.instance().getPlayPosition(); + //对应的播放列表TAG + if (GSYVideoManager.instance().getPlayTag().equals(SwitchListVideoAdapter.TAG) + && (position < firstVisibleItem || position > lastVisibleItem)) { + if(GSYVideoManager.isFullState(SwitchListVideoActivity.this)) { + return; + } + //如果滑出去了上面和下面就是否,和今日头条一样 + GSYVideoManager.releaseAllVideos(); + listNormalAdapter.notifyDataSetChanged(); + } + } + } + }); + + } + + @Override + public void onBackPressed() { + if (GSYVideoManager.backFromWindowFull(this)) { + return; + } + super.onBackPressed(); + } + + @Override + protected void onPause() { + super.onPause(); + //GSYVideoManager.onPause(); + } + + @Override + protected void onResume() { + super.onResume(); + //GSYVideoManager.onResume(); + } + + @Override + protected void onDestroy() { + super.onDestroy(); + GSYVideoManager.releaseAllVideos(); + } +} diff --git a/app/src/main/java/com/example/gsyvideoplayer/switchplay/SwitchListVideoAdapter.java b/app/src/main/java/com/example/gsyvideoplayer/switchplay/SwitchListVideoAdapter.java new file mode 100644 index 000000000..1698d95ee --- /dev/null +++ b/app/src/main/java/com/example/gsyvideoplayer/switchplay/SwitchListVideoAdapter.java @@ -0,0 +1,109 @@ +package com.example.gsyvideoplayer.switchplay; + +import android.content.Context; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.BaseAdapter; +import android.widget.ImageView; + +import com.example.gsyvideoplayer.R; +import com.example.gsyvideoplayer.model.VideoModel; +import com.shuyu.gsyvideoplayer.GSYVideoManager; +import com.shuyu.gsyvideoplayer.utils.OrientationUtils; +import com.shuyu.gsyvideoplayer.video.StandardGSYVideoPlayer; + +import java.util.ArrayList; +import java.util.List; + +public class SwitchListVideoAdapter extends BaseAdapter { + + public static final String TAG = "SwitchListVideoAdapter"; + + private List list = new ArrayList<>(); + private LayoutInflater inflater; + private Context context; + + private StandardGSYVideoPlayer curPlayer; + + protected OrientationUtils orientationUtils; + + protected boolean isPlay; + + protected boolean isFull; + + public SwitchListVideoAdapter(Context context) { + super(); + this.context = context; + inflater = LayoutInflater.from(context); + for (int i = 0; i < 40; i++) { + list.add(new VideoModel()); + } + + } + + @Override + public int getCount() { + return list.size(); + } + + @Override + public Object getItem(int position) { + return null; + } + + @Override + public long getItemId(int position) { + return 0; + } + + @Override + public View getView(final int position, View convertView, ViewGroup parent) { + final ViewHolder holder; + if (convertView == null) { + holder = new ViewHolder(); + convertView = inflater.inflate(R.layout.switch_list_video_item, null); + holder.gsyVideoPlayer = (SwitchVideo) convertView.findViewById(R.id.video_item_player); + holder.imageView = new ImageView(context); + convertView.setTag(holder); + } else { + holder = (ViewHolder) convertView.getTag(); + } + final String urlH = "http://9890.vod.myqcloud.com/9890_4e292f9a3dd011e6b4078980237cc3d3.f20.mp4"; + final String urlV = "http://7xjmzj.com1.z0.glb.clouddn.com/20171026175005_JObCxCE2.mp4"; + final String url = (position % 2 == 0) ? urlH : urlV; + final int coverId = (position % 2 == 0) ? R.mipmap.xxx1 : R.mipmap.xxx2; + + + //防止错位设置 + holder.gsyVideoPlayer.setPlayTag(TAG); + holder.gsyVideoPlayer.setPlayPosition(position); + SwitchUtil.optionPlayer(holder.gsyVideoPlayer, url, true, "这是title"); + holder.gsyVideoPlayer.setUpLazy(url, true, null, null, "这是title"); + + //增加封面 + holder.imageView.setScaleType(ImageView.ScaleType.CENTER_CROP); + holder.imageView.setImageResource(coverId); + if (holder.imageView.getParent() != null) { + ViewGroup viewGroup = (ViewGroup) holder.imageView.getParent(); + viewGroup.removeView(holder.imageView); + } + holder.gsyVideoPlayer.setThumbImageView(holder.imageView); + + if (GSYVideoManager.instance().getPlayTag().equals(SwitchListVideoAdapter.TAG) + && (position == GSYVideoManager.instance().getPlayPosition())) { + holder.gsyVideoPlayer.getThumbImageViewLayout().setVisibility(View.GONE); + } else { + holder.gsyVideoPlayer.getThumbImageViewLayout().setVisibility(View.VISIBLE); + } + + return convertView; + + } + + class ViewHolder { + SwitchVideo gsyVideoPlayer; + ImageView imageView; + } + +} diff --git a/app/src/main/java/com/example/gsyvideoplayer/switchplay/SwitchUtil.java b/app/src/main/java/com/example/gsyvideoplayer/switchplay/SwitchUtil.java new file mode 100644 index 000000000..d34d4ac67 --- /dev/null +++ b/app/src/main/java/com/example/gsyvideoplayer/switchplay/SwitchUtil.java @@ -0,0 +1,58 @@ +package com.example.gsyvideoplayer.switchplay; + +import android.view.View; + +import com.shuyu.gsyvideoplayer.listener.GSYMediaPlayerListener; + + +public class SwitchUtil { + + private static SwitchVideo sSwitchVideo; + private static GSYMediaPlayerListener sMediaPlayerListener; + + public static void optionPlayer(final SwitchVideo gsyVideoPlayer, String url, boolean cache, String title) { + //增加title + gsyVideoPlayer.getTitleTextView().setVisibility(View.GONE); + //设置返回键 + gsyVideoPlayer.getBackButton().setVisibility(View.GONE); + //设置全屏按键功能 + gsyVideoPlayer.getFullscreenButton().setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + gsyVideoPlayer.startWindowFullscreen(gsyVideoPlayer.getContext(), false, true); + } + }); + //是否根据视频尺寸,自动选择竖屏全屏或者横屏全屏 + gsyVideoPlayer.setAutoFullWithSize(true); + //音频焦点冲突时是否释放 + gsyVideoPlayer.setReleaseWhenLossAudio(true); + //全屏动画 + gsyVideoPlayer.setShowFullAnimation(false); + //小屏时不触摸滑动 + gsyVideoPlayer.setIsTouchWiget(false); + + gsyVideoPlayer.setSwitchUrl(url); + + gsyVideoPlayer.setSwitchCache(cache); + + gsyVideoPlayer.setSwitchTitle(title); + } + + + public static void savePlayState(SwitchVideo switchVideo) { + sSwitchVideo = switchVideo.saveState(); + sMediaPlayerListener = switchVideo; + } + + public static void clonePlayState(SwitchVideo switchVideo) { + switchVideo.cloneState(sSwitchVideo); + } + + public static void release() { + if (sMediaPlayerListener != null) { + sMediaPlayerListener.onAutoCompletion(); + } + sSwitchVideo = null; + sMediaPlayerListener = null; + } +} diff --git a/app/src/main/java/com/example/gsyvideoplayer/switchplay/SwitchVideo.java b/app/src/main/java/com/example/gsyvideoplayer/switchplay/SwitchVideo.java new file mode 100644 index 000000000..ec5a56d58 --- /dev/null +++ b/app/src/main/java/com/example/gsyvideoplayer/switchplay/SwitchVideo.java @@ -0,0 +1,86 @@ +package com.example.gsyvideoplayer.switchplay; + +import android.app.Activity; +import android.content.Context; +import android.util.AttributeSet; +import android.view.View; +import android.widget.TextView; + +import com.example.gsyvideoplayer.R; +import com.shuyu.gsyvideoplayer.video.StandardGSYVideoPlayer; + +public class SwitchVideo extends StandardGSYVideoPlayer { + + private TextView detailBtn; + + public SwitchVideo(Context context, Boolean fullFlag) { + super(context, fullFlag); + } + + public SwitchVideo(Context context) { + super(context); + } + + public SwitchVideo(Context context, AttributeSet attrs) { + super(context, attrs); + } + + @Override + protected void init(Context context) { + super.init(context); + detailBtn = (TextView) findViewById(R.id.detail_btn); + detailBtn.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + if (isInPlayingState()) { + SwitchUtil.savePlayState(SwitchVideo.this); + getGSYVideoManager().setLastListener(SwitchVideo.this); + //fixme 页面跳转是,元素共享,效果会有一个中间中间控件的存在 + //fixme 这时候中间控件 CURRENT_STATE_PLAYING,会触发 startProgressTimer + //FIXME 但是没有cancel + SwitchDetailActivity.startTActivity((Activity) getContext(), SwitchVideo.this); + } + } + }); + if (mIfCurrentIsFullscreen) { + detailBtn.setVisibility(GONE); + } + + } + + @Override + public int getLayoutId() { + return R.layout.switch_video; + } + + + public void setSwitchUrl(String url) { + mUrl = url; + mOriginUrl = url; + } + + public void setSwitchCache(boolean cache) { + mCache = cache; + } + + public void setSwitchTitle(String title) { + mTitle = title; + } + + public void setSurfaceToPlay() { + addTextureView(); + getGSYVideoManager().setListener(this); + checkoutState(); + } + + public SwitchVideo saveState() { + SwitchVideo switchVideo = new SwitchVideo(getContext()); + cloneParams(this, switchVideo); + return switchVideo; + } + + public void cloneState(SwitchVideo switchVideo) { + cloneParams(switchVideo, this); + } + +} diff --git a/app/src/main/java/com/example/gsyvideoplayer/utils/CommonUtil.java b/app/src/main/java/com/example/gsyvideoplayer/utils/CommonUtil.java index 98e496ee6..69eef3257 100644 --- a/app/src/main/java/com/example/gsyvideoplayer/utils/CommonUtil.java +++ b/app/src/main/java/com/example/gsyvideoplayer/utils/CommonUtil.java @@ -1,7 +1,17 @@ package com.example.gsyvideoplayer.utils; +import android.content.Context; +import android.graphics.Bitmap; import android.view.View; import android.view.ViewGroup; +import android.widget.Toast; + +import com.shuyu.gsyvideoplayer.utils.FileUtils; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.OutputStream; /** * Created by shuyu on 2016/11/11. @@ -18,5 +28,14 @@ public static void setViewHeight(View view, int width, int height) { view.setLayoutParams(layoutParams); } + public static void saveBitmap(Bitmap bitmap) throws FileNotFoundException { + if (bitmap != null) { + File file = new File(FileUtils.getPath(), "GSY-" + System.currentTimeMillis() + ".jpg"); + OutputStream outputStream; + outputStream = new FileOutputStream(file); + bitmap.compress(Bitmap.CompressFormat.JPEG, 100, outputStream); + bitmap.recycle(); + } + } } diff --git a/app/src/main/java/com/example/gsyvideoplayer/utils/JumpUtils.java b/app/src/main/java/com/example/gsyvideoplayer/utils/JumpUtils.java index d5e584d85..fb5ef4c3e 100644 --- a/app/src/main/java/com/example/gsyvideoplayer/utils/JumpUtils.java +++ b/app/src/main/java/com/example/gsyvideoplayer/utils/JumpUtils.java @@ -2,24 +2,44 @@ import android.app.Activity; import android.content.Intent; -import android.support.v4.app.ActivityCompat; -import android.support.v4.app.ActivityOptionsCompat; -import android.support.v4.util.Pair; + +import androidx.core.app.ActivityCompat; +import androidx.core.app.ActivityOptionsCompat; +import androidx.core.util.Pair; + import android.view.View; +import com.example.gsyvideoplayer.AudioDetailPlayer; +import com.example.gsyvideoplayer.AutoPlayRecyclerViewActivity; import com.example.gsyvideoplayer.DanmkuVideoActivity; +import com.example.gsyvideoplayer.DetailADPlayer; +import com.example.gsyvideoplayer.DetailADPlayer2; +import com.example.gsyvideoplayer.DetailControlActivity; +import com.example.gsyvideoplayer.DetailDownloadPlayer; +import com.example.gsyvideoplayer.DetailFilterActivity; import com.example.gsyvideoplayer.DetailListPlayer; import com.example.gsyvideoplayer.DetailMoreTypeActivity; +import com.example.gsyvideoplayer.DetailNormalActivityPlayer; import com.example.gsyvideoplayer.DetailPlayer; import com.example.gsyvideoplayer.FragmentVideoActivity; import com.example.gsyvideoplayer.InputUrlDetailActivity; +import com.example.gsyvideoplayer.ListADVideoActivity2; +import com.example.gsyvideoplayer.ListMultiVideoActivity; import com.example.gsyvideoplayer.ListVideo2Activity; import com.example.gsyvideoplayer.ListVideoActivity; import com.example.gsyvideoplayer.PlayActivity; +import com.example.gsyvideoplayer.PlayEmptyControlActivity; +import com.example.gsyvideoplayer.PlayPickActivity; import com.example.gsyvideoplayer.R; import com.example.gsyvideoplayer.RecyclerView2Activity; +import com.example.gsyvideoplayer.RecyclerView3Activity; import com.example.gsyvideoplayer.RecyclerViewActivity; +import com.example.gsyvideoplayer.ScrollingActivity; import com.example.gsyvideoplayer.WebDetailActivity; +import com.example.gsyvideoplayer.WindowActivity; +import com.example.gsyvideoplayer.exo.DetailExoListPlayer; +import com.example.gsyvideoplayer.exosubtitle.GSYExoSubTitleDetailPlayer; +import com.example.gsyvideoplayer.switchplay.SwitchListVideoActivity; /** * Created by shuyu on 2016/11/11. @@ -47,6 +67,46 @@ public static void goToVideoPlayer(Activity activity, View view) { } } + /** + * 跳转到视频播放 + * + * @param activity + * @param view + */ + public static void goToVideoPickPlayer(Activity activity, View view) { + Intent intent = new Intent(activity, PlayPickActivity.class); + intent.putExtra(PlayActivity.TRANSITION, true); + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) { + Pair pair = new Pair<>(view, PlayActivity.IMG_TRANSITION); + ActivityOptionsCompat activityOptions = ActivityOptionsCompat.makeSceneTransitionAnimation( + activity, pair); + ActivityCompat.startActivity(activity, intent, activityOptions.toBundle()); + } else { + activity.startActivity(intent); + activity.overridePendingTransition(R.anim.abc_fade_in, R.anim.abc_fade_out); + } + } + + /** + * 跳转到无UI视频播放 + * + * @param activity + * @param view + */ + public static void goToPlayEmptyControlActivity(Activity activity, View view) { + Intent intent = new Intent(activity, PlayEmptyControlActivity.class); + intent.putExtra(PlayActivity.TRANSITION, true); + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) { + Pair pair = new Pair<>(view, PlayActivity.IMG_TRANSITION); + ActivityOptionsCompat activityOptions = ActivityOptionsCompat.makeSceneTransitionAnimation( + activity, pair); + ActivityCompat.startActivity(activity, intent, activityOptions.toBundle()); + } else { + activity.startActivity(intent); + activity.overridePendingTransition(R.anim.abc_fade_in, R.anim.abc_fade_out); + } + } + /** * 跳转到视频列表 * @@ -58,6 +118,17 @@ public static void goToVideoPlayer(Activity activity) { ActivityCompat.startActivity(activity, intent, activityOptions.toBundle()); } + /** + * 跳转到视频列表 + * + * @param activity + */ + public static void goToAutoVideoPlayer(Activity activity) { + Intent intent = new Intent(activity, AutoPlayRecyclerViewActivity.class); + ActivityOptionsCompat activityOptions = ActivityOptionsCompat.makeSceneTransitionAnimation(activity); + ActivityCompat.startActivity(activity, intent, activityOptions.toBundle()); + } + /** * 跳转到视频列表2 * @@ -91,6 +162,18 @@ public static void goToVideoRecyclerPlayer2(Activity activity) { ActivityCompat.startActivity(activity, intent, activityOptions.toBundle()); } + /** + * 跳转到硬解码 + * + * @param activity + */ + public static void goMediaCodec(Activity activity) { + Intent intent = new Intent(activity, RecyclerView3Activity.class); + ActivityOptionsCompat activityOptions = ActivityOptionsCompat.makeSceneTransitionAnimation(activity); + ActivityCompat.startActivity(activity, intent, activityOptions.toBundle()); + } + + /** * 跳转到详情播放 * @@ -101,6 +184,60 @@ public static void goToDetailPlayer(Activity activity) { activity.startActivity(intent); } + /** + * 跳转到详情播放 + * + * @param activity + */ + public static void goToScrollDetailPlayer(Activity activity) { + Intent intent = new Intent(activity, ScrollingActivity.class); + activity.startActivity(intent); + } + + /** + * 跳转到AD详情播放 + * + * @param activity + */ + public static void goToVideoADPlayer(Activity activity) { + Intent intent = new Intent(activity, DetailADPlayer.class); + activity.startActivity(intent); + } + + /** + * 跳转到多个同时播放 + * + * @param activity + */ + public static void goToMultiVideoPlayer(Activity activity) { + Intent intent = new Intent(activity, ListMultiVideoActivity.class); + ActivityOptionsCompat activityOptions = ActivityOptionsCompat.makeSceneTransitionAnimation(activity); + ActivityCompat.startActivity(activity, intent, activityOptions.toBundle()); + } + + /** + * 跳转列表带广告 + * + * @param activity + */ + public static void goToADListVideoPlayer(Activity activity) { + //Intent intent = new Intent(activity, ListADVideoActivity.class); + Intent intent = new Intent(activity, ListADVideoActivity2.class); + ActivityOptionsCompat activityOptions = ActivityOptionsCompat.makeSceneTransitionAnimation(activity); + ActivityCompat.startActivity(activity, intent, activityOptions.toBundle()); + } + + + /** + * 跳转到详情播放 + * + * @param activity + */ + public static void goToScrollWindow(Activity activity) { + Intent intent = new Intent(activity, WindowActivity.class); + activity.startActivity(intent); + } + /** * 跳转到详情播放 * @@ -111,6 +248,68 @@ public static void goToDetailListPlayer(Activity activity) { activity.startActivity(intent); } + /** + * 跳转到详情播放 + * + * @param activity + */ + public static void goToDetailNormalActivity(Activity activity) { + Intent intent = new Intent(activity, DetailNormalActivityPlayer.class); + activity.startActivity(intent); + } + + /** + * 跳转到详情播放 + * + * @param activity + */ + public static void goToDetailDownloadActivity(Activity activity) { + Intent intent = new Intent(activity, DetailDownloadPlayer.class); + activity.startActivity(intent); + } + + /** + * 跳转到详情播放 + * + * @param activity + */ + public static void goToGSYExoSubTitleDetailPlayer(Activity activity) { + Intent intent = new Intent(activity, GSYExoSubTitleDetailPlayer.class); + activity.startActivity(intent); + } + + /** + * 跳转到详情播放 + * + * @param activity + */ + public static void goToDetailAudioActivity(Activity activity) { + Intent intent = new Intent(activity, AudioDetailPlayer.class); + activity.startActivity(intent); + } + + + /** + * 跳转到详情播放 + * + * @param activity + */ + public static void goToDetailExoListPlayer(Activity activity) { + Intent intent = new Intent(activity, DetailExoListPlayer.class); + activity.startActivity(intent); + } + + + /** + * 跳转到带广告的 + * + * @param activity + */ + public static void goToVideoADPlayer2(Activity activity) { + Intent intent = new Intent(activity, DetailADPlayer2.class); + activity.startActivity(intent); + } + /** * 跳转到详情播放 @@ -144,6 +343,7 @@ public static void gotoFragment(Activity activity) { /** * 跳到多类型 + * * @param activity */ public static void gotoMoreType(Activity activity) { @@ -153,10 +353,38 @@ public static void gotoMoreType(Activity activity) { /** * 跳到可输入 + * * @param activity */ public static void gotoInput(Activity activity) { Intent intent = new Intent(activity, InputUrlDetailActivity.class); activity.startActivity(intent); } + + /** + * 跳到可控制 + * + * @param activity + */ + public static void gotoControl(Activity activity) { + Intent intent = new Intent(activity, DetailControlActivity.class); + activity.startActivity(intent); + } + + /** + * 跳到滤镜 + * + * @param activity + */ + public static void gotoFilter(Activity activity) { + Intent intent = new Intent(activity, DetailFilterActivity.class); + activity.startActivity(intent); + } + + + public static void goToSwitch(Activity activity) { + Intent intent = new Intent(activity, SwitchListVideoActivity.class); + activity.startActivity(intent); + } + } diff --git a/app/src/main/java/com/example/gsyvideoplayer/utils/MemoryCallBack.java b/app/src/main/java/com/example/gsyvideoplayer/utils/MemoryCallBack.java new file mode 100644 index 000000000..71d6261b5 --- /dev/null +++ b/app/src/main/java/com/example/gsyvideoplayer/utils/MemoryCallBack.java @@ -0,0 +1,55 @@ +package com.example.gsyvideoplayer.utils; + +import com.shuyu.gsyvideoplayer.utils.Debuger; +import com.zhy.http.okhttp.OkHttpUtils; +import com.zhy.http.okhttp.callback.Callback; + +import java.io.IOException; +import java.io.InputStream; + +import okhttp3.Response; + +public abstract class MemoryCallBack extends Callback { + + + @Override + public Boolean parseNetworkResponse(Response response, int id) throws Exception { + return saveFile(response, id); + } + + + //// 模拟下载,这样可以和 proxy cache 公用一个本地cache + public boolean saveFile(Response response, final int id) throws IOException { + InputStream is = null; + byte[] buf = new byte[2048]; + int len = 0; + try { + is = response.body().byteStream(); + final long total = response.body().contentLength(); + + long sum = 0; + + while ((len = is.read(buf)) != -1) { + sum += len; + final long finalSum = sum; + OkHttpUtils.getInstance().getDelivery().execute(new Runnable() { + @Override + public void run() { + + Debuger.printfLog("######### inProgress" + finalSum * 1.0f / total); + inProgress(finalSum * 1.0f / total, total, id); + } + }); + } + return true; + + } finally { + try { + response.body().close(); + if (is != null) is.close(); + } catch (IOException e) { + } + + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/gsyvideoplayer/utils/ScrollCalculatorHelper.java b/app/src/main/java/com/example/gsyvideoplayer/utils/ScrollCalculatorHelper.java new file mode 100644 index 000000000..f1391e9a7 --- /dev/null +++ b/app/src/main/java/com/example/gsyvideoplayer/utils/ScrollCalculatorHelper.java @@ -0,0 +1,167 @@ +package com.example.gsyvideoplayer.utils; + +import android.app.AlertDialog; +import android.content.Context; +import android.content.DialogInterface; +import android.graphics.Rect; +import android.os.Handler; +import android.widget.Toast; + +import com.shuyu.gsyvideoplayer.utils.*; +import com.shuyu.gsyvideoplayer.video.base.GSYBaseVideoPlayer; + +import androidx.recyclerview.widget.RecyclerView; + +/** + * 计算滑动,自动播放的帮助类 + * Created by guoshuyu on 2017/11/2. + */ + +public class ScrollCalculatorHelper { + + private int firstVisible = 0; + private int lastVisible = 0; + private int visibleCount = 0; + private int playId; + private int rangeTop; + private int rangeBottom; + private PlayRunnable runnable; + + private Handler playHandler = new Handler(); + + public ScrollCalculatorHelper(int playId, int rangeTop, int rangeBottom) { + this.playId = playId; + this.rangeTop = rangeTop; + this.rangeBottom = rangeBottom; + } + + public void onScrollStateChanged(RecyclerView view, int scrollState) { + switch (scrollState) { + case RecyclerView.SCROLL_STATE_IDLE: + playVideo(view); + break; + } + } + + public void onScroll(RecyclerView view, int firstVisibleItem, int lastVisibleItem, int visibleItemCount) { + if (firstVisible == firstVisibleItem) { + return; + } + firstVisible = firstVisibleItem; + lastVisible = lastVisibleItem; + visibleCount = visibleItemCount; + } + + + void playVideo(RecyclerView view) { + + if (view == null) { + return; + } + + RecyclerView.LayoutManager layoutManager = view.getLayoutManager(); + + GSYBaseVideoPlayer gsyBaseVideoPlayer = null; + + boolean needPlay = false; + + for (int i = 0; i < visibleCount; i++) { + if (layoutManager.getChildAt(i) != null && layoutManager.getChildAt(i).findViewById(playId) != null) { + GSYBaseVideoPlayer player = (GSYBaseVideoPlayer) layoutManager.getChildAt(i).findViewById(playId); + Rect rect = new Rect(); + player.getLocalVisibleRect(rect); + int height = player.getHeight(); + //说明第一个完全可视 + if (rect.top == 0 && rect.bottom == height) { + gsyBaseVideoPlayer = player; + if ((player.getCurrentPlayer().getCurrentState() == GSYBaseVideoPlayer.CURRENT_STATE_NORMAL + || player.getCurrentPlayer().getCurrentState() == GSYBaseVideoPlayer.CURRENT_STATE_ERROR)) { + needPlay = true; + } + break; + } + + } + } + + if (gsyBaseVideoPlayer != null && needPlay) { + if (runnable != null) { + GSYBaseVideoPlayer tmpPlayer = runnable.gsyBaseVideoPlayer; + playHandler.removeCallbacks(runnable); + runnable = null; + if (tmpPlayer == gsyBaseVideoPlayer) { + return; + } + } + runnable = new PlayRunnable(gsyBaseVideoPlayer); + //降低频率 + playHandler.postDelayed(runnable, 400); + } + + + } + + private class PlayRunnable implements Runnable { + + GSYBaseVideoPlayer gsyBaseVideoPlayer; + + public PlayRunnable(GSYBaseVideoPlayer gsyBaseVideoPlayer) { + this.gsyBaseVideoPlayer = gsyBaseVideoPlayer; + } + + @Override + public void run() { + boolean inPosition = false; + //如果未播放,需要播放 + if (gsyBaseVideoPlayer != null) { + int[] screenPosition = new int[2]; + gsyBaseVideoPlayer.getLocationOnScreen(screenPosition); + int halfHeight = gsyBaseVideoPlayer.getHeight() / 2; + int rangePosition = screenPosition[1] + halfHeight; + //中心点在播放区域内 + if (rangePosition >= rangeTop && rangePosition <= rangeBottom) { + inPosition = true; + } + if (inPosition) { + startPlayLogic(gsyBaseVideoPlayer, gsyBaseVideoPlayer.getContext()); + //gsyBaseVideoPlayer.startPlayLogic(); + } + } + } + } + + + /***************************************自动播放的点击播放确认******************************************/ + private void startPlayLogic(GSYBaseVideoPlayer gsyBaseVideoPlayer, Context context) { + if (!com.shuyu.gsyvideoplayer.utils.CommonUtil.isWifiConnected(context)) { + //这里判断是否wifi + showWifiDialog(gsyBaseVideoPlayer, context); + return; + } + gsyBaseVideoPlayer.startPlayLogic(); + } + + private void showWifiDialog(final GSYBaseVideoPlayer gsyBaseVideoPlayer, Context context) { + if (!NetworkUtils.isAvailable(context)) { + Toast.makeText(context, context.getResources().getString(com.shuyu.gsyvideoplayer.R.string.no_net), Toast.LENGTH_LONG).show(); + return; + } + AlertDialog.Builder builder = new AlertDialog.Builder(context); + builder.setMessage(context.getResources().getString(com.shuyu.gsyvideoplayer.R.string.tips_not_wifi)); + builder.setPositiveButton(context.getResources().getString(com.shuyu.gsyvideoplayer.R.string.tips_not_wifi_confirm), new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + dialog.dismiss(); + gsyBaseVideoPlayer.startPlayLogic(); + } + }); + builder.setNegativeButton(context.getResources().getString(com.shuyu.gsyvideoplayer.R.string.tips_not_wifi_cancel), new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + dialog.dismiss(); + } + }); + builder.create().show(); + } + +} diff --git a/app/src/main/java/com/example/gsyvideoplayer/utils/SmallVideoHelper.java b/app/src/main/java/com/example/gsyvideoplayer/utils/SmallVideoHelper.java new file mode 100644 index 000000000..5bfb69c85 --- /dev/null +++ b/app/src/main/java/com/example/gsyvideoplayer/utils/SmallVideoHelper.java @@ -0,0 +1,743 @@ +package com.example.gsyvideoplayer.utils; + +import android.app.Activity; +import android.content.Context; +import android.graphics.Color; +import android.graphics.Point; +import android.graphics.drawable.Drawable; +import android.os.Handler; +import android.view.Gravity; +import android.view.View; +import android.view.ViewGroup; +import android.view.Window; +import android.widget.FrameLayout; + +import com.shuyu.gsyvideoplayer.builder.GSYVideoOptionBuilder; +import com.shuyu.gsyvideoplayer.listener.GSYVideoProgressListener; +import com.shuyu.gsyvideoplayer.listener.LockClickListener; +import com.shuyu.gsyvideoplayer.listener.VideoAllCallBack; +import com.shuyu.gsyvideoplayer.render.view.GSYVideoGLView; +import com.shuyu.gsyvideoplayer.utils.*; +import com.shuyu.gsyvideoplayer.video.StandardGSYVideoPlayer; +import com.shuyu.gsyvideoplayer.video.base.GSYBaseVideoPlayer; +import com.shuyu.gsyvideoplayer.video.base.GSYVideoPlayer; +import com.shuyu.gsyvideoplayer.utils.CommonUtil; + + +import java.io.File; +import java.util.Map; + +import androidx.transition.TransitionManager; + +import static com.shuyu.gsyvideoplayer.utils.CommonUtil.getActionBarHeight; +import static com.shuyu.gsyvideoplayer.utils.CommonUtil.getStatusBarHeight; +import static com.shuyu.gsyvideoplayer.utils.CommonUtil.hideNavKey; +import static com.shuyu.gsyvideoplayer.utils.CommonUtil.showNavKey; + +/** + * 列表模式的小屏和全屏工具类 + * Created by guoshuyu on 2018/1/15. + */ +public class SmallVideoHelper { + + /** + * 播放的标志 + */ + private String TAG = "NULL"; + /** + * 播放器 + */ + private StandardGSYVideoPlayer gsyVideoPlayer; + /** + * 全屏承载布局 + */ + private ViewGroup fullViewContainer; + /** + * 全屏承载布局 + */ + private ViewGroup windowViewContainer; + /** + * 记录列表中item的父布局 + */ + private ViewGroup listParent; + /** + * 布局 + */ + private ViewGroup.LayoutParams listParams; + /** + * 选择工具类 + */ + private OrientationUtils orientationUtils; + /** + * 播放配置 + */ + private GSYSmallVideoHelperBuilder gsyVideoOptionBuilder; + /** + * 上下文 + */ + private Context context; + /** + * 播放的位置 + */ + private int playPosition = -1; + /** + * 可视保存 + */ + private int systemUiVisibility; + /** + * 当前是否全屏 + */ + private boolean isFull; + /** + * 当前是否小屏 + */ + private boolean isSmall; + /** + * 当前item框的屏幕位置 + */ + private int[] listItemRect; + /** + * 当前item的大小 + */ + private int[] listItemSize; + /** + * handler + */ + private Handler handler = new Handler(); + + + public SmallVideoHelper(Context context) { + this(context, new StandardGSYVideoPlayer(context)); + } + + public SmallVideoHelper(Context context, StandardGSYVideoPlayer player) { + gsyVideoPlayer = player; + this.context = context; + this.windowViewContainer = (ViewGroup) (CommonUtil.scanForActivity(context)).findViewById(Window.ID_ANDROID_CONTENT); + + } + + /** + * 处理全屏逻辑 + */ + private void resolveToFull() { + systemUiVisibility = ((Activity) context).getWindow().getDecorView().getSystemUiVisibility(); + CommonUtil.hideSupportActionBar(context, gsyVideoOptionBuilder.isHideActionBar(), gsyVideoOptionBuilder.isHideStatusBar()); + if (gsyVideoOptionBuilder.isHideKey()) { + hideNavKey(context); + } + isFull = true; + ViewGroup viewGroup = (ViewGroup) gsyVideoPlayer.getParent(); + listParams = gsyVideoPlayer.getLayoutParams(); + if (viewGroup != null) { + listParent = viewGroup; + viewGroup.removeView(gsyVideoPlayer); + } + gsyVideoPlayer.setIfCurrentIsFullscreen(true); + gsyVideoPlayer.getFullscreenButton().setImageResource(gsyVideoPlayer.getShrinkImageRes()); + gsyVideoPlayer.getBackButton().setVisibility(View.VISIBLE); + //设置旋转 + orientationUtils = new OrientationUtils((Activity) context, gsyVideoPlayer); + orientationUtils.setEnable(gsyVideoOptionBuilder.isRotateViewAuto()); + gsyVideoPlayer.getBackButton().setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + resolveMaterialToNormal(gsyVideoPlayer); + } + }); + if (gsyVideoOptionBuilder.isShowFullAnimation()) { + if (fullViewContainer instanceof FrameLayout) { + //目前只做了frameLoayout的判断 + resolveMaterialAnimation(); + } else { + resolveFullAdd(); + } + + } else { + resolveFullAdd(); + } + } + + /** + * 添加到全屏父布局里 + */ + private void resolveFullAdd() { + if (gsyVideoOptionBuilder.isShowFullAnimation()) { + if (fullViewContainer != null) { + fullViewContainer.setBackgroundColor(Color.BLACK); + } + } + resolveChangeFirstLogic(0); + if (fullViewContainer != null) { + fullViewContainer.addView(gsyVideoPlayer); + } else { + windowViewContainer.addView(gsyVideoPlayer); + } + } + + /** + * 如果是5.0的动画开始位置 + */ + private void resolveMaterialAnimation() { + listItemRect = new int[2]; + listItemSize = new int[2]; + saveLocationStatus(context, gsyVideoOptionBuilder.isHideActionBar(), gsyVideoOptionBuilder.isHideStatusBar()); + FrameLayout.LayoutParams lpParent = new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT); + FrameLayout frameLayout = new FrameLayout(context); + frameLayout.setBackgroundColor(Color.BLACK); + FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(listItemSize[0], listItemSize[1]); + lp.setMargins(listItemRect[0], listItemRect[1], 0, 0); + frameLayout.addView(gsyVideoPlayer, lp); + if (fullViewContainer != null) { + fullViewContainer.addView(frameLayout, lpParent); + } else { + windowViewContainer.addView(frameLayout, lpParent); + } + handler.postDelayed(new Runnable() { + @Override + public void run() { + //开始动画 + if (fullViewContainer != null) { + TransitionManager.beginDelayedTransition(fullViewContainer); + } else { + TransitionManager.beginDelayedTransition(windowViewContainer); + } + resolveMaterialFullVideoShow(gsyVideoPlayer); + resolveChangeFirstLogic(600); + } + }, 300); + } + + /** + * 如果是5.0的,要从原位置过度到全屏位置 + */ + private void resolveMaterialFullVideoShow(GSYBaseVideoPlayer gsyVideoPlayer) { + FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) gsyVideoPlayer.getLayoutParams(); + lp.setMargins(0, 0, 0, 0); + lp.height = ViewGroup.LayoutParams.MATCH_PARENT; + lp.width = ViewGroup.LayoutParams.MATCH_PARENT; + lp.gravity = Gravity.CENTER; + gsyVideoPlayer.setLayoutParams(lp); + gsyVideoPlayer.setIfCurrentIsFullscreen(true); + } + + + /** + * 处理正常逻辑 + */ + private void resolveToNormal() { + int delay = orientationUtils.backToProtVideo(); + if (!gsyVideoOptionBuilder.isShowFullAnimation()) { + delay = 0; + } + handler.postDelayed(new Runnable() { + @Override + public void run() { + isFull = false; + removeWindowContainer(); + if (fullViewContainer != null) { + fullViewContainer.removeAllViews(); + } + if (gsyVideoPlayer.getParent() != null) { + ((ViewGroup) gsyVideoPlayer.getParent()).removeView(gsyVideoPlayer); + } + orientationUtils.setEnable(false); + gsyVideoPlayer.setIfCurrentIsFullscreen(false); + if (fullViewContainer != null) { + fullViewContainer.setBackgroundColor(Color.TRANSPARENT); + } + listParent.addView(gsyVideoPlayer, listParams); + gsyVideoPlayer.getFullscreenButton().setImageResource(gsyVideoPlayer.getEnlargeImageRes()); + gsyVideoPlayer.getBackButton().setVisibility(View.GONE); + gsyVideoPlayer.setIfCurrentIsFullscreen(false); + if (gsyVideoOptionBuilder.getVideoAllCallBack() != null) { + Debuger.printfLog("onQuitFullscreen"); + gsyVideoOptionBuilder.getVideoAllCallBack().onQuitFullscreen(gsyVideoOptionBuilder.getUrl(), gsyVideoOptionBuilder.getVideoTitle(), gsyVideoPlayer); + } + if (gsyVideoOptionBuilder.isHideKey()) { + showNavKey(context, systemUiVisibility); + } + CommonUtil.showSupportActionBar(context, gsyVideoOptionBuilder.isHideActionBar(), gsyVideoOptionBuilder.isHideStatusBar()); + } + }, delay); + } + + + /** + * 动画回到正常效果 + */ + private void resolveMaterialToNormal(final GSYVideoPlayer gsyVideoPlayer) { + if (gsyVideoOptionBuilder.isShowFullAnimation() && fullViewContainer instanceof FrameLayout) { + int delay = orientationUtils.backToProtVideo(); + handler.postDelayed(new Runnable() { + @Override + public void run() { + TransitionManager.beginDelayedTransition(fullViewContainer); + FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) gsyVideoPlayer.getLayoutParams(); + lp.setMargins(listItemRect[0], listItemRect[1], 0, 0); + lp.width = listItemSize[0]; + lp.height = listItemSize[1]; + //注意配置回来,不然动画效果会不对 + lp.gravity = Gravity.NO_GRAVITY; + gsyVideoPlayer.setLayoutParams(lp); + handler.postDelayed(new Runnable() { + @Override + public void run() { + resolveToNormal(); + } + }, 400); + } + }, delay); + } else { + resolveToNormal(); + } + } + + + /** + * 是否全屏一开始马上自动横屏 + */ + private void resolveChangeFirstLogic(int time) { + if (gsyVideoOptionBuilder.isLockLand()) { + if (time > 0) { + handler.postDelayed(new Runnable() { + @Override + public void run() { + if (orientationUtils.getIsLand() != 1) { + if (fullViewContainer != null) { + fullViewContainer.setBackgroundColor(Color.BLACK); + } + orientationUtils.resolveByClick(); + } + } + }, time); + } else { + if (orientationUtils.getIsLand() != 1) { + if (fullViewContainer != null) { + fullViewContainer.setBackgroundColor(Color.BLACK); + } + orientationUtils.resolveByClick(); + } + } + } + gsyVideoPlayer.setIfCurrentIsFullscreen(true); + if (gsyVideoOptionBuilder.getVideoAllCallBack() != null) { + Debuger.printfLog("onEnterFullscreen"); + gsyVideoOptionBuilder.getVideoAllCallBack().onEnterFullscreen(gsyVideoOptionBuilder.getUrl(), gsyVideoOptionBuilder.getVideoTitle(), gsyVideoPlayer); + } + } + + /** + * 保存大小和状态 + */ + private void saveLocationStatus(Context context, boolean statusBar, boolean actionBar) { + listParent.getLocationOnScreen(listItemRect); + int statusBarH = getStatusBarHeight(context); + int actionBerH = getActionBarHeight((Activity) context); + if (statusBar) { + listItemRect[1] = listItemRect[1] - statusBarH; + } + if (actionBar) { + listItemRect[1] = listItemRect[1] - actionBerH; + } + listItemSize[0] = listParent.getWidth(); + listItemSize[1] = listParent.getHeight(); + } + + + /** + * 是否当前播放 + */ + private boolean isPlayView(int position, String tag) { + return playPosition == position && TAG.equals(tag); + } + + private boolean isCurrentViewPlaying(int position, String tag) { + return isPlayView(position, tag); + } + + private boolean removeWindowContainer() { + if (windowViewContainer != null && windowViewContainer.indexOfChild(gsyVideoPlayer) != -1) { + windowViewContainer.removeView(gsyVideoPlayer); + return true; + } + return false; + } + + /** + * 动态添加视频播放 + * + * @param position 位置 + * @param imgView 封面 + * @param tag TAG类型 + * @param container player的容器 + * @param playBtn 播放按键 + */ + public void addVideoPlayer(final int position, View imgView, String tag, + ViewGroup container, View playBtn) { + container.removeAllViews(); + if (isCurrentViewPlaying(position, tag)) { + if (!isFull) { + ViewGroup viewGroup = (ViewGroup) gsyVideoPlayer.getParent(); + if (viewGroup != null) + viewGroup.removeAllViews(); + container.addView(gsyVideoPlayer); + playBtn.setVisibility(View.INVISIBLE); + } + } else { + playBtn.setVisibility(View.VISIBLE); + container.removeAllViews(); //增加封面 + container.addView(imgView); + } + } + + /** + * 设置列表播放中的位置和TAG,防止错位,回复播放位置 + * + * @param playPosition 列表中的播放位置 + * @param tag 播放的是哪个列表的tag + */ + public void setPlayPositionAndTag(int playPosition, String tag) { + this.playPosition = playPosition; + this.TAG = tag; + } + + /** + * 开始播放 + */ + public void startPlay() { + + if (isSmall()) { + smallVideoToNormal(); + } + + gsyVideoPlayer.release(); + + + if (gsyVideoOptionBuilder == null) { + throw new NullPointerException("gsyVideoOptionBuilder can't be null"); + } + + gsyVideoOptionBuilder.build(gsyVideoPlayer); + + //增加title + if (gsyVideoPlayer.getTitleTextView() != null) { + gsyVideoPlayer.getTitleTextView().setVisibility(View.GONE); + } + + //设置返回键 + if (gsyVideoPlayer.getBackButton() != null) { + gsyVideoPlayer.getBackButton().setVisibility(View.GONE); + } + + //设置全屏按键功能 + if (gsyVideoPlayer.getFullscreenButton() != null) { + gsyVideoPlayer.getFullscreenButton().setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + doFullBtnLogic(); + } + }); + } + gsyVideoPlayer.startPlayLogic(); + } + + /** + * 全屏按键逻辑 + */ + public void doFullBtnLogic() { + if (!isFull) { + resolveToFull(); + } else { + resolveMaterialToNormal(gsyVideoPlayer); + } + } + + + /** + * 处理返回正常逻辑 + */ + public boolean backFromFull() { + boolean isFull = false; + if (fullViewContainer != null && fullViewContainer.getChildCount() > 0) { + isFull = true; + resolveMaterialToNormal(gsyVideoPlayer); + } else if (windowViewContainer != null && windowViewContainer.indexOfChild(gsyVideoPlayer) != -1) { + isFull = true; + resolveMaterialToNormal(gsyVideoPlayer); + } + return isFull; + } + + /** + * 释放持有的视频 + */ + public void releaseVideoPlayer() { + removeWindowContainer(); + ViewGroup viewGroup = (ViewGroup) gsyVideoPlayer.getParent(); + if (viewGroup != null) + viewGroup.removeAllViews(); + playPosition = -1; + TAG = "NULL"; + if (orientationUtils != null) + orientationUtils.releaseListener(); + + } + + /** + * 显示小屏幕效果 + * + * @param size 小视频的大小 + * @param actionBar 是否有actionBar + * @param statusBar 是否有状态栏 + */ + public void showSmallVideo(Point size, final boolean actionBar, final boolean statusBar) { + if (gsyVideoPlayer.getCurrentState() == GSYVideoPlayer.CURRENT_STATE_PLAYING) { + gsyVideoPlayer.showSmallVideo(size, actionBar, statusBar); + isSmall = true; + } + } + + + /** + * 恢复小屏幕效果 + */ + public void smallVideoToNormal() { + isSmall = false; + gsyVideoPlayer.hideSmallVideo(); + } + + + /** + * 设置全屏显示的viewGroup + * 如果不设置即使用默认的 windowViewContainer + * + * @param fullViewContainer viewGroup + */ + public void setFullViewContainer(ViewGroup fullViewContainer) { + this.fullViewContainer = fullViewContainer; + } + + /** + * 是否全屏 + */ + public boolean isFull() { + return isFull; + } + + /** + * 设置配置 + */ + public void setGsyVideoOptionBuilder(GSYSmallVideoHelperBuilder gsyVideoOptionBuilder) { + this.gsyVideoOptionBuilder = gsyVideoOptionBuilder; + } + + public GSYVideoOptionBuilder getGsyVideoOptionBuilder() { + return gsyVideoOptionBuilder; + } + + public int getPlayPosition() { + return playPosition; + } + + public String getPlayTAG() { + return TAG; + } + + public boolean isSmall() { + return isSmall; + } + + /** + * 获取播放器,直接拿播放器,根据需要自定义配置 + */ + public StandardGSYVideoPlayer getGsyVideoPlayer() { + return gsyVideoPlayer; + } + + /** + * 配置 + */ + public static class GSYSmallVideoHelperBuilder extends GSYVideoOptionBuilder { + + protected boolean mHideActionBar; + + protected boolean mHideStatusBar; + + public boolean isHideActionBar() { + return mHideActionBar; + } + + public GSYSmallVideoHelperBuilder setHideActionBar(boolean hideActionBar) { + this.mHideActionBar = hideActionBar; + return this; + } + + public boolean isHideStatusBar() { + return mHideStatusBar; + } + + public GSYSmallVideoHelperBuilder setHideStatusBar(boolean hideStatusBar) { + this.mHideStatusBar = hideStatusBar; + return this; + } + + public int getShrinkImageRes() { + return mShrinkImageRes; + } + + public int getEnlargeImageRes() { + return mEnlargeImageRes; + } + + public int getPlayPosition() { + return mPlayPosition; + } + + public int getDialogProgressHighLightColor() { + return mDialogProgressHighLightColor; + } + + public int getDialogProgressNormalColor() { + return mDialogProgressNormalColor; + } + + public int getDismissControlTime() { + return mDismissControlTime; + } + + public long getSeekOnStart() { + return mSeekOnStart; + } + + public float getSeekRatio() { + return mSeekRatio; + } + + public float getSpeed() { + return mSpeed; + } + + public boolean isHideKey() { + return mHideKey; + } + + public boolean isShowFullAnimation() { + return mShowFullAnimation; + } + + public boolean isNeedShowWifiTip() { + return mNeedShowWifiTip; + } + + public boolean isRotateViewAuto() { + return mRotateViewAuto; + } + + public boolean isLockLand() { + return mLockLand; + } + + public boolean isLooping() { + return mLooping; + } + + public boolean isIsTouchWiget() { + return mIsTouchWiget; + } + + public boolean isIsTouchWigetFull() { + return mIsTouchWigetFull; + } + + public boolean isShowPauseCover() { + return mShowPauseCover; + } + + public boolean isRotateWithSystem() { + return mRotateWithSystem; + } + + public boolean isCacheWithPlay() { + return mCacheWithPlay; + } + + public boolean isNeedLockFull() { + return mNeedLockFull; + } + + public boolean isThumbPlay() { + return mThumbPlay; + } + + public boolean isSounchTouch() { + return mSounchTouch; + } + + public boolean isSetUpLazy() { + return mSetUpLazy; + } + + public String getPlayTag() { + return mPlayTag; + } + + public String getUrl() { + return mUrl; + } + + public String getVideoTitle() { + return mVideoTitle; + } + + public File getCachePath() { + return mCachePath; + } + + public Map getMapHeadData() { + return mMapHeadData; + } + + public VideoAllCallBack getVideoAllCallBack() { + return mVideoAllCallBack; + } + + public LockClickListener getLockClickListener() { + return mLockClickListener; + } + + public View getThumbImageView() { + return mThumbImageView; + } + + public Drawable getBottomProgressDrawable() { + return mBottomProgressDrawable; + } + + public Drawable getBottomShowProgressDrawable() { + return mBottomShowProgressDrawable; + } + + public Drawable getBottomShowProgressThumbDrawable() { + return mBottomShowProgressThumbDrawable; + } + + public Drawable getVolumeProgressDrawable() { + return mVolumeProgressDrawable; + } + + public Drawable getDialogProgressBarDrawable() { + return mDialogProgressBarDrawable; + } + + public GSYVideoGLView.ShaderInterface getEffectFilter() { + return mEffectFilter; + } + + public GSYVideoProgressListener getGSYVideoProgressListener() { + return mGSYVideoProgressListener; + } + } + + +} diff --git a/app/src/main/java/com/example/gsyvideoplayer/utils/floatUtil/FloatActivity.java b/app/src/main/java/com/example/gsyvideoplayer/utils/floatUtil/FloatActivity.java new file mode 100644 index 000000000..4ad254acd --- /dev/null +++ b/app/src/main/java/com/example/gsyvideoplayer/utils/floatUtil/FloatActivity.java @@ -0,0 +1,82 @@ +package com.example.gsyvideoplayer.utils.floatUtil; + +import android.app.Activity; +import android.content.Context; +import android.content.Intent; +import android.net.Uri; +import android.os.Build; +import android.os.Bundle; + +import androidx.annotation.RequiresApi; + +import java.util.ArrayList; +import java.util.List; + +/** + * 用于在内部自动申请权限 + * https://github.com/yhaolpz + */ + +public class FloatActivity extends Activity { + + private static List mPermissionListenerList; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + if (Build.VERSION.SDK_INT >= 23){ + requestAlertWindowPermission(); + } + } + + @RequiresApi(api = 23) + private void requestAlertWindowPermission() { + Intent intent = new Intent("android.settings.action.MANAGE_OVERLAY_PERMISSION"); + intent.setData(Uri.parse("package:" + getPackageName())); + startActivityForResult(intent, 1); + } + + + @RequiresApi(api = 23) + @Override + protected void onActivityResult(int requestCode, int resultCode, Intent data) { + super.onActivityResult(requestCode, resultCode, data); + if (Build.VERSION.SDK_INT >= 23){ + //todo 用23以上编译即可出现canDrawOverlays + if (Util.hasPermission(this)) { + mPermissionListener.onSuccess(); + } else { + mPermissionListener.onFail(); + } + } + finish(); + } + + static synchronized void request(Context context, PermissionListener permissionListener) { + if (mPermissionListenerList == null) { + mPermissionListenerList = new ArrayList<>(); + mPermissionListener = new PermissionListener() { + @Override + public void onSuccess() { + for (PermissionListener listener : mPermissionListenerList) { + listener.onSuccess(); + } + } + + @Override + public void onFail() { + for (PermissionListener listener : mPermissionListenerList) { + listener.onFail(); + } + } + }; + Intent intent = new Intent(context, FloatActivity.class); + intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + context.startActivity(intent); + } + mPermissionListenerList.add(permissionListener); + } + + + private static PermissionListener mPermissionListener; +} diff --git a/app/src/main/java/com/example/gsyvideoplayer/utils/floatUtil/FloatLifecycle.java b/app/src/main/java/com/example/gsyvideoplayer/utils/floatUtil/FloatLifecycle.java new file mode 100644 index 000000000..962ab2768 --- /dev/null +++ b/app/src/main/java/com/example/gsyvideoplayer/utils/floatUtil/FloatLifecycle.java @@ -0,0 +1,129 @@ +package com.example.gsyvideoplayer.utils.floatUtil; + +import android.app.Activity; +import android.app.Application; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.os.Bundle; +import android.os.Handler; + +/** + * Created by yhao on 17-12-1. + * 用于控制悬浮窗显示周期 + * 使用了三种方法针对返回桌面时隐藏悬浮按钮 + * 1.startCount计数,针对back到桌面可以及时隐藏 + * 2.监听home键,从而及时隐藏 + * 3.resumeCount计时,针对一些只执行onPause不执行onStop的奇葩情况 + */ + +class FloatLifecycle extends BroadcastReceiver implements Application.ActivityLifecycleCallbacks { + + private static final String SYSTEM_DIALOG_REASON_KEY = "reason"; + private static final String SYSTEM_DIALOG_REASON_HOME_KEY = "homekey"; + private static final long delay = 300; + private Handler mHandler; + private Class[] activities; + private boolean showFlag; + private int startCount; + private int resumeCount; + private boolean appBackground; + private LifecycleListener mLifecycleListener; + + + FloatLifecycle(Context applicationContext, boolean showFlag, Class[] activities, LifecycleListener lifecycleListener) { + this.showFlag = showFlag; + this.activities = activities; + mLifecycleListener = lifecycleListener; + mHandler = new Handler(); + ((Application) applicationContext).registerActivityLifecycleCallbacks(this); + applicationContext.registerReceiver(this, new IntentFilter(Intent.ACTION_CLOSE_SYSTEM_DIALOGS)); + } + + + private boolean needShow(Activity activity) { + if (activities == null) { + return true; + } + for (Class a : activities) { + if (a.isInstance(activity)) { + return showFlag; + } + } + return !showFlag; + } + + + @Override + public void onActivityResumed(Activity activity) { + resumeCount++; + if (needShow(activity)) { + mLifecycleListener.onShow(); + } else { + //mLifecycleListener.onHide(); + } + if (appBackground) { + appBackground = false; + } + } + + @Override + public void onActivityPaused(Activity activity) { + resumeCount--; + mHandler.postDelayed(new Runnable() { + @Override + public void run() { + if (resumeCount == 0) { + appBackground = true; + //mLifecycleListener.onPostHide(); + } + } + }, delay); + + } + + @Override + public void onActivityStarted(Activity activity) { + startCount++; + } + + + @Override + public void onActivityStopped(Activity activity) { + startCount--; + if (startCount == 0) { + //mLifecycleListener.onHide(); + } + } + + @Override + public void onReceive(Context context, Intent intent) { + String action = intent.getAction(); + if (action != null && action.equals(Intent.ACTION_CLOSE_SYSTEM_DIALOGS)) { + String reason = intent.getStringExtra(SYSTEM_DIALOG_REASON_KEY); + if (SYSTEM_DIALOG_REASON_HOME_KEY.equals(reason)) { + //mLifecycleListener.onHide(); + } + } + } + + + @Override + public void onActivityCreated(Activity activity, Bundle savedInstanceState) { + + } + + + @Override + public void onActivitySaveInstanceState(Activity activity, Bundle outState) { + + } + + @Override + public void onActivityDestroyed(Activity activity) { + + } + + +} diff --git a/app/src/main/java/com/example/gsyvideoplayer/utils/floatUtil/FloatPhone.java b/app/src/main/java/com/example/gsyvideoplayer/utils/floatUtil/FloatPhone.java new file mode 100644 index 000000000..7bb4d3776 --- /dev/null +++ b/app/src/main/java/com/example/gsyvideoplayer/utils/floatUtil/FloatPhone.java @@ -0,0 +1,113 @@ +package com.example.gsyvideoplayer.utils.floatUtil; + +import android.content.Context; +import android.graphics.PixelFormat; +import android.os.Build; +import android.view.View; +import android.view.WindowManager; + +/** + * Created by yhao on 17-11-14. + * 7.1及以上需申请权限 + */ + +class FloatPhone extends FloatView { + + private final Context mContext; + + private final WindowManager mWindowManager; + private final WindowManager.LayoutParams mLayoutParams; + private View mView; + private int mX, mY; + + FloatPhone(Context applicationContext) { + mContext = applicationContext; + mWindowManager = (WindowManager) applicationContext.getSystemService(Context.WINDOW_SERVICE); + mLayoutParams = new WindowManager.LayoutParams(); + } + + @Override + public void setSize(int width, int height) { + mLayoutParams.width = width; + mLayoutParams.height = height; + } + + @Override + public void setView(View view) { + int layout_type; + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + layout_type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY; + } else { + layout_type = WindowManager.LayoutParams.TYPE_PHONE; + } + mLayoutParams.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL + | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; + mLayoutParams.type = layout_type; + mLayoutParams.windowAnimations = 0; + mView = view; + } + + @Override + public void setGravity(int gravity, int xOffset, int yOffset) { + mLayoutParams.gravity = gravity; + mLayoutParams.x = mX = xOffset; + mLayoutParams.y = mY = yOffset; + } + + + @Override + public void init() { + if (Util.hasPermission(mContext)) { + mLayoutParams.format = PixelFormat.RGBA_8888; + mWindowManager.addView(mView, mLayoutParams); + } else { + FloatActivity.request(mContext, new PermissionListener() { + @Override + public void onSuccess() { + mLayoutParams.format = PixelFormat.RGBA_8888; + mWindowManager.addView(mView, mLayoutParams); + } + + @Override + public void onFail() { + + } + }); + } + } + + @Override + public void dismiss() { + mWindowManager.removeView(mView); + } + + @Override + public void updateXY(int x, int y) { + mLayoutParams.x = mX = x; + mLayoutParams.y = mY = y; + mWindowManager.updateViewLayout(mView, mLayoutParams); + } + + @Override + void updateX(int x) { + mLayoutParams.x = mX = x; + mWindowManager.updateViewLayout(mView, mLayoutParams); + + } + + @Override + void updateY(int y) { + mLayoutParams.y = mY = y; + mWindowManager.updateViewLayout(mView, mLayoutParams); + } + + @Override + int getX() { + return mX; + } + + @Override + int getY() { + return mY; + } +} diff --git a/app/src/main/java/com/example/gsyvideoplayer/utils/floatUtil/FloatToast.java b/app/src/main/java/com/example/gsyvideoplayer/utils/floatUtil/FloatToast.java new file mode 100644 index 000000000..820cb51d4 --- /dev/null +++ b/app/src/main/java/com/example/gsyvideoplayer/utils/floatUtil/FloatToast.java @@ -0,0 +1,96 @@ +package com.example.gsyvideoplayer.utils.floatUtil; + +import android.content.Context; +import android.view.View; +import android.view.WindowManager; +import android.widget.Toast; + +import java.lang.reflect.Field; +import java.lang.reflect.Method; + +/** + * 自定义 toast 方式,无需申请权限 + */ + +class FloatToast extends FloatView { + + + private Toast toast; + + private Object mTN; + private Method show; + private Method hide; + + private int mWidth; + private int mHeight; + + + FloatToast(Context applicationContext) { + toast = new Toast(applicationContext); + } + + + @Override + public void setSize(int width, int height) { + mWidth = width; + mHeight = height; + } + + @Override + public void setView(View view) { + toast.setView(view); + initTN(); + } + + @Override + public void setGravity(int gravity, int xOffset, int yOffset) { + toast.setGravity(gravity, xOffset, yOffset); + } + + @Override + public void init() { + try { + show.invoke(mTN); + } catch (Exception e) { + e.printStackTrace(); + } + } + + @Override + public void dismiss() { + try { + hide.invoke(mTN); + } catch (Exception e) { + e.printStackTrace(); + } + } + + + private void initTN() { + try { + //todo android P test + Field tnField = toast.getClass().getDeclaredField("mTN"); + tnField.setAccessible(true); + mTN = tnField.get(toast); + show = mTN.getClass().getMethod("show"); + hide = mTN.getClass().getMethod("hide"); + + Field tnParamsField = mTN.getClass().getDeclaredField("mParams"); + tnParamsField.setAccessible(true); + WindowManager.LayoutParams params = (WindowManager.LayoutParams) tnParamsField.get(mTN); + params.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL + | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; + params.width = mWidth; + params.height = mHeight; + params.windowAnimations = 0; + Field tnNextViewField = mTN.getClass().getDeclaredField("mNextView"); + tnNextViewField.setAccessible(true); + tnNextViewField.set(mTN, toast.getView()); + + } catch (Exception e) { + e.printStackTrace(); + } + } + + +} diff --git a/app/src/main/java/com/example/gsyvideoplayer/utils/floatUtil/FloatView.java b/app/src/main/java/com/example/gsyvideoplayer/utils/floatUtil/FloatView.java new file mode 100644 index 000000000..86b7e7cff --- /dev/null +++ b/app/src/main/java/com/example/gsyvideoplayer/utils/floatUtil/FloatView.java @@ -0,0 +1,38 @@ +package com.example.gsyvideoplayer.utils.floatUtil; + +import android.view.View; + +/** + * Created by yhao on 17-11-14. + * https://github.com/yhaolpz + */ + +abstract class FloatView { + + abstract void setSize(int width, int height); + + abstract void setView(View view); + + abstract void setGravity(int gravity, int xOffset, int yOffset); + + abstract void init(); + + abstract void dismiss(); + + void updateXY(int x, int y) { + } + + void updateX(int x) { + } + + void updateY(int y) { + } + + int getX() { + return 0; + } + + int getY() { + return 0; + } +} diff --git a/app/src/main/java/com/example/gsyvideoplayer/utils/floatUtil/FloatWindow.java b/app/src/main/java/com/example/gsyvideoplayer/utils/floatUtil/FloatWindow.java new file mode 100644 index 000000000..8b5fbacae --- /dev/null +++ b/app/src/main/java/com/example/gsyvideoplayer/utils/floatUtil/FloatWindow.java @@ -0,0 +1,189 @@ +package com.example.gsyvideoplayer.utils.floatUtil; + +import android.animation.TimeInterpolator; +import android.content.Context; +import androidx.annotation.LayoutRes; +import androidx.annotation.MainThread; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import android.view.Gravity; +import android.view.View; +import android.view.ViewGroup; + +import java.util.HashMap; +import java.util.Map; + +/** + * Created by yhao on 2017/12/22. + * https://github.com/yhaolpz + */ + +public class FloatWindow { + + private FloatWindow() { + + } + + private static final String mDefaultTag = "default_float_window_tag"; + private static Map mFloatWindowMap; + + public static IFloatWindow get() { + return get(mDefaultTag); + } + + public static IFloatWindow get(@NonNull String tag) { + return mFloatWindowMap == null ? null : mFloatWindowMap.get(tag); + } + + private static B mBuilder = null; + + @MainThread + public static B with(@NonNull Context applicationContext) { + return mBuilder = new B(applicationContext); + } + + public static void destroy() { + destroy(mDefaultTag); + } + + public static void destroy(String tag) { + if (mFloatWindowMap == null || !mFloatWindowMap.containsKey(tag)) { + return; + } + mFloatWindowMap.get(tag).dismiss(); + mFloatWindowMap.remove(tag); + } + + public static class B { + Context mApplicationContext; + View mView; + private int mLayoutId; + int mWidth = ViewGroup.LayoutParams.WRAP_CONTENT; + int mHeight = ViewGroup.LayoutParams.WRAP_CONTENT; + int gravity = Gravity.TOP | Gravity.START; + int xOffset; + int yOffset; + boolean mShow = true; + Class[] mActivities; + int mMoveType = MoveType.fixed; + long mDuration = 300; + TimeInterpolator mInterpolator; + private String mTag = mDefaultTag; + + private B() { + + } + + B(Context applicationContext) { + mApplicationContext = applicationContext; + } + + public B setView(@NonNull View view) { + mView = view; + return this; + } + + public B setView(@LayoutRes int layoutId) { + mLayoutId = layoutId; + return this; + } + + public B setWidth(int width) { + mWidth = width; + return this; + } + + public B setHeight(int height) { + mHeight = height; + return this; + } + + public B setWidth(@Screen.screenType int screenType, float ratio) { + mWidth = (int) ((screenType == Screen.width ? + Util.getScreenWidth(mApplicationContext) : + Util.getScreenHeight(mApplicationContext)) * ratio); + return this; + } + + + public B setHeight(@Screen.screenType int screenType, float ratio) { + mHeight = (int) ((screenType == Screen.width ? + Util.getScreenWidth(mApplicationContext) : + Util.getScreenHeight(mApplicationContext)) * ratio); + return this; + } + + + public B setX(int x) { + xOffset = x; + return this; + } + + public B setY(int y) { + yOffset = y; + return this; + } + + public B setX(@Screen.screenType int screenType, float ratio) { + xOffset = (int) ((screenType == Screen.width ? + Util.getScreenWidth(mApplicationContext) : + Util.getScreenHeight(mApplicationContext)) * ratio); + return this; + } + + public B setY(@Screen.screenType int screenType, float ratio) { + yOffset = (int) ((screenType == Screen.width ? + Util.getScreenWidth(mApplicationContext) : + Util.getScreenHeight(mApplicationContext)) * ratio); + return this; + } + + + /** + * 设置 Activity 过滤器,用于指定在哪些界面显示悬浮窗,默认全部界面都显示 + * + * @param show  过滤类型,子类类型也会生效 + * @param activities  过滤界面 + */ + public B setFilter(boolean show, @NonNull Class... activities) { + mShow = show; + mActivities = activities; + return this; + } + + + public B setMoveType(@MoveType.MOVE_TYPE int moveType) { + mMoveType = moveType; + return this; + } + + public B setMoveStyle(long duration, @Nullable TimeInterpolator interpolator) { + mDuration = duration; + mInterpolator = interpolator; + return this; + } + + public B setTag(@NonNull String tag) { + mTag = tag; + return this; + } + + public void build() { + if (mFloatWindowMap == null) { + mFloatWindowMap = new HashMap<>(); + } + if (mFloatWindowMap.containsKey(mTag)) { + throw new IllegalArgumentException("FloatWindow of this tag has been added, Please set a new tag for the new FloatWindow"); + } + if (mView == null && mLayoutId == 0) { + throw new IllegalArgumentException("View has not been set!"); + } + if (mView == null) { + mView = Util.inflate(mApplicationContext, mLayoutId); + } + IFloatWindow floatWindowImpl = new IFloatWindowImpl(this); + mFloatWindowMap.put(mTag, floatWindowImpl); + } + + } +} diff --git a/app/src/main/java/com/example/gsyvideoplayer/utils/floatUtil/IFloatWindow.java b/app/src/main/java/com/example/gsyvideoplayer/utils/floatUtil/IFloatWindow.java new file mode 100644 index 000000000..4cc25c0f8 --- /dev/null +++ b/app/src/main/java/com/example/gsyvideoplayer/utils/floatUtil/IFloatWindow.java @@ -0,0 +1,30 @@ +package com.example.gsyvideoplayer.utils.floatUtil; + +import android.view.View; + +/** + * Created by yhao on 2017/12/22. + * https://github.com/yhaolpz + */ + +public abstract class IFloatWindow { + public abstract void show(); + + public abstract void hide(); + + public abstract int getX(); + + public abstract int getY(); + + public abstract void updateX(int x); + + public abstract void updateX(@Screen.screenType int screenType,float ratio); + + public abstract void updateY(int y); + + public abstract void updateY(@Screen.screenType int screenType,float ratio); + + public abstract View getView(); + + abstract void dismiss(); +} diff --git a/app/src/main/java/com/example/gsyvideoplayer/utils/floatUtil/IFloatWindowImpl.java b/app/src/main/java/com/example/gsyvideoplayer/utils/floatUtil/IFloatWindowImpl.java new file mode 100644 index 000000000..e4422da43 --- /dev/null +++ b/app/src/main/java/com/example/gsyvideoplayer/utils/floatUtil/IFloatWindowImpl.java @@ -0,0 +1,253 @@ +package com.example.gsyvideoplayer.utils.floatUtil; + +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.animation.ObjectAnimator; +import android.animation.PropertyValuesHolder; +import android.animation.TimeInterpolator; +import android.animation.ValueAnimator; +import android.os.Build; +import android.view.MotionEvent; +import android.view.View; +import android.view.animation.DecelerateInterpolator; + +/** + * Created by yhao on 2017/12/22. + * https://github.com/yhaolpz + */ + +public class IFloatWindowImpl extends IFloatWindow { + + + private FloatWindow.B mB; + private FloatView mFloatView; + private FloatLifecycle mFloatLifecycle; + private boolean isShow; + private boolean once = true; + private ValueAnimator mAnimator; + private TimeInterpolator mDecelerateInterpolator; + + private IFloatWindowImpl() { + + } + + IFloatWindowImpl(FloatWindow.B b) { + mB = b; + if (mB.mMoveType == MoveType.fixed) { + if (Build.VERSION.SDK_INT >=25) { + mFloatView = new FloatPhone(b.mApplicationContext); + } else { + mFloatView = new FloatToast(b.mApplicationContext); + } + } else { + mFloatView = new FloatPhone(b.mApplicationContext); + initTouchEvent(); + } + mFloatView.setSize(mB.mWidth, mB.mHeight); + mFloatView.setGravity(mB.gravity, mB.xOffset, mB.yOffset); + mFloatView.setView(mB.mView); + mFloatLifecycle = new FloatLifecycle(mB.mApplicationContext, mB.mShow, mB.mActivities, new LifecycleListener() { + @Override + public void onShow() { + show(); + } + + @Override + public void onHide() { + hide(); + } + + @Override + public void onPostHide() { + postHide(); + } + }); + } + + @Override + public void show() { + if (once) { + mFloatView.init(); + once = false; + isShow = true; + } else { + if (isShow) return; + getView().setVisibility(View.VISIBLE); + isShow = true; + } + } + + @Override + public void hide() { + if (once || !isShow) return; + getView().setVisibility(View.INVISIBLE); + isShow = false; + } + + @Override + void dismiss() { + mFloatView.dismiss(); + isShow = false; + } + + @Override + public void updateX(int x) { + checkMoveType(); + mB.xOffset = x; + mFloatView.updateX(x); + } + + @Override + public void updateY(int y) { + checkMoveType(); + mB.yOffset = y; + mFloatView.updateY(y); + } + + @Override + public void updateX(int screenType, float ratio) { + checkMoveType(); + mB.xOffset = (int) ((screenType == Screen.width ? + Util.getScreenWidth(mB.mApplicationContext) : + Util.getScreenHeight(mB.mApplicationContext)) * ratio); + mFloatView.updateX(mB.xOffset); + + } + + @Override + public void updateY(int screenType, float ratio) { + checkMoveType(); + mB.yOffset = (int) ((screenType == Screen.width ? + Util.getScreenWidth(mB.mApplicationContext) : + Util.getScreenHeight(mB.mApplicationContext)) * ratio); + mFloatView.updateY(mB.yOffset); + + } + + @Override + public int getX() { + return mFloatView.getX(); + } + + @Override + public int getY() { + return mFloatView.getY(); + } + + + @Override + public View getView() { + return mB.mView; + } + + void postHide() { + if (once || !isShow) return; + getView().post(new Runnable() { + @Override + public void run() { + getView().setVisibility(View.INVISIBLE); + } + }); + isShow = false; + } + + private void checkMoveType() { + if (mB.mMoveType == MoveType.fixed) { + throw new IllegalArgumentException("FloatWindow of this tag is not allowed to move!"); + } + } + + private void initTouchEvent() { + switch (mB.mMoveType) { + case MoveType.free: + break; + default: + getView().setOnTouchListener(new View.OnTouchListener() { + float lastX, lastY, changeX, changeY; + int newX, newY; + + @Override + public boolean onTouch(View v, MotionEvent event) { + + switch (event.getAction()) { + case MotionEvent.ACTION_DOWN: + lastX = event.getRawX(); + lastY = event.getRawY(); + cancelAnimator(); + break; + case MotionEvent.ACTION_MOVE: + changeX = event.getRawX() - lastX; + changeY = event.getRawY() - lastY; + newX = (int) (mFloatView.getX() + changeX); + newY = (int) (mFloatView.getY() + changeY); + mFloatView.updateXY(newX, newY); + lastX = event.getRawX(); + lastY = event.getRawY(); + break; + case MotionEvent.ACTION_UP: + switch (mB.mMoveType) { + case MoveType.slide: + int startX = mFloatView.getX(); + int endX = (startX * 2 + v.getWidth() > + Util.getScreenWidth(mB.mApplicationContext)) ? + Util.getScreenWidth(mB.mApplicationContext) - v.getWidth() : 0; + mAnimator = ObjectAnimator.ofInt(startX, endX); + mAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { + @Override + public void onAnimationUpdate(ValueAnimator animation) { + int x = (int) animation.getAnimatedValue(); + mFloatView.updateX(x); + } + }); + startAnimator(); + break; + case MoveType.back: + PropertyValuesHolder pvhX = PropertyValuesHolder.ofInt("x", mFloatView.getX(), mB.xOffset); + PropertyValuesHolder pvhY = PropertyValuesHolder.ofInt("y", mFloatView.getY(), mB.yOffset); + mAnimator = ObjectAnimator.ofPropertyValuesHolder(pvhX, pvhY); + mAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { + @Override + public void onAnimationUpdate(ValueAnimator animation) { + int x = (int) animation.getAnimatedValue("x"); + int y = (int) animation.getAnimatedValue("y"); + mFloatView.updateXY(x, y); + } + }); + startAnimator(); + break; + } + break; + + } + return false; + } + }); + } + } + + private void startAnimator() { + if (mB.mInterpolator == null) { + if (mDecelerateInterpolator == null) { + mDecelerateInterpolator = new DecelerateInterpolator(); + } + mB.mInterpolator = mDecelerateInterpolator; + } + mAnimator.setInterpolator(mB.mInterpolator); + mAnimator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + mAnimator.removeAllUpdateListeners(); + mAnimator.removeAllListeners(); + mAnimator = null; + } + }); + mAnimator.setDuration(mB.mDuration).start(); + } + + private void cancelAnimator() { + if (mAnimator != null && mAnimator.isRunning()) { + mAnimator.cancel(); + } + } + +} diff --git a/app/src/main/java/com/example/gsyvideoplayer/utils/floatUtil/LifecycleListener.java b/app/src/main/java/com/example/gsyvideoplayer/utils/floatUtil/LifecycleListener.java new file mode 100644 index 000000000..9c8f85401 --- /dev/null +++ b/app/src/main/java/com/example/gsyvideoplayer/utils/floatUtil/LifecycleListener.java @@ -0,0 +1,15 @@ +package com.example.gsyvideoplayer.utils.floatUtil; + +/** + * Created by yhao on 2017/12/22. + * https://github.com/yhaolpz + */ + +interface LifecycleListener { + + void onShow(); + + void onHide(); + + void onPostHide(); +} diff --git a/app/src/main/java/com/example/gsyvideoplayer/utils/floatUtil/MoveType.java b/app/src/main/java/com/example/gsyvideoplayer/utils/floatUtil/MoveType.java new file mode 100644 index 000000000..b229a2be0 --- /dev/null +++ b/app/src/main/java/com/example/gsyvideoplayer/utils/floatUtil/MoveType.java @@ -0,0 +1,24 @@ +package com.example.gsyvideoplayer.utils.floatUtil; + +import androidx.annotation.IntDef; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * Created by yhao on 2017/12/22. + * https://github.com/yhaolpz + */ + +public class MoveType { + static final int fixed = 0; + public static final int free = 1; + public static final int active = 2; + public static final int slide = 3; + public static final int back = 4; + + @IntDef({fixed, free, active, slide, back}) + @Retention(RetentionPolicy.SOURCE) + @interface MOVE_TYPE { + } +} diff --git a/app/src/main/java/com/example/gsyvideoplayer/utils/floatUtil/PermissionListener.java b/app/src/main/java/com/example/gsyvideoplayer/utils/floatUtil/PermissionListener.java new file mode 100644 index 000000000..44be0d119 --- /dev/null +++ b/app/src/main/java/com/example/gsyvideoplayer/utils/floatUtil/PermissionListener.java @@ -0,0 +1,11 @@ +package com.example.gsyvideoplayer.utils.floatUtil; + +/** + * Created by yhao on 2017/11/14. + * https://github.com/yhaolpz + */ +interface PermissionListener { + void onSuccess(); + + void onFail(); +} diff --git a/app/src/main/java/com/example/gsyvideoplayer/utils/floatUtil/Screen.java b/app/src/main/java/com/example/gsyvideoplayer/utils/floatUtil/Screen.java new file mode 100644 index 000000000..e5ee904bf --- /dev/null +++ b/app/src/main/java/com/example/gsyvideoplayer/utils/floatUtil/Screen.java @@ -0,0 +1,21 @@ +package com.example.gsyvideoplayer.utils.floatUtil; + +import androidx.annotation.IntDef; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * Created by yhao on 2017/12/23. + * https://github.com/yhaolpz + */ + +public class Screen { + public static final int width = 0; + public static final int height = 1; + + @IntDef({width, height}) + @Retention(RetentionPolicy.SOURCE) + @interface screenType { + } +} diff --git a/app/src/main/java/com/example/gsyvideoplayer/utils/floatUtil/Util.java b/app/src/main/java/com/example/gsyvideoplayer/utils/floatUtil/Util.java new file mode 100644 index 000000000..463ff0411 --- /dev/null +++ b/app/src/main/java/com/example/gsyvideoplayer/utils/floatUtil/Util.java @@ -0,0 +1,50 @@ +package com.example.gsyvideoplayer.utils.floatUtil; + +import android.content.Context; +import android.graphics.Point; +import android.os.Build; +import android.provider.Settings; +import androidx.annotation.RequiresApi; +import android.view.LayoutInflater; +import android.view.View; +import android.view.WindowManager; + +/** + * Created by yhao on 2017/12/22. + * https://github.com/yhaolpz + */ + +public class Util { + + + static View inflate(Context applicationContext, int layoutId) { + LayoutInflater inflate = (LayoutInflater) applicationContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE); + return inflate.inflate(layoutId, null); + } + + @RequiresApi(api = Build.VERSION_CODES.M) + public static boolean hasPermission(Context context) { + return Settings.canDrawOverlays(context); + } + + + private static Point sPoint; + + static int getScreenWidth(Context context) { + if (sPoint == null) { + sPoint = new Point(); + WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); + wm.getDefaultDisplay().getSize(sPoint); + } + return sPoint.x; + } + + static int getScreenHeight(Context context) { + if (sPoint == null) { + sPoint = new Point(); + WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); + wm.getDefaultDisplay().getSize(sPoint); + } + return sPoint.y; + } +} diff --git a/app/src/main/java/com/example/gsyvideoplayer/video/AudioSampleVideo.java b/app/src/main/java/com/example/gsyvideoplayer/video/AudioSampleVideo.java new file mode 100644 index 000000000..b688c3ce4 --- /dev/null +++ b/app/src/main/java/com/example/gsyvideoplayer/video/AudioSampleVideo.java @@ -0,0 +1,224 @@ +package com.example.gsyvideoplayer.video; + +import android.content.Context; +import android.graphics.Point; +import android.util.AttributeSet; +import android.view.View; +import android.view.ViewGroup; +import android.view.Window; +import android.widget.ImageView; + +import com.bumptech.glide.Glide; +import com.bumptech.glide.request.RequestOptions; +import com.example.gsyvideoplayer.R; +import com.shuyu.gsyvideoplayer.utils.CommonUtil; +import com.shuyu.gsyvideoplayer.video.StandardGSYVideoPlayer; +import com.shuyu.gsyvideoplayer.video.base.GSYBaseVideoPlayer; + +public class AudioSampleVideo extends StandardGSYVideoPlayer { + + ImageView mCoverImage; + + String mCoverOriginUrl; + + int mDefaultRes; + + + public AudioSampleVideo(Context context, Boolean fullFlag) { + super(context, fullFlag); + } + + public AudioSampleVideo(Context context) { + super(context); + } + + public AudioSampleVideo(Context context, AttributeSet attrs) { + super(context, attrs); + } + + @Override + protected void init(Context context) { + super.init(context); + mCoverImage = (ImageView) findViewById(R.id.thumbImage); + + if (mThumbImageViewLayout != null && + (mCurrentState == -1 || mCurrentState == CURRENT_STATE_NORMAL || mCurrentState == CURRENT_STATE_ERROR)) { + mThumbImageViewLayout.setVisibility(VISIBLE); + } + } + + @Override + public int getLayoutId() { + return R.layout.audio_layout_cover; + } + + public void loadCoverImage(String url, int res) { + mCoverOriginUrl = url; + mDefaultRes = res; + Glide.with(getContext().getApplicationContext()) + .setDefaultRequestOptions( + new RequestOptions() + .frame(1000000) + .centerCrop() + .error(res) + .placeholder(res)) + .load(url) + .into(mCoverImage); + } + + @Override + public GSYBaseVideoPlayer startWindowFullscreen(Context context, boolean actionBar, boolean statusBar) { + GSYBaseVideoPlayer gsyBaseVideoPlayer = super.startWindowFullscreen(context, actionBar, statusBar); + AudioSampleVideo audioSampleVideo = (AudioSampleVideo) gsyBaseVideoPlayer; + audioSampleVideo.loadCoverImage(mCoverOriginUrl, mDefaultRes); + return gsyBaseVideoPlayer; + } + + + @Override + public GSYBaseVideoPlayer showSmallVideo(Point size, boolean actionBar, boolean statusBar) { + //下面这里替换成你自己的强制转化 + AudioSampleVideo audioSampleVideo = (AudioSampleVideo) super.showSmallVideo(size, actionBar, statusBar); + audioSampleVideo.mStartButton.setVisibility(GONE); + audioSampleVideo.mStartButton = null; + return audioSampleVideo; + } + + @Override + protected void cloneParams(GSYBaseVideoPlayer from, GSYBaseVideoPlayer to) { + super.cloneParams(from, to); + AudioSampleVideo sf = (AudioSampleVideo) from; + AudioSampleVideo st = (AudioSampleVideo) to; + st.mShowFullAnimation = sf.mShowFullAnimation; + } + + + /** + * 退出window层播放全屏效果 + */ + @SuppressWarnings("ResourceType") + @Override + protected void clearFullscreenLayout() { + if (!mFullAnimEnd) { + return; + } + mIfCurrentIsFullscreen = false; + int delay = 0; + if (mOrientationUtils != null) { + delay = mOrientationUtils.backToProtVideo(); + mOrientationUtils.setEnable(false); + if (mOrientationUtils != null) { + mOrientationUtils.releaseListener(); + mOrientationUtils = null; + } + } + + if (!mShowFullAnimation) { + delay = 0; + } + + final ViewGroup vp = (CommonUtil.scanForActivity(getContext())).findViewById(Window.ID_ANDROID_CONTENT); + final View oldF = vp.findViewById(getFullId()); + if (oldF != null) { + AudioSampleVideo gsyVideoPlayer = (AudioSampleVideo) oldF; + gsyVideoPlayer.mIfCurrentIsFullscreen = false; + } + + if (delay == 0) { + backToNormal(); + } else { + postDelayed(new Runnable() { + @Override + public void run() { + backToNormal(); + } + }, delay); + } + + } + + @Override + protected void addTextureView() { + + } + + @Override + protected void changeUiToNormal() { + super.changeUiToNormal(); + setViewShowState(mThumbImageViewLayout, VISIBLE); + } + + @Override + protected void changeUiToPreparingShow() { + super.changeUiToPreparingShow(); + setViewShowState(mThumbImageViewLayout, VISIBLE); + } + + @Override + protected void changeUiToPlayingShow() { + super.changeUiToPlayingShow(); + setViewShowState(mThumbImageViewLayout, VISIBLE); + } + + @Override + protected void changeUiToPauseShow() { + super.changeUiToPauseShow(); + setViewShowState(mThumbImageViewLayout, VISIBLE); + } + + @Override + protected void changeUiToPlayingBufferingShow() { + super.changeUiToPlayingBufferingShow(); + setViewShowState(mThumbImageViewLayout, VISIBLE); + } + + @Override + protected void changeUiToCompleteShow() { + super.changeUiToCompleteShow(); + setViewShowState(mThumbImageViewLayout, VISIBLE); + } + + @Override + protected void changeUiToError() { + super.changeUiToError(); + setViewShowState(mThumbImageViewLayout, VISIBLE); + } + + @Override + protected void changeUiToPrepareingClear() { + super.changeUiToPrepareingClear(); + setViewShowState(mThumbImageViewLayout, VISIBLE); + } + + @Override + protected void changeUiToPlayingClear() { + super.changeUiToPlayingClear(); + setViewShowState(mThumbImageViewLayout, VISIBLE); + } + + @Override + protected void changeUiToPauseClear() { + super.changeUiToPauseClear(); + setViewShowState(mThumbImageViewLayout, VISIBLE); + } + + @Override + protected void changeUiToPlayingBufferingClear() { + super.changeUiToPlayingBufferingClear(); + setViewShowState(mThumbImageViewLayout, VISIBLE); + } + + @Override + protected void changeUiToClear() { + super.changeUiToClear(); + setViewShowState(mThumbImageViewLayout, VISIBLE); + } + + @Override + protected void changeUiToCompleteClear() { + super.changeUiToCompleteClear(); + setViewShowState(mThumbImageViewLayout, VISIBLE); + } + + +} diff --git a/app/src/main/java/com/example/gsyvideoplayer/video/CustomRenderVideoPlayer.java b/app/src/main/java/com/example/gsyvideoplayer/video/CustomRenderVideoPlayer.java new file mode 100644 index 000000000..8482ca088 --- /dev/null +++ b/app/src/main/java/com/example/gsyvideoplayer/video/CustomRenderVideoPlayer.java @@ -0,0 +1,32 @@ +package com.example.gsyvideoplayer.video; + +import android.content.Context; +import android.util.AttributeSet; + +import com.example.gsyvideoplayer.view.CustomRenderView; +import com.shuyu.gsyvideoplayer.video.NormalGSYVideoPlayer; + +/** + * 自定义渲染控件 + * Created by guoshuyu on 2018/1/30. + */ + +public class CustomRenderVideoPlayer extends NormalGSYVideoPlayer { + public CustomRenderVideoPlayer(Context context, Boolean fullFlag) { + super(context, fullFlag); + } + + public CustomRenderVideoPlayer(Context context) { + super(context); + } + + public CustomRenderVideoPlayer(Context context, AttributeSet attrs) { + super(context, attrs); + } + + @Override + protected void addTextureView() { + mTextureView = new CustomRenderView(); + mTextureView.addView(getContext(), mTextureViewContainer, mRotate, this, this, mEffectFilter, mMatrixGL, mRenderer, mMode); + } +} diff --git a/app/src/main/java/com/example/gsyvideoplayer/video/DanmakuVideoPlayer.java b/app/src/main/java/com/example/gsyvideoplayer/video/DanmakuVideoPlayer.java index 3d6771c1a..93a9c5550 100644 --- a/app/src/main/java/com/example/gsyvideoplayer/video/DanmakuVideoPlayer.java +++ b/app/src/main/java/com/example/gsyvideoplayer/video/DanmakuVideoPlayer.java @@ -2,34 +2,27 @@ import android.content.Context; import android.graphics.Color; -import android.graphics.drawable.BitmapDrawable; -import android.graphics.drawable.Drawable; -import android.text.Spannable; -import android.text.SpannableStringBuilder; -import android.text.Spanned; -import android.text.style.BackgroundColorSpan; -import android.text.style.ImageSpan; import android.util.AttributeSet; import android.util.Log; import android.view.View; import android.view.ViewGroup; -import android.widget.SeekBar; import android.widget.TextView; import com.example.gsyvideoplayer.R; import com.example.gsyvideoplayer.adapter.DanamakuAdapter; import com.example.gsyvideoplayer.utils.BiliDanmukuParser; -import com.shuyu.gsyvideoplayer.GSYVideoManager; -import com.shuyu.gsyvideoplayer.GSYVideoPlayer; +import com.shuyu.gsyvideoplayer.video.base.GSYVideoPlayer; import com.shuyu.gsyvideoplayer.utils.Debuger; -import com.shuyu.gsyvideoplayer.video.GSYBaseVideoPlayer; +import com.shuyu.gsyvideoplayer.video.base.GSYBaseVideoPlayer; import com.shuyu.gsyvideoplayer.video.StandardGSYVideoPlayer; +import java.io.BufferedReader; +import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; -import java.net.MalformedURLException; -import java.net.URL; -import java.net.URLConnection; +import java.io.InputStreamReader; import java.util.HashMap; import master.flame.danmaku.controller.IDanmakuView; @@ -38,26 +31,23 @@ import master.flame.danmaku.danmaku.loader.android.DanmakuLoaderFactory; import master.flame.danmaku.danmaku.model.BaseDanmaku; import master.flame.danmaku.danmaku.model.DanmakuTimer; -import master.flame.danmaku.danmaku.model.IDanmakus; import master.flame.danmaku.danmaku.model.IDisplayer; -import master.flame.danmaku.danmaku.model.android.BaseCacheStuffer; import master.flame.danmaku.danmaku.model.android.DanmakuContext; import master.flame.danmaku.danmaku.model.android.Danmakus; import master.flame.danmaku.danmaku.model.android.SpannedCacheStuffer; import master.flame.danmaku.danmaku.parser.BaseDanmakuParser; import master.flame.danmaku.danmaku.parser.IDataSource; -import master.flame.danmaku.danmaku.util.IOUtils; import master.flame.danmaku.ui.widget.DanmakuView; /** - * Created by guoshuyu on 2017/2/16. - *

- * 配置弹幕使用的播放器,目前使用的是本地模拟数据。 - *

- * 模拟数据的弹幕时常比较短,后面的时长点是没有数据的。 - *

- * 注意:这只是一个例子,演示如何集合弹幕,需要完善如弹出输入弹幕等的,可以自行完善。 - * 注意:b站的弹幕so只有v5 v7 x86、没有64,所以记得配置上ndk过滤。 + Created by guoshuyu on 2017/2/16. +

+ 配置弹幕使用的播放器,目前使用的是本地模拟数据。 +

+ 模拟数据的弹幕时常比较短,后面的时长点是没有数据的。 +

+ 注意:这只是一个例子,演示如何集合弹幕,需要完善如弹出输入弹幕等的,可以自行完善。 + 注意:b站的弹幕so只有v5 v7 x86、没有64,所以记得配置上ndk过滤。 */ public class DanmakuVideoPlayer extends StandardGSYVideoPlayer { @@ -72,6 +62,8 @@ public class DanmakuVideoPlayer extends StandardGSYVideoPlayer { private boolean mDanmaKuShow = true; + private File mDumakuFile; + public DanmakuVideoPlayer(Context context, Boolean fullFlag) { super(context, fullFlag); } @@ -115,19 +107,24 @@ public void onPrepared() { @Override public void onVideoPause() { super.onVideoPause(); - if (mDanmakuView != null && mDanmakuView.isPrepared()) { - mDanmakuView.pause(); - } + danmakuOnPause(); } @Override - public void onVideoResume() { - super.onVideoResume(); - if (mDanmakuView != null && mDanmakuView.isPrepared() && mDanmakuView.isPaused()) { - mDanmakuView.resume(); - } + public void onVideoResume(boolean isResume) { + super.onVideoResume(isResume); + danmakuOnResume(); } + @Override + protected void clickStartIcon() { + super.clickStartIcon(); + if (mCurrentState == CURRENT_STATE_PLAYING) { + danmakuOnResume(); + } else if (mCurrentState == CURRENT_STATE_PAUSE) { + danmakuOnPause(); + } + } @Override public void onCompletion() { @@ -162,9 +159,15 @@ public void onClick(View v) { } } + @Override + protected void cloneParams(GSYBaseVideoPlayer from, GSYBaseVideoPlayer to) { + ((DanmakuVideoPlayer) to).mDumakuFile = ((DanmakuVideoPlayer) from).mDumakuFile; + super.cloneParams(from, to); + } + /** - * 处理播放器在全屏切换时,弹幕显示的逻辑 - * 需要格外注意的是,因为全屏和小屏,是切换了播放器,所以需要同步之间的弹幕状态 + 处理播放器在全屏切换时,弹幕显示的逻辑 + 需要格外注意的是,因为全屏和小屏,是切换了播放器,所以需要同步之间的弹幕状态 */ @Override public GSYBaseVideoPlayer startWindowFullscreen(Context context, boolean actionBar, boolean statusBar) { @@ -180,8 +183,8 @@ public GSYBaseVideoPlayer startWindowFullscreen(Context context, boolean actionB } /** - * 处理播放器在退出全屏时,弹幕显示的逻辑 - * 需要格外注意的是,因为全屏和小屏,是切换了播放器,所以需要同步之间的弹幕状态 + 处理播放器在退出全屏时,弹幕显示的逻辑 + 需要格外注意的是,因为全屏和小屏,是切换了播放器,所以需要同步之间的弹幕状态 */ @Override protected void resolveNormalVideoShow(View oldF, ViewGroup vp, GSYVideoPlayer gsyVideoPlayer) { @@ -198,6 +201,25 @@ protected void resolveNormalVideoShow(View oldF, ViewGroup vp, GSYVideoPlayer gs } } + protected void danmakuOnPause() { + if (mDanmakuView != null && mDanmakuView.isPrepared()) { + mDanmakuView.pause(); + } + } + + protected void danmakuOnResume() { + if (mDanmakuView != null && mDanmakuView.isPrepared() && mDanmakuView.isPaused()) { + mDanmakuView.resume(); + } + } + + public void setDanmaKuStream(File is) { + mDumakuFile = is; + if (!getDanmakuView().isPrepared()) { + onPrepareDanmaku((DanmakuVideoPlayer) getCurrentPlayer()); + } + } + private void initDanmaku() { // 设置最大显示行数 @@ -215,8 +237,13 @@ private void initDanmaku() { .setMaximumLines(maxLinesPair) .preventOverlapping(overlappingEnablePair); if (mDanmakuView != null) { - //todo 替换成你的数据流 + if (mDumakuFile != null) { + mParser = createParser(getIsStream(mDumakuFile)); + } + + //todo 这是为了demo效果,实际上需要去掉这个,外部传输文件进来 mParser = createParser(this.getResources().openRawResource(R.raw.comments)); + mDanmakuView.setCallback(new master.flame.danmaku.controller.DrawHandler.Callback() { @Override public void updateTimer(DanmakuTimer timer) { @@ -247,8 +274,32 @@ public void prepared() { } } + private InputStream getIsStream(File file) { + try { + InputStream instream = new FileInputStream(file); + InputStreamReader inputreader = new InputStreamReader(instream); + BufferedReader buffreader = new BufferedReader(inputreader); + String line; + StringBuilder sb1 = new StringBuilder(); + sb1.append(""); + //分行读取 + while ((line = buffreader.readLine()) != null) { + sb1.append(line); + } + sb1.append(""); + Log.e("3333333", sb1.toString()); + instream.close(); + return new ByteArrayInputStream(sb1.toString().getBytes()); + } catch (java.io.FileNotFoundException e) { + Log.d("TestFile", "The File doesn't not exist."); + } catch (IOException e) { + Log.d("TestFile", e.getMessage()); + } + return null; + } + /** - * 弹幕的显示与关闭 + 弹幕的显示与关闭 */ private void resolveDanmakuShow() { post(new Runnable() { @@ -269,30 +320,29 @@ public void run() { } /** - * 开始播放弹幕 + 开始播放弹幕 */ private void onPrepareDanmaku(DanmakuVideoPlayer gsyVideoPlayer) { - if (gsyVideoPlayer.getDanmakuView() != null && !gsyVideoPlayer.getDanmakuView().isPrepared()) { + if (gsyVideoPlayer.getDanmakuView() != null && !gsyVideoPlayer.getDanmakuView().isPrepared() && gsyVideoPlayer.getParser() != null) { gsyVideoPlayer.getDanmakuView().prepare(gsyVideoPlayer.getParser(), gsyVideoPlayer.getDanmakuContext()); } } /** - * 弹幕偏移 + 弹幕偏移 */ private void resolveDanmakuSeek(DanmakuVideoPlayer gsyVideoPlayer, long time) { - if (GSYVideoManager.instance().getMediaPlayer() != null && mHadPlay - && gsyVideoPlayer.getDanmakuView() != null && gsyVideoPlayer.getDanmakuView().isPrepared()) { + if (mHadPlay && gsyVideoPlayer.getDanmakuView() != null && gsyVideoPlayer.getDanmakuView().isPrepared()) { gsyVideoPlayer.getDanmakuView().seekTo(time); } } /** - * 创建解析器对象,解析输入流 - * - * @param stream - * @return + 创建解析器对象,解析输入流 + + @param stream + @return */ private BaseDanmakuParser createParser(InputStream stream) { @@ -321,7 +371,7 @@ protected Danmakus parse() { } /** - * 释放弹幕控件 + 释放弹幕控件 */ private void releaseDanmaku(DanmakuVideoPlayer danmakuVideoPlayer) { if (danmakuVideoPlayer != null && danmakuVideoPlayer.getDanmakuView() != null) { @@ -331,6 +381,11 @@ private void releaseDanmaku(DanmakuVideoPlayer danmakuVideoPlayer) { } public BaseDanmakuParser getParser() { + if (mParser == null) { + if (mDumakuFile != null) { + mParser = createParser(getIsStream(mDumakuFile)); + } + } return mParser; } @@ -359,7 +414,7 @@ public boolean getDanmaKuShow() { } /** - * 模拟添加弹幕数据 + 模拟添加弹幕数据 */ private void addDanmaku(boolean islive) { BaseDanmaku danmaku = mDanmakuContext.mDanmakuFactory.createDanmaku(BaseDanmaku.TYPE_SCROLL_RL); diff --git a/app/src/main/java/com/example/gsyvideoplayer/video/EmptyControlVideo.java b/app/src/main/java/com/example/gsyvideoplayer/video/EmptyControlVideo.java new file mode 100644 index 000000000..390385ad3 --- /dev/null +++ b/app/src/main/java/com/example/gsyvideoplayer/video/EmptyControlVideo.java @@ -0,0 +1,51 @@ +package com.example.gsyvideoplayer.video; + +import android.content.Context; +import android.util.AttributeSet; + +import com.example.gsyvideoplayer.R; +import com.shuyu.gsyvideoplayer.video.StandardGSYVideoPlayer; + +/** + * 无任何控制ui的播放 + * Created by guoshuyu on 2017/8/6. + */ + +public class EmptyControlVideo extends StandardGSYVideoPlayer { + + public EmptyControlVideo(Context context, Boolean fullFlag) { + super(context, fullFlag); + } + + public EmptyControlVideo(Context context) { + super(context); + } + + public EmptyControlVideo(Context context, AttributeSet attrs) { + super(context, attrs); + } + + @Override + public int getLayoutId() { + return R.layout.empty_control_video; + } + + @Override + protected void touchSurfaceMoveFullLogic(float absDeltaX, float absDeltaY) { + super.touchSurfaceMoveFullLogic(absDeltaX, absDeltaY); + //不给触摸快进,如果需要,屏蔽下方代码即可 + mChangePosition = false; + + //不给触摸音量,如果需要,屏蔽下方代码即可 + mChangeVolume = false; + + //不给触摸亮度,如果需要,屏蔽下方代码即可 + mBrightness = false; + } + + @Override + protected void touchDoubleUp() { + //super.touchDoubleUp(); + //不需要双击暂停 + } +} diff --git a/app/src/main/java/com/example/gsyvideoplayer/video/FloatingVideo.java b/app/src/main/java/com/example/gsyvideoplayer/video/FloatingVideo.java new file mode 100644 index 000000000..142ec3f22 --- /dev/null +++ b/app/src/main/java/com/example/gsyvideoplayer/video/FloatingVideo.java @@ -0,0 +1,186 @@ +package com.example.gsyvideoplayer.video; + + +import android.app.AlertDialog; +import android.content.Context; +import android.content.DialogInterface; +import android.media.AudioManager; +import android.os.Build; +import android.util.AttributeSet; +import android.view.View; +import android.view.ViewGroup; +import android.view.WindowManager; + +import com.example.gsyvideoplayer.R; +import com.shuyu.gsyvideoplayer.utils.Debuger; +import com.shuyu.gsyvideoplayer.utils.NetworkUtils; +import com.shuyu.gsyvideoplayer.video.StandardGSYVideoPlayer; + +import java.util.Timer; +import java.util.TimerTask; + +import static com.shuyu.gsyvideoplayer.utils.CommonUtil.hideNavKey; + +/** + * 多窗体下的悬浮窗页面支持Video + * Created by shuyu on 2017/12/25. + */ + +public class FloatingVideo extends StandardGSYVideoPlayer { + + protected Timer mDismissControlViewTimer; + + /** + * 1.5.0开始加入,如果需要不同布局区分功能,需要重载 + */ + public FloatingVideo(Context context, Boolean fullFlag) { + super(context, fullFlag); + } + + public FloatingVideo(Context context) { + super(context); + } + + public FloatingVideo(Context context, AttributeSet attrs) { + super(context, attrs); + } + + @Override + protected void init(Context context) { + if (getActivityContext() != null) { + this.mContext = getActivityContext(); + } else { + this.mContext = context; + } + + initInflate(mContext); + + mTextureViewContainer = (ViewGroup) findViewById(R.id.surface_container); + mStartButton = findViewById(R.id.start); + + if (isInEditMode()) + return; + mScreenWidth = getActivityContext().getResources().getDisplayMetrics().widthPixels; + mScreenHeight = getActivityContext().getResources().getDisplayMetrics().heightPixels; + mAudioManager = (AudioManager) getActivityContext().getApplicationContext().getSystemService(Context.AUDIO_SERVICE); + mStartButton = findViewById(com.shuyu.gsyvideoplayer.R.id.start); + mStartButton.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + clickStartIcon(); + } + }); + } + + @Override + public int getLayoutId() { + return R.layout.layout_floating_video; + } + + + @Override + protected void startPrepare() { + if (getGSYVideoManager().listener() != null) { + getGSYVideoManager().listener().onCompletion(); + } + getGSYVideoManager().setListener(this); + getGSYVideoManager().setPlayTag(mPlayTag); + getGSYVideoManager().setPlayPosition(mPlayPosition); + mAudioManager.requestAudioFocus(onAudioFocusChangeListener, AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN_TRANSIENT); + //((Activity) getActivityContext()).getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); + mBackUpPlayingBufferState = -1; + getGSYVideoManager().prepare(mUrl, mMapHeadData, mLooping, mSpeed, mCache, mCachePath, null); + setStateAndUi(CURRENT_STATE_PREPAREING); + } + + @Override + public void onAutoCompletion() { + setStateAndUi(CURRENT_STATE_AUTO_COMPLETE); + + mSaveChangeViewTIme = 0; + + if (mTextureViewContainer.getChildCount() > 0) { + mTextureViewContainer.removeAllViews(); + } + + if (!mIfCurrentIsFullscreen) + getGSYVideoManager().setLastListener(null); + mAudioManager.abandonAudioFocus(onAudioFocusChangeListener); + //((Activity) getActivityContext()).getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); + + releaseNetWorkState(); + + if (mVideoAllCallBack != null && isCurrentMediaListener()) { + Debuger.printfLog("onAutoComplete"); + mVideoAllCallBack.onAutoComplete(mOriginUrl, mTitle, this); + } + } + + @Override + public void onCompletion() { + //make me normal first + setStateAndUi(CURRENT_STATE_NORMAL); + + mSaveChangeViewTIme = 0; + + if (mTextureViewContainer.getChildCount() > 0) { + mTextureViewContainer.removeAllViews(); + } + + if (!mIfCurrentIsFullscreen) { + getGSYVideoManager().setListener(null); + getGSYVideoManager().setLastListener(null); + } + getGSYVideoManager().setCurrentVideoHeight(0); + getGSYVideoManager().setCurrentVideoWidth(0); + + mAudioManager.abandonAudioFocus(onAudioFocusChangeListener); + //((Activity) getActivityContext()).getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); + + releaseNetWorkState(); + + } + + + @Override + protected Context getActivityContext() { + return getContext(); + } + + + @Override + protected boolean isShowNetConfirm() { + return false; + } + + @Override + protected void showWifiDialog() { + if (!NetworkUtils.isAvailable(mContext)) { + //Toast.makeText(mContext, getResources().getString(R.string.no_net), Toast.LENGTH_LONG).show(); + startPlayLogic(); + return; + } + AlertDialog.Builder builder = new AlertDialog.Builder(getActivityContext()); + builder.setMessage(getResources().getString(com.shuyu.gsyvideoplayer.R.string.tips_not_wifi)); + builder.setPositiveButton(getResources().getString(com.shuyu.gsyvideoplayer.R.string.tips_not_wifi_confirm), new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + dialog.dismiss(); + startPlayLogic(); + } + }); + builder.setNegativeButton(getResources().getString(com.shuyu.gsyvideoplayer.R.string.tips_not_wifi_cancel), new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + dialog.dismiss(); + } + }); + AlertDialog alertDialog = builder.create(); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + alertDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY); + } else { + alertDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT); + } + alertDialog.show(); + } +} diff --git a/app/src/main/java/com/example/gsyvideoplayer/video/LandLayoutVideo.java b/app/src/main/java/com/example/gsyvideoplayer/video/LandLayoutVideo.java index 064412f11..6ef0daebb 100644 --- a/app/src/main/java/com/example/gsyvideoplayer/video/LandLayoutVideo.java +++ b/app/src/main/java/com/example/gsyvideoplayer/video/LandLayoutVideo.java @@ -2,10 +2,17 @@ import android.content.Context; import android.util.AttributeSet; +import android.view.GestureDetector; +import android.view.MotionEvent; +import android.view.View; +import android.view.ViewGroup; import android.widget.ImageView; import com.example.gsyvideoplayer.R; +import com.shuyu.gsyvideoplayer.utils.Debuger; import com.shuyu.gsyvideoplayer.video.StandardGSYVideoPlayer; +import com.shuyu.gsyvideoplayer.video.base.GSYBaseVideoPlayer; +import com.shuyu.gsyvideoplayer.video.base.GSYVideoPlayer; /** * Created by shuyu on 2016/12/23. @@ -13,6 +20,8 @@ */ public class LandLayoutVideo extends StandardGSYVideoPlayer { + private boolean isLinkScroll = false; + /** * 1.5.0开始加入,如果需要不同布局区分功能,需要重载 */ @@ -29,6 +38,36 @@ public LandLayoutVideo(Context context, AttributeSet attrs) { } + @Override + protected void init(Context context) { + super.init(context); + post(new Runnable() { + @Override + public void run() { + gestureDetector = new GestureDetector(getContext().getApplicationContext(), new GestureDetector.SimpleOnGestureListener() { + @Override + public boolean onDoubleTap(MotionEvent e) { + touchDoubleUp(); + return super.onDoubleTap(e); + } + + @Override + public boolean onSingleTapConfirmed(MotionEvent e) { + if (!mChangePosition && !mChangeVolume && !mBrightness) { + onClickUiToggle(); + } + return super.onSingleTapConfirmed(e); + } + + @Override + public void onLongPress(MotionEvent e) { + super.onLongPress(e); + } + }); + } + }); + } + //这个必须配置最上面的构造才能生效 @Override public int getLayoutId() { @@ -56,5 +95,35 @@ protected void updateStartImage() { } } + @Override + public int getEnlargeImageRes() { + return R.drawable.custom_enlarge; + } + @Override + public int getShrinkImageRes() { + return R.drawable.custom_shrink; + } + + @Override + public boolean onInterceptTouchEvent(MotionEvent ev) { + if (isLinkScroll && !isIfCurrentIsFullscreen()) { + getParent().requestDisallowInterceptTouchEvent(true); + } + return super.onInterceptTouchEvent(ev); + } + + + @Override + protected void resolveNormalVideoShow(View oldF, ViewGroup vp, GSYVideoPlayer gsyVideoPlayer) { + LandLayoutVideo landLayoutVideo = (LandLayoutVideo)gsyVideoPlayer; + landLayoutVideo.dismissProgressDialog(); + landLayoutVideo.dismissVolumeDialog(); + landLayoutVideo.dismissBrightnessDialog(); + super.resolveNormalVideoShow(oldF, vp, gsyVideoPlayer); + } + + public void setLinkScroll(boolean linkScroll) { + isLinkScroll = linkScroll; + } } diff --git a/app/src/main/java/com/example/gsyvideoplayer/video/ListADVideoPlayer.java b/app/src/main/java/com/example/gsyvideoplayer/video/ListADVideoPlayer.java new file mode 100644 index 000000000..5b4b031a5 --- /dev/null +++ b/app/src/main/java/com/example/gsyvideoplayer/video/ListADVideoPlayer.java @@ -0,0 +1,46 @@ +package com.example.gsyvideoplayer.video; + +import android.content.Context; +import android.util.AttributeSet; + +import com.shuyu.gsyvideoplayer.listener.GSYMediaPlayerListener; +import com.shuyu.gsyvideoplayer.utils.Debuger; +import com.shuyu.gsyvideoplayer.video.GSYADVideoPlayer; + +public class ListADVideoPlayer extends GSYADVideoPlayer { + + public ListADVideoPlayer(Context context, Boolean fullFlag) { + super(context, fullFlag); + } + + public ListADVideoPlayer(Context context) { + super(context); + } + + public ListADVideoPlayer(Context context, AttributeSet attrs) { + super(context, attrs); + } + + @Override + protected void startPrepare() { + GSYMediaPlayerListener listener = getGSYVideoManager().listener(); + super.startPrepare(); + if (listener != null) { + listener.onAutoCompletion(); + } + } + + @Override + public void onAutoCompletion() { + super.onAutoCompletion(); + if (!isCurrentMediaListener() && mVideoAllCallBack != null) { + Debuger.printfLog("onAutoComplete"); + mVideoAllCallBack.onAutoComplete(mOriginUrl, mTitle, this); + } + } + + @Override + public void onCompletion() { + super.onCompletion(); + } +} diff --git a/app/src/main/java/com/example/gsyvideoplayer/video/MultiSampleVideo.java b/app/src/main/java/com/example/gsyvideoplayer/video/MultiSampleVideo.java new file mode 100644 index 000000000..648c795c5 --- /dev/null +++ b/app/src/main/java/com/example/gsyvideoplayer/video/MultiSampleVideo.java @@ -0,0 +1,149 @@ +package com.example.gsyvideoplayer.video; + +import android.content.Context; +import android.graphics.Point; +import android.media.AudioManager; +import android.text.TextUtils; +import android.util.AttributeSet; +import android.widget.ImageView; + +import com.bumptech.glide.Glide; +import com.bumptech.glide.request.RequestOptions; +import com.example.gsyvideoplayer.R; +import com.example.gsyvideoplayer.video.manager.CustomManager; +import com.shuyu.gsyvideoplayer.utils.Debuger; +import com.shuyu.gsyvideoplayer.video.StandardGSYVideoPlayer; +import com.shuyu.gsyvideoplayer.video.base.GSYBaseVideoPlayer; +import com.shuyu.gsyvideoplayer.video.base.GSYVideoViewBridge; + + +/** + * 多个同时播放的播放控件 + * Created by guoshuyu on 2018/1/31. + */ + +public class MultiSampleVideo extends StandardGSYVideoPlayer { + + private final static String TAG = "MultiSampleVideo"; + + + ImageView mCoverImage; + + String mCoverOriginUrl; + + int mDefaultRes; + + public MultiSampleVideo(Context context, Boolean fullFlag) { + super(context, fullFlag); + } + + public MultiSampleVideo(Context context) { + super(context); + } + + public MultiSampleVideo(Context context, AttributeSet attrs) { + super(context, attrs); + } + + @Override + protected void init(Context context) { + super.init(context); + mCoverImage = (ImageView) findViewById(R.id.thumbImage); + if (mThumbImageViewLayout != null && + (mCurrentState == -1 || mCurrentState == CURRENT_STATE_NORMAL || mCurrentState == CURRENT_STATE_ERROR)) { + mThumbImageViewLayout.setVisibility(VISIBLE); + } + onAudioFocusChangeListener = new AudioManager.OnAudioFocusChangeListener() { + @Override + public void onAudioFocusChange(int focusChange) { + switch (focusChange) { + case AudioManager.AUDIOFOCUS_GAIN: + break; + case AudioManager.AUDIOFOCUS_LOSS: + //todo 判断如果不是外界造成的就不处理 + break; + case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT: + //todo 判断如果不是外界造成的就不处理 + break; + case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK: + break; + } + } + }; + } + + @Override + public GSYVideoViewBridge getGSYVideoManager() { + CustomManager.getCustomManager(getKey()).initContext(getContext().getApplicationContext()); + return CustomManager.getCustomManager(getKey()); + } + + @Override + protected boolean backFromFull(Context context) { + return CustomManager.backFromWindowFull(context, getKey()); + } + + @Override + protected void releaseVideos() { + CustomManager.releaseAllVideos(getKey()); + } + + + @Override + protected int getFullId() { + return CustomManager.FULLSCREEN_ID; + } + + @Override + protected int getSmallId() { + return CustomManager.SMALL_ID; + } + + + @Override + public int getLayoutId() { + return R.layout.video_layout_cover; + } + + public void loadCoverImage(String url, int res) { + mCoverOriginUrl = url; + mDefaultRes = res; + Glide.with(getContext().getApplicationContext()) + .setDefaultRequestOptions( + new RequestOptions() + .frame(1000000) + .centerCrop() + .error(res) + .placeholder(res)) + .load(url) + .into(mCoverImage); + } + + @Override + public GSYBaseVideoPlayer startWindowFullscreen(Context context, boolean actionBar, boolean statusBar) { + GSYBaseVideoPlayer gsyBaseVideoPlayer = super.startWindowFullscreen(context, actionBar, statusBar); + MultiSampleVideo multiSampleVideo = (MultiSampleVideo) gsyBaseVideoPlayer; + multiSampleVideo.loadCoverImage(mCoverOriginUrl, mDefaultRes); + return multiSampleVideo; + } + + + @Override + public GSYBaseVideoPlayer showSmallVideo(Point size, boolean actionBar, boolean statusBar) { + //下面这里替换成你自己的强制转化 + MultiSampleVideo multiSampleVideo = (MultiSampleVideo) super.showSmallVideo(size, actionBar, statusBar); + multiSampleVideo.mStartButton.setVisibility(GONE); + multiSampleVideo.mStartButton = null; + return multiSampleVideo; + } + + public String getKey() { + if (mPlayPosition == -22) { + Debuger.printfError(getClass().getSimpleName() + " used getKey() " + "******* PlayPosition never set. ********"); + } + if (TextUtils.isEmpty(mPlayTag)) { + Debuger.printfError(getClass().getSimpleName() + " used getKey() " + "******* PlayTag never set. ********"); + } + return TAG + mPlayPosition + mPlayTag; + } +} diff --git a/gsyVideoPlayer/src/main/java/com/shuyu/gsyvideoplayer/video/CustomGSYVideoPlayer.java b/app/src/main/java/com/example/gsyvideoplayer/video/PreViewGSYVideoPlayer.java similarity index 53% rename from gsyVideoPlayer/src/main/java/com/shuyu/gsyvideoplayer/video/CustomGSYVideoPlayer.java rename to app/src/main/java/com/example/gsyvideoplayer/video/PreViewGSYVideoPlayer.java index 6b0685fba..d15350a17 100644 --- a/gsyVideoPlayer/src/main/java/com/shuyu/gsyvideoplayer/video/CustomGSYVideoPlayer.java +++ b/app/src/main/java/com/example/gsyvideoplayer/video/PreViewGSYVideoPlayer.java @@ -1,50 +1,50 @@ -package com.shuyu.gsyvideoplayer.video; +package com.example.gsyvideoplayer.video; import android.content.Context; -import android.graphics.SurfaceTexture; import android.util.AttributeSet; -import android.view.Surface; -import android.view.TextureView; -import android.view.ViewGroup; +import android.widget.ImageView; import android.widget.RelativeLayout; import android.widget.SeekBar; -import com.shuyu.gsyvideoplayer.GSYPreViewManager; -import com.shuyu.gsyvideoplayer.GSYTextureView; -import com.shuyu.gsyvideoplayer.GSYVideoManager; -import com.shuyu.gsyvideoplayer.R; +import com.bumptech.glide.Glide; +import com.bumptech.glide.request.RequestOptions; +import com.example.gsyvideoplayer.R; +import com.shuyu.gsyvideoplayer.utils.CommonUtil; +import com.shuyu.gsyvideoplayer.utils.Debuger; +import com.shuyu.gsyvideoplayer.video.NormalGSYVideoPlayer; +import com.shuyu.gsyvideoplayer.video.base.GSYBaseVideoPlayer; /** + * 进度图小图预览的另类实现 * Created by shuyu on 2016/12/10. - * 进度图小图预览,目前对本地视频还可以,网络视频效果不好,不建议使用 */ -public class CustomGSYVideoPlayer extends StandardGSYVideoPlayer { +public class PreViewGSYVideoPlayer extends NormalGSYVideoPlayer { private RelativeLayout mPreviewLayout; - private GSYTextureView mPreviewTexture; + private ImageView mPreView; //是否因为用户点击 private boolean mIsFromUser; //是否打开滑动预览 - private boolean mOpenPreView; + private boolean mOpenPreView = true; private int mPreProgress = -2; /** * 1.5.0开始加入,如果需要不同布局区分功能,需要重载 */ - public CustomGSYVideoPlayer(Context context, Boolean fullFlag) { + public PreViewGSYVideoPlayer(Context context, Boolean fullFlag) { super(context, fullFlag); } - public CustomGSYVideoPlayer(Context context) { + public PreViewGSYVideoPlayer(Context context) { super(context); } - public CustomGSYVideoPlayer(Context context, AttributeSet attrs) { + public PreViewGSYVideoPlayer(Context context, AttributeSet attrs) { super(context, attrs); } @@ -56,55 +56,17 @@ protected void init(Context context) { private void initView() { mPreviewLayout = (RelativeLayout) findViewById(R.id.preview_layout); + mPreView = (ImageView) findViewById(R.id.preview_image); } @Override public int getLayoutId() { - return R.layout.video_layout_custom; + return R.layout.video_layout_preview; } - @Override - protected void addTextureView() { - super.addTextureView(); - - if (mPreviewLayout.getChildCount() > 0) { - mPreviewLayout.removeAllViews(); - } - mPreviewTexture = null; - mPreviewTexture = new GSYTextureView(getActivityContext()); - mPreviewTexture.setSurfaceTextureListener(new TextureView.SurfaceTextureListener() { - @Override - public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) { - GSYPreViewManager.instance().setDisplay(new Surface(surface)); - } - - @Override - public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) { - - } - - @Override - public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) { - GSYPreViewManager.instance().setDisplay(null); - return true; - } - - @Override - public void onSurfaceTextureUpdated(SurfaceTexture surface) { - - } - }); - mPreviewTexture.setRotation(mRotate); - - RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT); - layoutParams.addRule(RelativeLayout.CENTER_IN_PARENT); - mPreviewLayout.addView(mPreviewTexture, layoutParams); - } - @Override protected void prepareVideo() { - GSYPreViewManager.instance().prepare(mUrl, mMapHeadData, mLooping, mSpeed); super.prepareVideo(); } @@ -113,18 +75,16 @@ public void onProgressChanged(SeekBar seekBar, final int progress, boolean fromU super.onProgressChanged(seekBar, progress, fromUser); if (fromUser && mOpenPreView) { int width = seekBar.getWidth(); + int time = progress * getDuration() / 100; int offset = (int) (width - (getResources().getDimension(R.dimen.seek_bar_image) / 2)) / 100 * progress; - + Debuger.printfError("***************** " + progress); + Debuger.printfError("***************** " + time); + showPreView(mOriginUrl, time); RelativeLayout.LayoutParams layoutParams = (RelativeLayout.LayoutParams) mPreviewLayout.getLayoutParams(); layoutParams.leftMargin = offset; //设置帧预览图的显示位置 mPreviewLayout.setLayoutParams(layoutParams); - if (GSYPreViewManager.instance().getMediaPlayer() != null - && mHadPlay && (mOpenPreView) - && GSYPreViewManager.instance().isSeekToComplete()) { - GSYPreViewManager.instance().setSeekToComplete(false); - int time = progress * getDuration() / 100; - GSYPreViewManager.instance().getMediaPlayer().seekTo(time); + if (mHadPlay && mOpenPreView) { mPreProgress = progress; } } @@ -165,11 +125,18 @@ protected void setTextAndProgress(int secProgress) { @Override public GSYBaseVideoPlayer startWindowFullscreen(Context context, boolean actionBar, boolean statusBar) { GSYBaseVideoPlayer gsyBaseVideoPlayer = super.startWindowFullscreen(context, actionBar, statusBar); - CustomGSYVideoPlayer customGSYVideoPlayer = (CustomGSYVideoPlayer) gsyBaseVideoPlayer; + PreViewGSYVideoPlayer customGSYVideoPlayer = (PreViewGSYVideoPlayer) gsyBaseVideoPlayer; customGSYVideoPlayer.mOpenPreView = mOpenPreView; return gsyBaseVideoPlayer; } + + @Override + public void onPrepared() { + super.onPrepared(); + startDownFrame(mOriginUrl); + } + public boolean isOpenPreView() { return mOpenPreView; } @@ -180,4 +147,38 @@ public boolean isOpenPreView() { public void setOpenPreView(boolean localFile) { this.mOpenPreView = localFile; } + + + private void showPreView(String url, long time) { + int width = CommonUtil.dip2px(getContext(), 150); + int height = CommonUtil.dip2px(getContext(), 100); + Glide.with(getContext().getApplicationContext()) + .setDefaultRequestOptions( + new RequestOptions() + //这里限制了只从缓存读取 + .onlyRetrieveFromCache(true) + .frame(1000 * time) + .override(width, height) + .dontAnimate() + .centerCrop()) + .load(url) + .into(mPreView); + } + + + private void startDownFrame(String url) { + for (int i = 1; i <= 100; i++) { + int time = i * getDuration() / 100; + int width = CommonUtil.dip2px(getContext(), 150); + int height = CommonUtil.dip2px(getContext(), 100); + Glide.with(getContext().getApplicationContext()) + .setDefaultRequestOptions( + new RequestOptions() + .frame(1000 * time) + .override(width, height) + .centerCrop()) + .load(url).preload(width, height); + + } + } } diff --git a/app/src/main/java/com/example/gsyvideoplayer/video/RequestListADVideoPlayer.java b/app/src/main/java/com/example/gsyvideoplayer/video/RequestListADVideoPlayer.java new file mode 100644 index 000000000..8c34fe1ae --- /dev/null +++ b/app/src/main/java/com/example/gsyvideoplayer/video/RequestListADVideoPlayer.java @@ -0,0 +1,52 @@ +package com.example.gsyvideoplayer.video; + +import android.content.Context; +import android.os.Handler; +import android.util.AttributeSet; + +import com.shuyu.gsyvideoplayer.GSYVideoADManager; + +import java.util.Random; + +public class RequestListADVideoPlayer extends ListADVideoPlayer { + + public RequestListADVideoPlayer(Context context, Boolean fullFlag) { + super(context, fullFlag); + } + + public RequestListADVideoPlayer(Context context) { + super(context); + } + + public RequestListADVideoPlayer(Context context, AttributeSet attrs) { + super(context, attrs); + } + + @Override + public void startPlayLogic() { + //模拟请求 + new Handler().postDelayed(new Runnable() { + @Override + public void run() { + int min = 1; + int max = 10; + Random random = new Random(); + int i = random.nextInt(max) % (max - min + 1) + min; + if (i % 3 == 0) { + mOriginUrl = "http://wdquan-space.b0.upaiyun.com/VIDEO/2018/11/22/ae0645396048_hls_time10.m3u8"; + mUrl = "http://wdquan-space.b0.upaiyun.com/VIDEO/2018/11/22/ae0645396048_hls_time10.m3u8"; + mTitle = "ggg2"; + } else if (i % 4 == 0) { + //模拟请求失败 + onError(0, 0); + } else { + mOriginUrl = "http://7xjmzj.com1.z0.glb.clouddn.com/20171026175005_JObCxCE2.mp4"; + mUrl = "http://7xjmzj.com1.z0.glb.clouddn.com/20171026175005_JObCxCE2.mp4"; + mTitle = "ffff1"; + } + mCache = false; + RequestListADVideoPlayer.super.startPlayLogic(); + } + }, 2000); + } +} diff --git a/app/src/main/java/com/example/gsyvideoplayer/video/SampleControlVideo.java b/app/src/main/java/com/example/gsyvideoplayer/video/SampleControlVideo.java new file mode 100644 index 000000000..fdde5e217 --- /dev/null +++ b/app/src/main/java/com/example/gsyvideoplayer/video/SampleControlVideo.java @@ -0,0 +1,265 @@ +package com.example.gsyvideoplayer.video; + +import android.content.Context; +import android.graphics.Matrix; +import android.util.AttributeSet; +import android.view.Surface; +import android.view.View; +import android.view.ViewGroup; +import android.widget.TextView; + +import com.example.gsyvideoplayer.R; +import com.shuyu.gsyvideoplayer.utils.GSYVideoType; +import com.shuyu.gsyvideoplayer.video.StandardGSYVideoPlayer; +import com.shuyu.gsyvideoplayer.video.base.GSYBaseVideoPlayer; +import com.shuyu.gsyvideoplayer.video.base.GSYVideoPlayer; + + +/** + * Created by shuyu on 2016/12/7. + * 注意 + * 这个播放器的demo配置切换到全屏播放器 + * 这只是单纯的作为全屏播放显示,如果需要做大小屏幕切换,请记得在这里耶设置上视频全屏的需要的自定义配置 + */ + +public class SampleControlVideo extends StandardGSYVideoPlayer { + + private TextView mMoreScale; + + private TextView mChangeRotate; + + private TextView mChangeTransform; + + //记住切换数据源类型 + private int mType = 0; + + private int mTransformSize = 0; + + //数据源 + private int mSourcePosition = 0; + + /** + * 1.5.0开始加入,如果需要不同布局区分功能,需要重载 + */ + public SampleControlVideo(Context context, Boolean fullFlag) { + super(context, fullFlag); + } + + public SampleControlVideo(Context context) { + super(context); + } + + public SampleControlVideo(Context context, AttributeSet attrs) { + super(context, attrs); + } + + @Override + protected void init(Context context) { + super.init(context); + initView(); + } + + private void initView() { + mMoreScale = (TextView) findViewById(R.id.moreScale); + mChangeRotate = (TextView) findViewById(R.id.change_rotate); + mChangeTransform = (TextView) findViewById(R.id.change_transform); + + //切换清晰度 + mMoreScale.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + if (!mHadPlay) { + return; + } + if (mType == 0) { + mType = 1; + } else if (mType == 1) { + mType = 2; + } else if (mType == 2) { + mType = 3; + } else if (mType == 3) { + mType = 4; + } else if (mType == 4) { + mType = 0; + } + resolveTypeUI(); + } + }); + + //旋转播放角度 + mChangeRotate.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + if (!mHadPlay) { + return; + } + if ((mTextureView.getRotation() - mRotate) == 270) { + mTextureView.setRotation(mRotate); + mTextureView.requestLayout(); + } else { + mTextureView.setRotation(mTextureView.getRotation() + 90); + mTextureView.requestLayout(); + } + } + }); + + //镜像旋转 + mChangeTransform.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + if (!mHadPlay) { + return; + } + if (mTransformSize == 0) { + mTransformSize = 1; + } else if (mTransformSize == 1) { + mTransformSize = 2; + } else if (mTransformSize == 2) { + mTransformSize = 0; + } + resolveTransform(); + } + }); + + } + + @Override + public void onSurfaceSizeChanged(Surface surface, int width, int height) { + super.onSurfaceSizeChanged(surface, width, height); + resolveTransform(); + } + + /** + * 处理显示逻辑 + */ + @Override + public void onSurfaceAvailable(Surface surface) { + super.onSurfaceAvailable(surface); + resolveRotateUI(); + resolveTransform(); + } + + /** + * 处理镜像旋转 + * 注意,暂停时 + */ + protected void resolveTransform() { + switch (mTransformSize) { + case 1: { + Matrix transform = new Matrix(); + transform.setScale(-1, 1, mTextureView.getWidth() / 2, 0); + mTextureView.setTransform(transform); + mChangeTransform.setText("左右镜像"); + mTextureView.invalidate(); + } + break; + case 2: { + Matrix transform = new Matrix(); + transform.setScale(1, -1, 0, mTextureView.getHeight() / 2); + mTextureView.setTransform(transform); + mChangeTransform.setText("上下镜像"); + mTextureView.invalidate(); + } + break; + case 0: { + Matrix transform = new Matrix(); + transform.setScale(1, 1, mTextureView.getWidth() / 2, 0); + mTextureView.setTransform(transform); + mChangeTransform.setText("旋转镜像"); + mTextureView.invalidate(); + } + break; + } + } + + @Override + public int getLayoutId() { + return R.layout.sample_video; + } + + + /** + * 全屏时将对应处理参数逻辑赋给全屏播放器 + * + * @param context + * @param actionBar + * @param statusBar + * @return + */ + @Override + public GSYBaseVideoPlayer startWindowFullscreen(Context context, boolean actionBar, boolean statusBar) { + SampleControlVideo sampleVideo = (SampleControlVideo) super.startWindowFullscreen(context, actionBar, statusBar); + sampleVideo.mSourcePosition = mSourcePosition; + sampleVideo.mType = mType; + sampleVideo.mTransformSize = mTransformSize; + //sampleVideo.resolveTransform(); + sampleVideo.resolveTypeUI(); + //sampleVideo.resolveRotateUI(); + //这个播放器的demo配置切换到全屏播放器 + //这只是单纯的作为全屏播放显示,如果需要做大小屏幕切换,请记得在这里耶设置上视频全屏的需要的自定义配置 + //比如已旋转角度之类的等等 + //可参考super中的实现 + return sampleVideo; + } + + /** + * 推出全屏时将对应处理参数逻辑返回给非播放器 + * + * @param oldF + * @param vp + * @param gsyVideoPlayer + */ + @Override + protected void resolveNormalVideoShow(View oldF, ViewGroup vp, GSYVideoPlayer gsyVideoPlayer) { + super.resolveNormalVideoShow(oldF, vp, gsyVideoPlayer); + if (gsyVideoPlayer != null) { + SampleControlVideo sampleVideo = (SampleControlVideo) gsyVideoPlayer; + mSourcePosition = sampleVideo.mSourcePosition; + mType = sampleVideo.mType; + mTransformSize = sampleVideo.mTransformSize; + resolveTypeUI(); + } + } + + /** + * 旋转逻辑 + */ + private void resolveRotateUI() { + if (!mHadPlay) { + return; + } + mTextureView.setRotation(mRotate); + mTextureView.requestLayout(); + } + + /** + * 显示比例 + * 注意,GSYVideoType.setShowType是全局静态生效,除非重启APP。 + */ + private void resolveTypeUI() { + if (!mHadPlay) { + return; + } + if (mType == 1) { + mMoreScale.setText("16:9"); + GSYVideoType.setShowType(GSYVideoType.SCREEN_TYPE_16_9); + } else if (mType == 2) { + mMoreScale.setText("4:3"); + GSYVideoType.setShowType(GSYVideoType.SCREEN_TYPE_4_3); + } else if (mType == 3) { + mMoreScale.setText("全屏"); + GSYVideoType.setShowType(GSYVideoType.SCREEN_TYPE_FULL); + } else if (mType == 4) { + mMoreScale.setText("拉伸全屏"); + GSYVideoType.setShowType(GSYVideoType.SCREEN_MATCH_FULL); + } else if (mType == 0) { + mMoreScale.setText("默认比例"); + GSYVideoType.setShowType(GSYVideoType.SCREEN_TYPE_DEFAULT); + } + changeTextureViewShowType(); + if (mTextureView != null) + mTextureView.requestLayout(); + } + + +} diff --git a/app/src/main/java/com/example/gsyvideoplayer/video/SampleCoverVideo.java b/app/src/main/java/com/example/gsyvideoplayer/video/SampleCoverVideo.java new file mode 100644 index 000000000..121e4f64b --- /dev/null +++ b/app/src/main/java/com/example/gsyvideoplayer/video/SampleCoverVideo.java @@ -0,0 +1,241 @@ +package com.example.gsyvideoplayer.video; + +import android.content.Context; +import android.graphics.Point; +import android.util.AttributeSet; +import android.view.Surface; +import android.view.View; +import android.view.ViewGroup; +import android.view.Window; +import android.widget.ImageView; +import android.widget.SeekBar; + +import com.bumptech.glide.Glide; +import com.bumptech.glide.request.RequestOptions; +import com.example.gsyvideoplayer.R; +import com.shuyu.gsyvideoplayer.utils.CommonUtil; +import com.shuyu.gsyvideoplayer.utils.Debuger; +import com.shuyu.gsyvideoplayer.utils.GSYVideoType; +import com.shuyu.gsyvideoplayer.video.StandardGSYVideoPlayer; +import com.shuyu.gsyvideoplayer.video.base.GSYBaseVideoPlayer; + + +/** + * 带封面 + * Created by guoshuyu on 2017/9/3. + */ + +public class SampleCoverVideo extends StandardGSYVideoPlayer { + + ImageView mCoverImage; + + String mCoverOriginUrl; + + int mDefaultRes; + + public SampleCoverVideo(Context context, Boolean fullFlag) { + super(context, fullFlag); + } + + public SampleCoverVideo(Context context) { + super(context); + } + + public SampleCoverVideo(Context context, AttributeSet attrs) { + super(context, attrs); + } + + @Override + protected void init(Context context) { + super.init(context); + mCoverImage = (ImageView) findViewById(R.id.thumbImage); + + if (mThumbImageViewLayout != null && + (mCurrentState == -1 || mCurrentState == CURRENT_STATE_NORMAL || mCurrentState == CURRENT_STATE_ERROR)) { + mThumbImageViewLayout.setVisibility(VISIBLE); + } + } + + @Override + public int getLayoutId() { + return R.layout.video_layout_cover; + } + + public void loadCoverImage(String url, int res) { + mCoverOriginUrl = url; + mDefaultRes = res; + Glide.with(getContext().getApplicationContext()) + .setDefaultRequestOptions( + new RequestOptions() + .frame(1000000) + .centerCrop() + .error(res) + .placeholder(res)) + .load(url) + .into(mCoverImage); + } + + @Override + public GSYBaseVideoPlayer startWindowFullscreen(Context context, boolean actionBar, boolean statusBar) { + GSYBaseVideoPlayer gsyBaseVideoPlayer = super.startWindowFullscreen(context, actionBar, statusBar); + SampleCoverVideo sampleCoverVideo = (SampleCoverVideo) gsyBaseVideoPlayer; + sampleCoverVideo.loadCoverImage(mCoverOriginUrl, mDefaultRes); + return gsyBaseVideoPlayer; + } + + + @Override + public GSYBaseVideoPlayer showSmallVideo(Point size, boolean actionBar, boolean statusBar) { + //下面这里替换成你自己的强制转化 + SampleCoverVideo sampleCoverVideo = (SampleCoverVideo) super.showSmallVideo(size, actionBar, statusBar); + sampleCoverVideo.mStartButton.setVisibility(GONE); + sampleCoverVideo.mStartButton = null; + return sampleCoverVideo; + } + + @Override + protected void cloneParams(GSYBaseVideoPlayer from, GSYBaseVideoPlayer to) { + super.cloneParams(from, to); + SampleCoverVideo sf = (SampleCoverVideo) from; + SampleCoverVideo st = (SampleCoverVideo) to; + st.mShowFullAnimation = sf.mShowFullAnimation; + } + + + /** + * 退出window层播放全屏效果 + */ + @SuppressWarnings("ResourceType") + @Override + protected void clearFullscreenLayout() { + if (!mFullAnimEnd) { + return; + } + mIfCurrentIsFullscreen = false; + int delay = 0; + if (mOrientationUtils != null) { + delay = mOrientationUtils.backToProtVideo(); + mOrientationUtils.setEnable(false); + if (mOrientationUtils != null) { + mOrientationUtils.releaseListener(); + mOrientationUtils = null; + } + } + + if (!mShowFullAnimation) { + delay = 0; + } + + final ViewGroup vp = (CommonUtil.scanForActivity(getContext())).findViewById(Window.ID_ANDROID_CONTENT); + final View oldF = vp.findViewById(getFullId()); + if (oldF != null) { + //此处fix bug#265,推出全屏的时候,虚拟按键问题 + SampleCoverVideo gsyVideoPlayer = (SampleCoverVideo) oldF; + gsyVideoPlayer.mIfCurrentIsFullscreen = false; + } + + if (delay == 0) { + backToNormal(); + } else { + postDelayed(new Runnable() { + @Override + public void run() { + backToNormal(); + } + }, delay); + } + + } + + + /******************* 下方两个重载方法,在播放开始前不屏蔽封面,不需要可屏蔽 ********************/ + @Override + public void onSurfaceUpdated(Surface surface) { + super.onSurfaceUpdated(surface); + if (mThumbImageViewLayout != null && mThumbImageViewLayout.getVisibility() == VISIBLE) { + mThumbImageViewLayout.setVisibility(INVISIBLE); + } + } + + @Override + protected void setViewShowState(View view, int visibility) { + if (view == mThumbImageViewLayout && visibility != VISIBLE) { + return; + } + super.setViewShowState(view, visibility); + } + + @Override + public void onSurfaceAvailable(Surface surface) { + super.onSurfaceAvailable(surface); + if (GSYVideoType.getRenderType() != GSYVideoType.TEXTURE) { + if (mThumbImageViewLayout != null && mThumbImageViewLayout.getVisibility() == VISIBLE) { + mThumbImageViewLayout.setVisibility(INVISIBLE); + } + } + } + + /******************* 下方重载方法,在播放开始不显示底部进度和按键,不需要可屏蔽 ********************/ + + protected boolean byStartedClick; + + @Override + protected void onClickUiToggle() { + if (mIfCurrentIsFullscreen && mLockCurScreen && mNeedLockFull) { + setViewShowState(mLockScreen, VISIBLE); + return; + } + byStartedClick = true; + super.onClickUiToggle(); + + } + + @Override + protected void changeUiToNormal() { + super.changeUiToNormal(); + byStartedClick = false; + } + + @Override + protected void changeUiToPreparingShow() { + super.changeUiToPreparingShow(); + Debuger.printfLog("Sample changeUiToPreparingShow"); + setViewShowState(mBottomContainer, INVISIBLE); + setViewShowState(mStartButton, INVISIBLE); + } + + @Override + protected void changeUiToPlayingBufferingShow() { + super.changeUiToPlayingBufferingShow(); + Debuger.printfLog("Sample changeUiToPlayingBufferingShow"); + if (!byStartedClick) { + setViewShowState(mBottomContainer, INVISIBLE); + setViewShowState(mStartButton, INVISIBLE); + } + } + + @Override + protected void changeUiToPlayingShow() { + super.changeUiToPlayingShow(); + Debuger.printfLog("Sample changeUiToPlayingShow"); + if (!byStartedClick) { + setViewShowState(mBottomContainer, INVISIBLE); + setViewShowState(mStartButton, INVISIBLE); + } + } + + @Override + public void startAfterPrepared() { + super.startAfterPrepared(); + Debuger.printfLog("Sample startAfterPrepared"); + setViewShowState(mBottomContainer, INVISIBLE); + setViewShowState(mStartButton, INVISIBLE); + setViewShowState(mBottomProgressBar, VISIBLE); + } + + @Override + public void onStartTrackingTouch(SeekBar seekBar) { + byStartedClick = true; + super.onStartTrackingTouch(seekBar); + } +} diff --git a/app/src/main/java/com/example/gsyvideoplayer/video/SampleVideo.java b/app/src/main/java/com/example/gsyvideoplayer/video/SampleVideo.java index 6e0922fc2..9d27a5a7a 100644 --- a/app/src/main/java/com/example/gsyvideoplayer/video/SampleVideo.java +++ b/app/src/main/java/com/example/gsyvideoplayer/video/SampleVideo.java @@ -5,19 +5,18 @@ import android.graphics.SurfaceTexture; import android.os.Handler; import android.util.AttributeSet; +import android.view.Surface; import android.view.View; import android.view.ViewGroup; -import android.widget.ImageView; import android.widget.TextView; import android.widget.Toast; import com.example.gsyvideoplayer.R; import com.example.gsyvideoplayer.model.SwitchVideoModel; import com.example.gsyvideoplayer.view.SwitchVideoTypeDialog; -import com.shuyu.gsyvideoplayer.GSYVideoManager; -import com.shuyu.gsyvideoplayer.GSYVideoPlayer; +import com.shuyu.gsyvideoplayer.video.base.GSYVideoPlayer; import com.shuyu.gsyvideoplayer.utils.GSYVideoType; -import com.shuyu.gsyvideoplayer.video.GSYBaseVideoPlayer; +import com.shuyu.gsyvideoplayer.video.base.GSYBaseVideoPlayer; import com.shuyu.gsyvideoplayer.video.StandardGSYVideoPlayer; import java.io.File; @@ -51,6 +50,8 @@ public class SampleVideo extends StandardGSYVideoPlayer { //数据源 private int mSourcePosition = 0; + private String mTypeText = "标准"; + /** * 1.5.0开始加入,如果需要不同布局区分功能,需要重载 */ @@ -82,7 +83,7 @@ private void initView() { mMoreScale.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { - if(!mHadPlay) { + if (!mHadPlay) { return; } if (mType == 0) { @@ -93,7 +94,7 @@ public void onClick(View v) { mType = 3; } else if (mType == 3) { mType = 4; - } else if(mType == 4) { + } else if (mType == 4) { mType = 0; } resolveTypeUI(); @@ -112,19 +113,15 @@ public void onClick(View v) { mChangeRotate.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { - if(!mHadPlay) { + if (!mHadPlay) { return; } if ((mTextureView.getRotation() - mRotate) == 270) { mTextureView.setRotation(mRotate); mTextureView.requestLayout(); - mCoverImageView.setRotation(mRotate); - mCoverImageView.requestLayout(); } else { mTextureView.setRotation(mTextureView.getRotation() + 90); mTextureView.requestLayout(); - mCoverImageView.setRotation(mCoverImageView.getRotation() + 90); - mCoverImageView.requestLayout(); } } }); @@ -133,7 +130,7 @@ public void onClick(View v) { mChangeTransform.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { - if(!mHadPlay) { + if (!mHadPlay) { return; } if (mTransformSize == 0) { @@ -153,45 +150,46 @@ public void onClick(View v) { * 需要在尺寸发生变化的时候重新处理 */ @Override - public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) { - super.onSurfaceTextureSizeChanged(surface, width, height); + public void onSurfaceSizeChanged(Surface surface, int width, int height) { + super.onSurfaceSizeChanged(surface, width, height); + resolveTransform(); + } + + @Override + public void onSurfaceAvailable(Surface surface) { + super.onSurfaceAvailable(surface); + resolveRotateUI(); resolveTransform(); } /** * 处理镜像旋转 + * 注意,暂停时 */ protected void resolveTransform() { - switch (mTransformSize) { case 1: { Matrix transform = new Matrix(); transform.setScale(-1, 1, mTextureView.getWidth() / 2, 0); mTextureView.setTransform(transform); - mCoverImageView.setScaleType(ImageView.ScaleType.MATRIX); - mCoverImageView.setImageMatrix(transform); - mTransformCover = transform; mChangeTransform.setText("左右镜像"); + mTextureView.invalidate(); } break; case 2: { Matrix transform = new Matrix(); transform.setScale(1, -1, 0, mTextureView.getHeight() / 2); mTextureView.setTransform(transform); - mCoverImageView.setScaleType(ImageView.ScaleType.MATRIX); - mCoverImageView.setImageMatrix(transform); - mTransformCover = transform; mChangeTransform.setText("上下镜像"); + mTextureView.invalidate(); } break; case 0: { Matrix transform = new Matrix(); transform.setScale(1, 1, mTextureView.getWidth() / 2, 0); mTextureView.setTransform(transform); - mCoverImageView.setScaleType(ImageView.ScaleType.MATRIX); - mCoverImageView.setImageMatrix(transform); - mTransformCover = null; mChangeTransform.setText("旋转镜像"); + mTextureView.invalidate(); } break; } @@ -203,7 +201,7 @@ protected void resolveTransform() { * * @param url 播放url * @param cacheWithPlay 是否边播边缓存 - * @param title title + * @param title title * @return */ public boolean setUp(List url, boolean cacheWithPlay, String title) { @@ -233,6 +231,7 @@ public int getLayoutId() { /** * 全屏时将对应处理参数逻辑赋给全屏播放器 + * * @param context * @param actionBar * @param statusBar @@ -245,6 +244,7 @@ public GSYBaseVideoPlayer startWindowFullscreen(Context context, boolean actionB sampleVideo.mType = mType; sampleVideo.mTransformSize = mTransformSize; sampleVideo.mUrlList = mUrlList; + sampleVideo.mTypeText = mTypeText; //sampleVideo.resolveTransform(); sampleVideo.resolveTypeUI(); //sampleVideo.resolveRotateUI(); @@ -256,8 +256,8 @@ public GSYBaseVideoPlayer startWindowFullscreen(Context context, boolean actionB } /** - * * 推出全屏时将对应处理参数逻辑返回给非播放器 + * * @param oldF * @param vp * @param gsyVideoPlayer @@ -270,32 +270,21 @@ protected void resolveNormalVideoShow(View oldF, ViewGroup vp, GSYVideoPlayer gs mSourcePosition = sampleVideo.mSourcePosition; mType = sampleVideo.mType; mTransformSize = sampleVideo.mTransformSize; + mTypeText = sampleVideo.mTypeText; setUp(mUrlList, mCache, mCachePath, mTitle); resolveTypeUI(); } } - /** - * 处理显示逻辑 - */ - @Override - public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) { - super.onSurfaceTextureAvailable(surface, width, height); - resolveRotateUI(); - resolveTransform(); - } - /** * 旋转逻辑 */ private void resolveRotateUI() { - if(!mHadPlay) { + if (!mHadPlay) { return; } mTextureView.setRotation(mRotate); mTextureView.requestLayout(); - mCoverImageView.setRotation(mRotate); - mCoverImageView.requestLayout(); } /** @@ -303,42 +292,36 @@ private void resolveRotateUI() { * 注意,GSYVideoType.setShowType是全局静态生效,除非重启APP。 */ private void resolveTypeUI() { - if(!mHadPlay) { + if (!mHadPlay) { return; } if (mType == 1) { mMoreScale.setText("16:9"); GSYVideoType.setShowType(GSYVideoType.SCREEN_TYPE_16_9); - if (mTextureView != null) - mTextureView.requestLayout(); } else if (mType == 2) { mMoreScale.setText("4:3"); GSYVideoType.setShowType(GSYVideoType.SCREEN_TYPE_4_3); - if (mTextureView != null) - mTextureView.requestLayout(); } else if (mType == 3) { mMoreScale.setText("全屏"); GSYVideoType.setShowType(GSYVideoType.SCREEN_TYPE_FULL); - if (mTextureView != null) - mTextureView.requestLayout(); } else if (mType == 4) { mMoreScale.setText("拉伸全屏"); GSYVideoType.setShowType(GSYVideoType.SCREEN_MATCH_FULL); - if (mTextureView != null) - mTextureView.requestLayout(); } else if (mType == 0) { mMoreScale.setText("默认比例"); GSYVideoType.setShowType(GSYVideoType.SCREEN_TYPE_DEFAULT); - if (mTextureView != null) - mTextureView.requestLayout(); } + changeTextureViewShowType(); + if (mTextureView != null) + mTextureView.requestLayout(); + mSwitchSize.setText(mTypeText); } /** * 弹出切换清晰度 */ private void showSwitchDialog() { - if(!mHadPlay) { + if (!mHadPlay) { return; } SwitchVideoTypeDialog switchVideoTypeDialog = new SwitchVideoTypeDialog(getContext()); @@ -348,12 +331,11 @@ public void onItemClick(int position) { final String name = mUrlList.get(position).getName(); if (mSourcePosition != position) { if ((mCurrentState == GSYVideoPlayer.CURRENT_STATE_PLAYING - || mCurrentState == GSYVideoPlayer.CURRENT_STATE_PAUSE) - && GSYVideoManager.instance().getMediaPlayer() != null) { + || mCurrentState == GSYVideoPlayer.CURRENT_STATE_PAUSE)) { final String url = mUrlList.get(position).getUrl(); onVideoPause(); final long currentPosition = mCurrentPosition; - GSYVideoManager.instance().releaseMediaPlayer(); + getGSYVideoManager().releaseMediaPlayer(); cancelProgressTimer(); hideAllWidget(); new Handler().postDelayed(new Runnable() { @@ -366,6 +348,7 @@ public void run() { hideAllWidget(); } }, 500); + mTypeText = name; mSwitchSize.setText(name); mSourcePosition = position; } diff --git a/app/src/main/java/com/example/gsyvideoplayer/video/SmartPickVideo.java b/app/src/main/java/com/example/gsyvideoplayer/video/SmartPickVideo.java new file mode 100644 index 000000000..3ff196f90 --- /dev/null +++ b/app/src/main/java/com/example/gsyvideoplayer/video/SmartPickVideo.java @@ -0,0 +1,360 @@ +package com.example.gsyvideoplayer.video; + +import android.content.Context; +import android.util.AttributeSet; +import android.view.Surface; +import android.view.View; +import android.view.ViewGroup; +import android.widget.TextView; +import android.widget.Toast; + +import com.example.gsyvideoplayer.R; +import com.example.gsyvideoplayer.model.SwitchVideoModel; +import com.example.gsyvideoplayer.view.LoadingDialog; +import com.example.gsyvideoplayer.view.SwitchVideoTypeDialog; +import com.shuyu.gsyvideoplayer.GSYVideoBaseManager; +import com.shuyu.gsyvideoplayer.GSYVideoManager; +import com.shuyu.gsyvideoplayer.listener.GSYMediaPlayerListener; +import com.shuyu.gsyvideoplayer.utils.Debuger; +import com.shuyu.gsyvideoplayer.video.StandardGSYVideoPlayer; +import com.shuyu.gsyvideoplayer.video.base.GSYBaseVideoPlayer; +import com.shuyu.gsyvideoplayer.video.base.GSYVideoPlayer; + +import java.io.File; +import java.util.ArrayList; +import java.util.List; + +/** + * 无缝切换视频的DEMO + * 这里是切换清晰度,稍微修改下也可以作为切换下一集等 + */ + +public class SmartPickVideo extends StandardGSYVideoPlayer { + + + private TextView mSwitchSize; + + private List mUrlList = new ArrayList<>(); + + //记住切换数据源类型 + private int mType = 0; + //数据源 + private int mSourcePosition = 0; + private int mPreSourcePosition = 0; + + private String mTypeText = "标准"; + + private GSYVideoManager mTmpManager; + + //切换过程中最好弹出loading,不给其他任何操作 + private LoadingDialog mLoadingDialog; + + private boolean isChanging; + + /** + * 1.5.0开始加入,如果需要不同布局区分功能,需要重载 + */ + public SmartPickVideo(Context context, Boolean fullFlag) { + super(context, fullFlag); + } + + public SmartPickVideo(Context context) { + super(context); + } + + public SmartPickVideo(Context context, AttributeSet attrs) { + super(context, attrs); + } + + @Override + protected void init(Context context) { + super.init(context); + initView(); + } + + private void initView() { + mSwitchSize = (TextView) findViewById(R.id.switchSize); + //切换视频清晰度 + mSwitchSize.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + if (mHadPlay && !isChanging) { + showSwitchDialog(); + } + } + }); + } + + /** + * 设置播放URL + * + * @param url 播放url + * @param cacheWithPlay 是否边播边缓存 + * @param title title + * @return + */ + public boolean setUp(List url, boolean cacheWithPlay, String title) { + mUrlList = url; + return setUp(url.get(mSourcePosition).getUrl(), cacheWithPlay, title); + } + + /** + * 设置播放URL + * + * @param url 播放url + * @param cacheWithPlay 是否边播边缓存 + * @param cachePath 缓存路径,如果是M3U8或者HLS,请设置为false + * @param title title + * @return + */ + public boolean setUp(List url, boolean cacheWithPlay, File cachePath, String title) { + mUrlList = url; + return setUp(url.get(mSourcePosition).getUrl(), cacheWithPlay, cachePath, title); + } + + @Override + public int getLayoutId() { + return R.layout.sample_video_pick; + } + + + /** + * 全屏时将对应处理参数逻辑赋给全屏播放器 + * + * @param context + * @param actionBar + * @param statusBar + * @return + */ + @Override + public GSYBaseVideoPlayer startWindowFullscreen(Context context, boolean actionBar, boolean statusBar) { + SmartPickVideo sampleVideo = (SmartPickVideo) super.startWindowFullscreen(context, actionBar, statusBar); + sampleVideo.mSourcePosition = mSourcePosition; + sampleVideo.mListItemRect = mListItemRect; + sampleVideo.mListItemSize = mListItemSize; + sampleVideo.mType = mType; + sampleVideo.mUrlList = mUrlList; + sampleVideo.mTypeText = mTypeText; + sampleVideo.mSwitchSize.setText(mTypeText); + return sampleVideo; + } + + /** + * 推出全屏时将对应处理参数逻辑返回给非播放器 + * + * @param oldF + * @param vp + * @param gsyVideoPlayer + */ + @Override + protected void resolveNormalVideoShow(View oldF, ViewGroup vp, GSYVideoPlayer gsyVideoPlayer) { + super.resolveNormalVideoShow(oldF, vp, gsyVideoPlayer); + if (gsyVideoPlayer != null) { + SmartPickVideo sampleVideo = (SmartPickVideo) gsyVideoPlayer; + mSourcePosition = sampleVideo.mSourcePosition; + mType = sampleVideo.mType; + mTypeText = sampleVideo.mTypeText; + mSwitchSize.setText(mTypeText); + setUp(mUrlList, mCache, mCachePath, mTitle); + } + } + + @Override + public void onAutoCompletion() { + super.onAutoCompletion(); + releaseTmpManager(); + } + + @Override + public void onCompletion() { + super.onCompletion(); + releaseTmpManager(); + } + + /** + * 弹出切换清晰度 + */ + private void showSwitchDialog() { + if (!mHadPlay) { + return; + } + SwitchVideoTypeDialog switchVideoTypeDialog = new SwitchVideoTypeDialog(getContext()); + switchVideoTypeDialog.initList(mUrlList, new SwitchVideoTypeDialog.OnListItemClickListener() { + @Override + public void onItemClick(int position) { + resolveStartChange(position); + } + }); + switchVideoTypeDialog.show(); + } + + + private void resolveChangeUrl(boolean cacheWithPlay, File cachePath, String url) { + if (mTmpManager != null) { + mCache = cacheWithPlay; + mCachePath = cachePath; + mOriginUrl = url; + this.mUrl = url; + } + } + + + private GSYMediaPlayerListener gsyMediaPlayerListener = new GSYMediaPlayerListener() { + @Override + public void onPrepared() { + if (mTmpManager != null) { + mTmpManager.start(); + mTmpManager.seekTo(getCurrentPositionWhenPlaying()); + } + } + + @Override + public void onAutoCompletion() { + + } + + @Override + public void onCompletion() { + + } + + @Override + public void onBufferingUpdate(int percent) { + + } + + @Override + public void onSeekComplete() { + if (mTmpManager != null) { + GSYVideoBaseManager manager = GSYVideoManager.instance(); + GSYVideoManager.changeManager(mTmpManager); + mTmpManager.setLastListener(manager.lastListener()); + mTmpManager.setListener(manager.listener()); + + manager.setDisplay(null); + + Debuger.printfError("**** showDisplay onSeekComplete ***** " + mSurface); + Debuger.printfError("**** showDisplay onSeekComplete isValid***** " + mSurface.isValid()); + mTmpManager.setDisplay(mSurface); + changeUiToPlayingClear(); + resolveChangedResult(); + manager.releaseMediaPlayer(); + } + } + + @Override + public void onError(int what, int extra) { + mSourcePosition = mPreSourcePosition; + if (mTmpManager != null) { + mTmpManager.releaseMediaPlayer(); + } + post(new Runnable() { + @Override + public void run() { + resolveChangedResult(); + Toast.makeText(mContext, "change Fail", Toast.LENGTH_LONG).show(); + } + }); + } + + @Override + public void onInfo(int what, int extra) { + + } + + @Override + public void onVideoSizeChanged() { + + } + + @Override + public void onBackFullscreen() { + + } + + @Override + public void onVideoPause() { + + } + + @Override + public void onVideoResume() { + + } + + @Override + public void onVideoResume(boolean seek) { + + } + }; + + private void resolveStartChange(int position) { + final String name = mUrlList.get(position).getName(); + if (mSourcePosition != position) { + if ((mCurrentState == GSYVideoPlayer.CURRENT_STATE_PLAYING + || mCurrentState == GSYVideoPlayer.CURRENT_STATE_PAUSE)) { + showLoading(); + final String url = mUrlList.get(position).getUrl(); + cancelProgressTimer(); + hideAllWidget(); + if (mTitle != null && mTitleTextView != null) { + mTitleTextView.setText(mTitle); + } + mPreSourcePosition = mSourcePosition; + isChanging = true; + mTypeText = name; + mSwitchSize.setText(name); + mSourcePosition = position; + //创建临时管理器执行加载播放 + mTmpManager = GSYVideoManager.tmpInstance(gsyMediaPlayerListener); + mTmpManager.initContext(getContext().getApplicationContext()); + resolveChangeUrl(mCache, mCachePath, url); + mTmpManager.prepare(mUrl, mMapHeadData, mLooping, mSpeed, mCache, mCachePath, null); + changeUiToPlayingBufferingShow(); + } + } else { + Toast.makeText(getContext(), "已经是 " + name, Toast.LENGTH_LONG).show(); + } + } + + private void resolveChangedResult() { + isChanging = false; + mTmpManager = null; + final String name = mUrlList.get(mSourcePosition).getName(); + final String url = mUrlList.get(mSourcePosition).getUrl(); + mTypeText = name; + mSwitchSize.setText(name); + resolveChangeUrl(mCache, mCachePath, url); + hideLoading(); + } + + private void releaseTmpManager() { + if (mTmpManager != null) { + mTmpManager.releaseMediaPlayer(); + mTmpManager = null; + } + } + + private void showLoading() { + hideLoading(); + mLoadingDialog = new LoadingDialog(mContext); + mLoadingDialog.show(); + } + + private void hideLoading() { + if (mLoadingDialog != null) { + mLoadingDialog.dismiss(); + mLoadingDialog = null; + } + } + + @Override + public boolean onSurfaceDestroyed(Surface surface) { + //清空释放 + setDisplay(null); + //同一消息队列中去release + //todo 需要处理为什么全屏时全屏的surface会被释放了 + //releaseSurface(surface); + return true; + } +} diff --git a/app/src/main/java/com/example/gsyvideoplayer/video/manager/CustomManager.java b/app/src/main/java/com/example/gsyvideoplayer/video/manager/CustomManager.java new file mode 100644 index 000000000..9fd102688 --- /dev/null +++ b/app/src/main/java/com/example/gsyvideoplayer/video/manager/CustomManager.java @@ -0,0 +1,188 @@ +package com.example.gsyvideoplayer.video.manager; + +import android.app.Activity; +import android.content.Context; +import android.text.TextUtils; +import android.view.View; +import android.view.ViewGroup; +import android.view.Window; + +import com.example.gsyvideoplayer.R; +import com.shuyu.gsyvideoplayer.GSYVideoBaseManager; +import com.shuyu.gsyvideoplayer.player.IPlayerManager; +import com.shuyu.gsyvideoplayer.player.IjkPlayerManager; +import com.shuyu.gsyvideoplayer.utils.CommonUtil; +import com.shuyu.gsyvideoplayer.video.base.GSYVideoPlayer; + +import java.util.HashMap; +import java.util.Map; + +import static com.shuyu.gsyvideoplayer.utils.CommonUtil.hideNavKey; + +/** + * 多个播放的管理器 + * Created by guoshuyu on 2018/1/31. + */ + +public class CustomManager extends GSYVideoBaseManager { + + public static final int SMALL_ID = R.id.custom_small_id; + + public static final int FULLSCREEN_ID = R.id.custom_full_id; + + public static String TAG = "GSYVideoManager"; + + private static Map sMap = new HashMap<>(); + + + public CustomManager() { + init(); + } + + @Override + protected IPlayerManager getPlayManager() { + return new IjkPlayerManager(); + } + + /** + * 退出全屏,主要用于返回键 + * + * @return 返回是否全屏 + */ + @SuppressWarnings("ResourceType") + public static boolean backFromWindowFull(Context context, String key) { + boolean backFrom = false; + ViewGroup vp = (ViewGroup) (CommonUtil.scanForActivity(context)).findViewById(Window.ID_ANDROID_CONTENT); + View oldF = vp.findViewById(FULLSCREEN_ID); + if (oldF != null) { + backFrom = true; + hideNavKey(context); + if (getCustomManager(key).lastListener() != null) { + getCustomManager(key).lastListener().onBackFullscreen(); + } + } + return backFrom; + } + + /** + * 页面销毁了记得调用是否所有的video + */ + public static void releaseAllVideos(String key) { + if (getCustomManager(key).listener() != null) { + getCustomManager(key).listener().onCompletion(); + } + getCustomManager(key).releaseMediaPlayer(); + } + + + /** + * 暂停播放 + */ + public void onPause(String key) { + if (getCustomManager(key).listener() != null) { + getCustomManager(key).listener().onVideoPause(); + } + } + + /** + * 恢复播放 + */ + public void onResume(String key) { + if (getCustomManager(key).listener() != null) { + getCustomManager(key).listener().onVideoResume(); + } + } + + + /** + * 恢复暂停状态 + * + * @param seek 是否产生seek动作,直播设置为false + */ + public void onResume(String key, boolean seek) { + if (getCustomManager(key).listener() != null) { + getCustomManager(key).listener().onVideoResume(seek); + } + } + + + /** + * 单例管理器 + */ + public static synchronized Map instance() { + return sMap; + } + + /** + * 单例管理器 + */ + public static synchronized CustomManager getCustomManager(String key) { + if (TextUtils.isEmpty(key)) { + throw new IllegalStateException("key not be empty"); + } + CustomManager customManager = sMap.get(key); + if (customManager == null) { + customManager = new CustomManager(); + sMap.put(key, customManager); + } + return customManager; + } + + public static void onPauseAll() { + if (sMap.size() > 0) { + for (Map.Entry header : sMap.entrySet()) { + header.getValue().onPause(header.getKey()); + } + } + } + + public static void onResumeAll() { + if (sMap.size() > 0) { + for (Map.Entry header : sMap.entrySet()) { + header.getValue().onResume(header.getKey()); + } + } + } + + /** + * 恢复暂停状态 + * + * @param seek 是否产生seek动作 + */ + public static void onResumeAll(boolean seek) { + if (sMap.size() > 0) { + for (Map.Entry header : sMap.entrySet()) { + header.getValue().onResume(header.getKey(), seek); + } + } + } + + public static void clearAllVideo() { + if (sMap.size() > 0) { + for (Map.Entry header : sMap.entrySet()) { + CustomManager.releaseAllVideos(header.getKey()); + } + } + sMap.clear(); + } + + public static void removeManager(String key) { + sMap.remove(key); + } + + /** + * 当前是否全屏状态 + * + * @return 当前是否全屏状态, true代表是。 + */ + @SuppressWarnings("ResourceType") + public static boolean isFullState(Activity activity) { + ViewGroup vp = (ViewGroup) (CommonUtil.scanForActivity(activity)).findViewById(Window.ID_ANDROID_CONTENT); + final View full = vp.findViewById(FULLSCREEN_ID); + GSYVideoPlayer gsyVideoPlayer = null; + if (full != null) { + gsyVideoPlayer = (GSYVideoPlayer) full; + } + return gsyVideoPlayer != null; + } +} diff --git a/app/src/main/java/com/example/gsyvideoplayer/view/CustomRenderView.java b/app/src/main/java/com/example/gsyvideoplayer/view/CustomRenderView.java new file mode 100644 index 000000000..394b87b57 --- /dev/null +++ b/app/src/main/java/com/example/gsyvideoplayer/view/CustomRenderView.java @@ -0,0 +1,23 @@ +package com.example.gsyvideoplayer.view; + +import android.content.Context; +import android.view.ViewGroup; + +import com.shuyu.gsyvideoplayer.render.GSYRenderView; +import com.shuyu.gsyvideoplayer.render.glrender.GSYVideoGLViewBaseRender; +import com.shuyu.gsyvideoplayer.render.view.GSYVideoGLView; +import com.shuyu.gsyvideoplayer.render.view.listener.IGSYSurfaceListener; +import com.shuyu.gsyvideoplayer.utils.MeasureHelper; + +/** + * 自定义代理渲染层 + * Created by guoshuyu on 2018/1/30. + */ + +public class CustomRenderView extends GSYRenderView { + + @Override + public void addView(Context context, ViewGroup textureViewContainer, int rotate, IGSYSurfaceListener gsySurfaceListener, MeasureHelper.MeasureFormVideoParamsListener videoParamsListener, GSYVideoGLView.ShaderInterface effect, float[] transform, GSYVideoGLViewBaseRender customRender, int mode) { + mShowView = CustomTextureSurface.addSurfaceView(context, textureViewContainer, rotate, gsySurfaceListener, videoParamsListener); + } +} diff --git a/app/src/main/java/com/example/gsyvideoplayer/view/CustomTextureSurface.java b/app/src/main/java/com/example/gsyvideoplayer/view/CustomTextureSurface.java new file mode 100644 index 000000000..14a632b42 --- /dev/null +++ b/app/src/main/java/com/example/gsyvideoplayer/view/CustomTextureSurface.java @@ -0,0 +1,229 @@ +package com.example.gsyvideoplayer.view; + +import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.Matrix; +import android.util.AttributeSet; +import android.view.SurfaceHolder; +import android.view.SurfaceView; +import android.view.View; +import android.view.ViewGroup; + +import com.shuyu.gsyvideoplayer.listener.GSYVideoShotListener; +import com.shuyu.gsyvideoplayer.listener.GSYVideoShotSaveListener; +import com.shuyu.gsyvideoplayer.render.GSYRenderView; +import com.shuyu.gsyvideoplayer.render.glrender.GSYVideoGLViewBaseRender; +import com.shuyu.gsyvideoplayer.render.view.GSYVideoGLView; +import com.shuyu.gsyvideoplayer.render.view.IGSYRenderView; +import com.shuyu.gsyvideoplayer.render.view.listener.IGSYSurfaceListener; +import com.shuyu.gsyvideoplayer.utils.MeasureHelper; + +import java.io.File; + +/** + * 自定义渲染层 + * Created by guoshuyu on 2018/1/30. + */ + +public class CustomTextureSurface extends SurfaceView implements IGSYRenderView, SurfaceHolder.Callback2, MeasureHelper.MeasureFormVideoParamsListener { + + private IGSYSurfaceListener mIGSYSurfaceListener; + + private MeasureHelper measureHelper; + + private MeasureHelper.MeasureFormVideoParamsListener mVideoParamsListener; + + public CustomTextureSurface(Context context) { + super(context); + init(); + } + + public CustomTextureSurface(Context context, AttributeSet attrs) { + super(context, attrs); + init(); + } + + public CustomTextureSurface(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + init(); + } + + private void init() { + measureHelper = new MeasureHelper(this, this); + } + + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + measureHelper.prepareMeasure(widthMeasureSpec, heightMeasureSpec, (int) getRotation()); + setMeasuredDimension(measureHelper.getMeasuredWidth(), measureHelper.getMeasuredHeight()); + } + + @Override + public void surfaceCreated(SurfaceHolder holder) { + if (mIGSYSurfaceListener != null) { + mIGSYSurfaceListener.onSurfaceAvailable(holder.getSurface()); + } + } + + @Override + public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { + if (mIGSYSurfaceListener != null) { + mIGSYSurfaceListener.onSurfaceSizeChanged(holder.getSurface(), width, height); + } + } + + @Override + public void surfaceDestroyed(SurfaceHolder holder) { + //清空释放 + if (mIGSYSurfaceListener != null) { + mIGSYSurfaceListener.onSurfaceDestroyed(holder.getSurface()); + } + } + + @Override + public void surfaceRedrawNeeded(SurfaceHolder holder) { + + } + + @Override + public IGSYSurfaceListener getIGSYSurfaceListener() { + return mIGSYSurfaceListener; + } + + @Override + public void setIGSYSurfaceListener(IGSYSurfaceListener surfaceListener) { + getHolder().addCallback(this); + this.mIGSYSurfaceListener = surfaceListener; + } + + + @Override + public int getSizeH() { + return measureHelper.getMeasuredHeight(); + } + + @Override + public int getSizeW() { + return measureHelper.getMeasuredWidth(); + } + + @Override + public void taskShotPic(GSYVideoShotListener gsyVideoShotListener, boolean shotHigh) { + + } + + @Override + public void saveFrame(File file, boolean high, GSYVideoShotSaveListener gsyVideoShotSaveListener) { + + } + + @Override + public View getRenderView() { + return this; + } + + @Override + public Bitmap initCover() { + return null; + } + + @Override + public Bitmap initCoverHigh() { + return null; + } + + @Override + public void onRenderResume() { + + } + + @Override + public void onRenderPause() { + + } + + @Override + public void releaseRenderAll() { + + } + + @Override + public void setRenderMode(int mode) { + + } + + @Override + public void setRenderTransform(Matrix transform) { + + } + + @Override + public void setGLRenderer(GSYVideoGLViewBaseRender renderer) { + + } + + @Override + public void setGLMVPMatrix(float[] MVPMatrix) { + + } + + @Override + public void setGLEffectFilter(GSYVideoGLView.ShaderInterface effectFilter) { + + } + + @Override + public void setVideoParamsListener(MeasureHelper.MeasureFormVideoParamsListener listener) { + mVideoParamsListener = listener; + } + + @Override + public int getCurrentVideoWidth() { + if (mVideoParamsListener != null) { + return mVideoParamsListener.getCurrentVideoWidth(); + } + return 0; + } + + @Override + public int getCurrentVideoHeight() { + if (mVideoParamsListener != null) { + return mVideoParamsListener.getCurrentVideoHeight(); + } + return 0; + } + + @Override + public int getVideoSarNum() { + if (mVideoParamsListener != null) { + return mVideoParamsListener.getVideoSarNum(); + } + return 0; + } + + @Override + public int getVideoSarDen() { + if (mVideoParamsListener != null) { + return mVideoParamsListener.getVideoSarDen(); + } + return 0; + } + + /** + * 添加播放的view + */ + public static CustomTextureSurface addSurfaceView(Context context, ViewGroup textureViewContainer, int rotate, + final IGSYSurfaceListener gsySurfaceListener, + final MeasureHelper.MeasureFormVideoParamsListener videoParamsListener) { + if (textureViewContainer.getChildCount() > 0) { + textureViewContainer.removeAllViews(); + } + CustomTextureSurface showSurfaceView = new CustomTextureSurface(context); + showSurfaceView.setIGSYSurfaceListener(gsySurfaceListener); + showSurfaceView.setRotation(rotate); + showSurfaceView.setVideoParamsListener(videoParamsListener); + GSYRenderView.addToParent(textureViewContainer, showSurfaceView); + return showSurfaceView; + } +} diff --git a/app/src/main/java/com/example/gsyvideoplayer/view/FloatPlayerView.java b/app/src/main/java/com/example/gsyvideoplayer/view/FloatPlayerView.java new file mode 100644 index 000000000..6a36e5b8a --- /dev/null +++ b/app/src/main/java/com/example/gsyvideoplayer/view/FloatPlayerView.java @@ -0,0 +1,69 @@ +package com.example.gsyvideoplayer.view; + +import android.content.Context; +import android.util.AttributeSet; +import android.view.Gravity; +import android.view.ViewGroup; +import android.widget.FrameLayout; + +import com.example.gsyvideoplayer.video.FloatingVideo; +import com.shuyu.gsyvideoplayer.GSYVideoManager; + +/** + * 适配了悬浮窗的view + * Created by guoshuyu on 2017/12/25. + */ + +public class FloatPlayerView extends FrameLayout { + + FloatingVideo videoPlayer; + + public FloatPlayerView(Context context) { + super(context); + init(); + } + + public FloatPlayerView(Context context, AttributeSet attrs) { + super(context, attrs); + init(); + } + + public FloatPlayerView(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + init(); + } + + private void init() { + + videoPlayer = new FloatingVideo(getContext()); + + LayoutParams layoutParams = new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT); + layoutParams.gravity = Gravity.CENTER; + + addView(videoPlayer, layoutParams); + + String source1 = "http://9890.vod.myqcloud.com/9890_4e292f9a3dd011e6b4078980237cc3d3.f20.mp4"; + + videoPlayer.setUp(source1, true, "测试视频"); + + //增加封面 + /*ImageView imageView = new ImageView(getContext()); + imageView.setScaleType(ImageView.ScaleType.CENTER_CROP); + imageView.setImageResource(R.mipmap.xxx1); + videoPlayer.setThumbImageView(imageView);*/ + + //是否可以滑动调整 + videoPlayer.setIsTouchWiget(false); + + } + + + public void onPause() { + videoPlayer.getCurrentPlayer().onVideoPause(); + } + + public void onResume() { + videoPlayer.getCurrentPlayer().onVideoResume(); + } + +} diff --git a/app/src/main/java/com/example/gsyvideoplayer/view/LoadingDialog.java b/app/src/main/java/com/example/gsyvideoplayer/view/LoadingDialog.java new file mode 100644 index 000000000..166a5e6d7 --- /dev/null +++ b/app/src/main/java/com/example/gsyvideoplayer/view/LoadingDialog.java @@ -0,0 +1,44 @@ +package com.example.gsyvideoplayer.view; + +import android.app.Dialog; +import android.content.Context; +import android.content.DialogInterface; +import android.os.Bundle; +import android.text.InputFilter; +import android.text.TextUtils; +import android.util.DisplayMetrics; +import android.view.LayoutInflater; +import android.view.View; +import android.view.Window; +import android.view.WindowManager; +import android.view.inputmethod.InputMethodManager; +import android.widget.CheckBox; +import android.widget.EditText; +import android.widget.TextView; + +import com.example.gsyvideoplayer.R; + +public class LoadingDialog extends Dialog { + + private Context context; + + public LoadingDialog(Context context) { + super(context, R.style.dialog_style); + this.context = context; + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + init(); + } + + public void init() { + LayoutInflater inflater = LayoutInflater.from(context); + View view = inflater.inflate(R.layout.layout_loading_dialog, null); + setContentView(view); + + setCanceledOnTouchOutside(false); + setCancelable(false); + } +} \ No newline at end of file diff --git a/app/src/main/res/drawable-xhdpi/lololo.png b/app/src/main/res/drawable-xhdpi/lololo.png new file mode 100644 index 000000000..214bff407 Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/lololo.png differ diff --git a/app/src/main/res/layout/activity_danmaku_layout.xml b/app/src/main/res/layout/activity_danmaku_layout.xml index 436255045..aa5a217fb 100644 --- a/app/src/main/res/layout/activity_danmaku_layout.xml +++ b/app/src/main/res/layout/activity_danmaku_layout.xml @@ -4,7 +4,7 @@ android:layout_width="match_parent" android:layout_height="match_parent"> - - + + + + + + + + + + + + + + + + + + + + +