diff --git a/.gitignore b/.gitignore
index f6b286c..4bec01d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,40 +1,9 @@
-# Built application files
-*.apk
-*.ap_
-
-# Files for the ART/Dalvik VM
-*.dex
-
-# Java class files
-*.class
-
-# Generated files
-bin/
-gen/
-out/
-
-# Gradle files
-.gradle/
-build/
-
-# Local configuration file (sdk path, etc)
-local.properties
-
-# Proguard folder generated by Eclipse
-proguard/
-
-# Log Files
-*.log
-
-# Android Studio Navigation editor temp files
-.navigation/
-
-# Android Studio captures folder
-captures/
-
-# Intellij
*.iml
-.idea/workspace.xml
-
-# Keystore files
-*.jks
+.gradle
+/local.properties
+/.idea/workspace.xml
+/.idea
+.DS_Store
+/build
+/captures
+.externalNativeBuild
diff --git a/ISSUE_TEMPLATE.zh-CN.md b/ISSUE_TEMPLATE.zh-CN.md
new file mode 100644
index 0000000..1a52d83
--- /dev/null
+++ b/ISSUE_TEMPLATE.zh-CN.md
@@ -0,0 +1,13 @@
+## 该问题是怎么引起的?
+
+
+
+## 重现步骤
+
+
+
+## 报错信息
+
+
+
+
diff --git a/LICENSE b/LICENSE
index 8dada3e..4621858 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,201 +1,191 @@
- 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}
+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:
+
+You must give any other recipients of the Work or Derivative Works a copy of
+this License; and
+You must cause any modified files to carry prominent notices stating that You
+changed the files; and
+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
+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 2017 蔡小路
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
+ 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.
+ limitations under the License.
\ No newline at end of file
diff --git a/PULL_REQUEST_TEMPLATE.zh-CN.md b/PULL_REQUEST_TEMPLATE.zh-CN.md
new file mode 100644
index 0000000..636c20f
--- /dev/null
+++ b/PULL_REQUEST_TEMPLATE.zh-CN.md
@@ -0,0 +1,15 @@
+## 该Pull Request关联的Issue
+
+
+## 修改描述
+
+
+
+## 测试用例
+
+
+
+## 修复效果的截屏
+
+
+
diff --git a/README.md b/README.md
index 8d02846..3d1fd69 100644
--- a/README.md
+++ b/README.md
@@ -1,2 +1,68 @@
-# QSVideoPlayer
-安卓视频播放器AndroidVideoplayer,ui参考jc播放器,架构重新设计,支持多种解码
+#QSVideoPlayer
+ * UI设计基于jc播放器,可快速自定义ui打造自己的播放器,不用写一行播放逻辑
+ * 可扩展设计,解码模块目前提供了 AndroidMedia(系统自带)、ijkMedia(基于ffmepg)+ijkExoMedia(基于exo)、ExoMedia(2.0.4)解码器
+ * 根据系统版本选择SurfaceView和TextureView,支持api9+
+ * 支持本地视频,在线视频,m3u8直播等...
+
+根据需求自行选择需要的解码器,不需要的解码器在build.gradle注释掉/删掉jar,关联错误的类删掉即可,不会影响其他
+1.一般简单播放视频AndroidMedia足够(体积最小,无依赖)
+2.有点需求可选择AndroidMedia+ExoMedia(1MB)
+3.需求高的可选AndroidMedia+(ijkMedia+ijkExoMedia)(打完包6MB+)
+目前测试解码效果ijkMedia最快最好,AndroidMedia个别视频有半途中断BUG,exo无明显缺陷
+
+#使用说明
+下载项目 添加qsvideoplayre文件夹为自己的项目依赖即可
+diy播放器:
+可直接修改QSVideoView改造自己的播放器 或 继承BaseVideoView参考QSVideoView
+只需子类提供layout布局以及设置各个状态的ui即可完成自己的播放器,播放逻辑一行不用写
+```
+QSVideoView qsVideoView = (QSVideoView) findViewById(R.id.xxx);
+qsVideoView.setUp(url, "这是一一一一一一一一一个标题");
+qsVideoView.play();//
+```
+
+#其他API
+
+```
+qsVideoView.stop();//暂停
+qsVideoView.seekTo(100000);//跳进度
+qsVideoView.release();//销毁
+
+qsVideoView.getCoverImageView().setImageResource(R.mipmap.cover);//封面
+
+//设置监听
+qsVideoView.setPlayListener(new PlayListener() {
+ @Override
+ public void onStatus(int status) {//播放器的ui状态
+ if (status == IVideoPlayer.STATE_AUTO_COMPLETE)
+ qsVideoView.quitWindowFullscreen();//播放完成退出全屏
+ }
+
+ @Override//全屏/普通...
+ public void onMode(int mode) {
+
+ }
+
+ @Override//播放事件 初始化完成/缓冲/出错/...
+ public void onEvent(int what, Integer... extra) {
+
+ }
+
+ });
+
+qsVideoView.enterFullMode=1;//进入全屏的模式 0默认是横屏 1是竖屏,随传感器自动切换横屏
+
+
+```
+#返回键退出全屏
+```
+ @Override
+ public void onBackPressed() {
+ if (qsVideoView.onBackPressed())
+ return;
+ super.onBackPressed();
+ }
+```
+
+![输入图片说明](http://git.oschina.net/uploads/images/2017/0301/151443_9778d0d4_530535.jpeg "在这里输入图片标题")
+![输入图片说明](http://git.oschina.net/uploads/images/2017/0224/180438_84c8332c_530535.jpeg "在这里输入图片标题")
\ No newline at end of file
diff --git a/app/.gitignore b/app/.gitignore
new file mode 100644
index 0000000..796b96d
--- /dev/null
+++ b/app/.gitignore
@@ -0,0 +1 @@
+/build
diff --git a/app/build.gradle b/app/build.gradle
new file mode 100644
index 0000000..0f6af62
--- /dev/null
+++ b/app/build.gradle
@@ -0,0 +1,44 @@
+apply plugin: 'com.android.application'
+
+android {
+ compileSdkVersion 24
+ buildToolsVersion "25.0.2"
+ defaultConfig {
+ applicationId "org.song.demo"
+ minSdkVersion 15
+ targetSdkVersion 24
+ versionCode 1
+ versionName "1.0"
+ testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
+ }
+ sourceSets {
+ main() {
+ manifest.srcFile 'src/main/AndroidManifest.xml'
+ java.srcDirs = ['src/main/java']
+ resources.srcDirs = ['src/main/resources']
+ res.srcDirs = ['src/main/res']
+ assets.srcDirs = ['src/main/assets']
+ jniLibs.srcDirs = ['libs']
+ jni.srcDirs = ['src/main/jni']
+
+ }
+ }
+ buildTypes {
+ release {
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+ }
+ }
+}
+
+dependencies {
+ compile fileTree(dir: 'libs', include: ['*.jar'])
+ androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
+ exclude group: 'com.android.support', module: 'support-annotations'
+ })
+ compile 'com.android.support:appcompat-v7:24.2.1'
+ testCompile 'junit:junit:4.12'
+
+ compile project(':qsvideoplayer')
+
+}
diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro
new file mode 100644
index 0000000..6037025
--- /dev/null
+++ b/app/proguard-rules.pro
@@ -0,0 +1,17 @@
+# Add project specific ProGuard rules here.
+# By default, the flags in this file are appended to flags specified
+# in C:\Develop\Android SDK/tools/proguard/proguard-android.txt
+# You can edit the include path and order by changing the proguardFiles
+# directive in build.gradle.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# Add any project specific keep options here:
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+# public *;
+#}
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..4d3a710
--- /dev/null
+++ b/app/src/main/AndroidManifest.xml
@@ -0,0 +1,36 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/assets/tv.txt b/app/src/main/assets/tv.txt
new file mode 100644
index 0000000..767bea8
--- /dev/null
+++ b/app/src/main/assets/tv.txt
@@ -0,0 +1,63 @@
+CCTV1ۺ,http://58.135.196.138:8090/live/db3bd108e3364bf3888ccaf8377af077/index.m3u8
+CCTV2ƾ,http://58.135.196.138:8090/live/e31fa63612644555a545781ea32e66d4/index.m3u8
+CCTV3,http://58.135.196.138:8090/live/A68CE6833D654a9e932A657689463088/index.m3u8
+CCTV4Ĺ(),http://58.135.196.138:8090/live/56383AB184D54ac8B20478B6A43906DC/index.m3u8
+cctv5,http://58.135.196.138:8090/live/6b9e3889ec6e2ab1a8c7bd0845e5368a/index.m3u8
+CCTV6Ӱ,http://58.135.196.138:8090/live/6132e9cb136050bd94822db31d1401af/index.m3u8
+CCTV7ũҵ,http://58.135.196.138:8090/live/a9a97bed07eca008a1c88c9d7c74965d/index.m3u8
+CCTV8Ӿ,http://58.135.196.138:8090/live/6e38d69416f160bad4657788d6c06c01/index.m3u8
+CCTV9¼,http://58.135.196.138:8090/live/557a950d2cfcf2ee1aad5a260893c2b8/index.m3u8
+CCTV10ƽ,http://58.135.196.138:8090/live/a0effe8f31af0011b750e817c1b6e8c7/index.m3u8
+CCTV11Ϸ,http://58.135.196.138:8090/live/2D5B4B7AB5A14d79A0D9A1D37540E2BF/index.m3u8
+CCTV12뷨,http://58.135.196.138:8090/live/3720126fbea745f74c1c89df9797caac/index.m3u8
+CCTV13,http://58.135.196.138:8090/live/5e02ac2a0829f7ab10d6543fdd211d8e/index.m3u8
+CCTV14ٶ,http://58.135.196.138:8090/live/4A5DF0BA0C994081B02F8215491C4E48/index.m3u8
+CCTV15,http://58.135.196.138:8090/live/ADBD55B50F2D47bb970DCCBAF458E6C8/index.m3u8
+֮,http://58.135.196.138:8090/live/1AB9A2A4F4CE48b397D72A067906D763/index.m3u8
+,http://58.135.196.138:8090/live/D6C05AA9AFEF4ac58A6ED0FFF27EE85A/index.m3u8
+һ糡,http://58.135.196.138:8090/live/13BCA1A2EFDB4db3B5DF6A8D343C3E39/index.m3u8
+ɾ糡,http://58.135.196.138:8090/live/65A216E9B6FC4905AF6756E2AEFC3ED3/index.m3u8
+ƾ糡,http://58.135.196.138:8090/live/8D574FF6D0B04a0eA8B51D55B7C8E6DD/index.m3u8
+,http://58.135.196.138:8090/live/789E9CE292A040d2A459CF55D2FB362E/789E9CE292A040d2A459CF55D2FB362E.m3u8
+Ѷ,http://58.135.196.138:8090/live/369024B4C9BF4b0f909187FC58B9951C/369024B4C9BF4b0f909187FC58B9951C.m3u8
+,http://58.135.196.138:8090/live/F56C438F495049168C447030C175F7DB/F56C438F495049168C447030C175F7DB.m3u8
+ƽ,http://58.135.196.138:8090/live/1E8AF3D32194426dAC087FF20B098BCC/1E8AF3D32194426dAC087FF20B098BCC.m3u8
+Ӱ,http://58.135.196.138:8090/live/8a6870a0febf6283b27210aab721beb4/8a6870a0febf6283b27210aab721beb4.m3u8
+ƾ,http://58.135.196.138:8090/live/8C213826A5E049849097FE77230B0616/8C213826A5E049849097FE77230B0616.m3u8
+,http://58.135.196.138:8090/live/c6e9431fbe2634418def86d4b193d80d/c6e9431fbe2634418def86d4b193d80d.m3u8
+,http://58.135.196.138:8090/live/dd728b0bef32c98060a0d4f757b6dbaf/dd728b0bef32c98060a0d4f757b6dbaf.m3u8
+,http://58.135.196.138:8090/live/87446EA70C0C42119656A11938ACB2DD/87446EA70C0C42119656A11938ACB2DD.m3u8
+,http://58.135.196.138:8090/live/C42481BFB2FD456480A098436834DFF0/C42481BFB2FD456480A098436834DFF0.m3u8
+ٶ,http://58.135.196.138:8090/live/543E9B8D048648e399E66EA338EF761C/543E9B8D048648e399E66EA338EF761C.m3u8
+DOXTV,http://58.135.196.138:8090/live/7FDF7FBEC2B8474dA349102809CB8F97/7FDF7FBEC2B8474dA349102809CB8F97.m3u863
+Ƶ,http://58.135.196.138:8090/live/022ED5AEC04546d99958328E2E6BB614/022ED5AEC04546d99958328E2E6BB614.m3u8
+,http://58.135.196.138:8090/live/A3A45E102C5F41afBD78A5E27FF0578F/A3A45E102C5F41afBD78A5E27FF0578F.m3u8
+Ƹ,http://58.135.196.138:8090/live/2615D9C21AB248678ED9CF172E463539/2615D9C21AB248678ED9CF172E463539.m3u8
+ƾ,http://58.135.196.138:8090/live/295A4BAF84504aba8B1ED913A9790E08/295A4BAF84504aba8B1ED913A9790E08.m3u8
+㳡,http://58.135.196.138:8090/live/E1F31105D6094bb6ADB5E698CB6EE4D8/E1F31105D6094bb6ADB5E698CB6EE4D8.m3u8
+Ƶ,http://58.135.196.138:8090/live/6E6AAB7CE02545e0868A71B4A8DA2559/6E6AAB7CE02545e0868A71B4A8DA2559.m3u8
+,http://58.135.196.138:8090/live/4818B652431344a8882ED935471711B5/4818B652431344a8882ED935471711B5.m3u8
+߶,http://58.135.196.138:8090/live/ED54356140BD4282AA0C6FE656CC6583/ED54356140BD4282AA0C6FE656CC6583.m3u8
+Ц糡,http://58.135.196.138:8090/live/B081DB925E434fa8A8A2812860B86785/B081DB925E434fa8A8A2812860B86785.m3u8
+,http://58.135.196.138:8090/live/B3EDABA59F6240889F4C5162AF4655D3/B3EDABA59F6240889F4C5162AF4655D3.m3u8
+,http://58.135.196.138:8090/live/333BAD8D3C2E4bfeA648053B7286A0BF/333BAD8D3C2E4bfeA648053B7286A0BF.m3u8
+,http://58.135.196.138:8090/live/77C9E60036F64711B8C5F5111BF5A7F1/77C9E60036F64711B8C5F5111BF5A7F1.m3u8
+ͥ,http://58.135.196.138:8090/live/D70719FBFE2242c6BA2B6235C834D896/D70719FBFE2242c6BA2B6235C834D896.m3u8
+ӥͨ,http://58.135.196.138:8090/live/345A6019C2734ec7BE05BD4C6837EFD3/345A6019C2734ec7BE05BD4C6837EFD3.m3u8
+,http://58.135.196.138:8090/live/5B33459CF0314e3e831351B3ED17F844/5B33459CF0314e3e831351B3ED17F844.m3u8
+,http://58.135.196.138:8090/live/AA4E088E6AB54779A3A4ED856016317E/AA4E088E6AB54779A3A4ED856016317E.m3u8
+ֳ,http://58.135.196.138:8090/live/95589A5033F948749F9ABD897E2C0032/95589A5033F948749F9ABD897E2C0032.m3u8
+ŷ,http://58.135.196.138:8090/live/980D3B2CF76042269F81A12B635ED9E9/980D3B2CF76042269F81A12B635ED9E9.m3u8
+ȫʵ,http://58.135.196.138:8090/live/94C1846FCB3E48278D2B6BA49164EC33/94C1846FCB3E48278D2B6BA49164EC33.m3u8
+ʱ,http://58.135.196.138:8090/live/379B386B5A214f8fA2B9F4DF1946BF6D/379B386B5A214f8fA2B9F4DF1946BF6D.m3u8
+ʱ,http://58.135.196.138:8090/live/373331B772D1418eAB2D7EEA0C7B3691/373331B772D1418eAB2D7EEA0C7B3691.m3u8
+ʱʳ,http://58.135.196.138:8090/live/07A1BF033E7940898A01053F79EF3595/07A1BF033E7940898A01053F79EF3595.m3u8
+ղ,http://58.135.196.138:8090/live/91ADF5DB36DE4c0f8E56FC3386836EA3/91ADF5DB36DE4c0f8E56FC3386836EA3.m3u8
+ĺ,http://58.135.196.138:8090/live/6E1087643B3D445fBCECAF3B54544767/6E1087643B3D445fBCECAF3B54544767.m3u8
+ȷ¼,http://58.135.196.138:8090/live/394BAD414F284fbf9EA8F08106741A63/394BAD414F284fbf9EA8F08106741A63.m3u8
+¶,http://58.135.196.138:8090/live/3CDBE7F96BC1456cAAD86C571D5B1C43/3CDBE7F96BC1456cAAD86C571D5B1C43.m3u8
+,http://58.135.196.138:8090/live/883091E42C394ec3996932F152B95FD3/883091E42C394ec3996932F152B95FD3.m3u8
+̳,http://58.135.196.138:8090/live/62E9275D131549b080F9ABBDB5E48F4E/62E9275D131549b080F9ABBDB5E48F4E.m3u8
+ű,http://58.135.196.138:8090/live/FF453B3E5B4F446e978A7624254E3CD2/FF453B3E5B4F446e978A7624254E3CD2.m3u8
+Ϸ,http://58.135.196.138:8090/live/82769FB7307D460193359CA465D1C159/82769FB7307D460193359CA465D1C159.m3u8
+Ӿ糡,http://58.135.196.138:8090/live/369B8B9031894010B5BA822A1637FEF1/369B8B9031894010B5BA822A1637FEF1.m3u8
\ No newline at end of file
diff --git a/app/src/main/java/org/song/demo/MainActivity.java b/app/src/main/java/org/song/demo/MainActivity.java
new file mode 100644
index 0000000..addd396
--- /dev/null
+++ b/app/src/main/java/org/song/demo/MainActivity.java
@@ -0,0 +1,158 @@
+package org.song.demo;
+
+import android.os.Bundle;
+import android.os.Handler;
+import android.support.v7.app.AppCompatActivity;
+import android.view.View;
+import android.view.WindowManager;
+
+import org.song.videoplayer.IVideoPlayer;
+import org.song.videoplayer.PlayListener;
+import org.song.videoplayer.QSVideoView;
+
+public class MainActivity extends AppCompatActivity {
+
+ QSVideoView qsVideoView;
+
+ String mp4 = "http://sz-kpie-videos.oss-cn-shenzhen.aliyuncs.com/videos/20161110/11.mp4";
+
+ String m3u8 = "http://live.hkstv.hk.lxdns.com/live/hks/playlist.m3u8";
+
+ String url;
+ int media;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
+ setContentView(R.layout.activity_main);
+ qsVideoView = (QSVideoView) findViewById(R.id.qs);
+ qsVideoView.getCoverImageView().setImageResource(R.mipmap.cover);
+ qsVideoView.setPlayListener(new PlayListener() {
+ @Override
+ public void onStatus(int status) {//播放状态
+ if (status == IVideoPlayer.STATE_AUTO_COMPLETE)
+ qsVideoView.quitWindowFullscreen();//播放完成退出全屏
+ }
+
+ @Override//全屏/普通
+ public void onMode(int mode) {
+
+ }
+
+ @Override
+ public void onEvent(int what, Integer... extra) {
+
+ }
+
+ });
+ play(mp4, 0);
+ }
+
+
+ private void play(String url, int media) {
+ qsVideoView.release();
+ qsVideoView.setiMediaControl(media);
+ qsVideoView.setUp(url, "这是一一一一一一一一一个标题");
+ //qsVideoView.seekTo(12300);
+ qsVideoView.play();
+ this.url = url;
+ this.media = media;
+
+ //qsVideoView.enterFullMode =1;
+ }
+
+
+ public void 系统硬解(View v) {
+ play(url, 0);
+ setTitle("系统硬解");
+
+ }
+
+ public void ijk_ffmepg解码(View v) {
+ play(url, 1);
+ setTitle("ijk_ffmepg解码");
+
+ }
+
+ public void exo解码(View v) {
+ play(url, 2);
+ setTitle("exo解码");
+
+ }
+
+ public void ijk_exo解码(View v) {
+ play(url, 3);
+ setTitle("ijk_exo解码");
+
+ }
+
+ public void 网络视频(View v) {
+ play(mp4, media);
+
+ }
+
+ public void m3u8直播(View v) {
+ play(m3u8, media);
+ }
+
+ public void 销毁(View v) {
+ qsVideoView.release();
+ }
+
+
+ @Override
+ public void onBackPressed() {
+ if (qsVideoView.onBackPressed())
+ return;
+ super.onBackPressed();
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ if (flag)
+ qsVideoView.play();
+ handler.removeCallbacks(runnable);
+ if (position > 0) {
+ qsVideoView.seekTo(position);
+ position = 0;
+ }
+
+ }
+
+ boolean flag;//记录退出时播放状态 回来的时候继续播放
+ int position;//记录销毁时的进度 回来继续盖进度播放
+
+ @Override
+ public void onPause() {
+ super.onPause();//暂停
+ flag = qsVideoView.isPlaying();
+ qsVideoView.pause();
+ }
+
+
+ @Override
+ public void onStop() {
+ super.onStop();//不马上销毁 延时10秒
+ handler.postDelayed(runnable, 1000 * 10);
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();//销毁
+ qsVideoView.release();
+ }
+
+
+ Handler handler = new Handler();
+ Runnable runnable = new Runnable() {
+ @Override
+ public void run() {
+ if (qsVideoView.getCurrentState() != IVideoPlayer.STATE_AUTO_COMPLETE)
+ position = qsVideoView.getPosition();
+ qsVideoView.release();
+ }
+ };
+
+}
diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml
new file mode 100644
index 0000000..21d05bb
--- /dev/null
+++ b/app/src/main/res/layout/activity_main.xml
@@ -0,0 +1,90 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher.png b/app/src/main/res/mipmap-hdpi/ic_launcher.png
new file mode 100644
index 0000000..cde69bc
Binary files /dev/null and b/app/src/main/res/mipmap-hdpi/ic_launcher.png differ
diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher.png b/app/src/main/res/mipmap-mdpi/ic_launcher.png
new file mode 100644
index 0000000..c133a0c
Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/ic_launcher.png differ
diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/app/src/main/res/mipmap-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..bfa42f0
Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ
diff --git a/app/src/main/res/mipmap-xxhdpi/cover.jpg b/app/src/main/res/mipmap-xxhdpi/cover.jpg
new file mode 100644
index 0000000..4f7e757
Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/cover.jpg differ
diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100644
index 0000000..324e72c
Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ
diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100644
index 0000000..aee44e1
Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ
diff --git a/app/src/main/res/values-w820dp/dimens.xml b/app/src/main/res/values-w820dp/dimens.xml
new file mode 100644
index 0000000..63fc816
--- /dev/null
+++ b/app/src/main/res/values-w820dp/dimens.xml
@@ -0,0 +1,6 @@
+
+
+ 64dp
+
diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml
new file mode 100644
index 0000000..3ab3e9c
--- /dev/null
+++ b/app/src/main/res/values/colors.xml
@@ -0,0 +1,6 @@
+
+
+ #3F51B5
+ #303F9F
+ #FF4081
+
diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml
new file mode 100644
index 0000000..47c8224
--- /dev/null
+++ b/app/src/main/res/values/dimens.xml
@@ -0,0 +1,5 @@
+
+
+ 16dp
+ 16dp
+
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
new file mode 100644
index 0000000..cab1817
--- /dev/null
+++ b/app/src/main/res/values/strings.xml
@@ -0,0 +1,3 @@
+
+ QSVideoPayer
+
diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml
new file mode 100644
index 0000000..9513160
--- /dev/null
+++ b/app/src/main/res/values/styles.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
diff --git a/build.gradle b/build.gradle
new file mode 100644
index 0000000..366be34
--- /dev/null
+++ b/build.gradle
@@ -0,0 +1,24 @@
+// Top-level build file where you can add configuration options common to all sub-projects/modules.
+
+buildscript {
+ repositories {
+ jcenter()
+ }
+ dependencies {
+ classpath 'com.android.tools.build:gradle:2.2.0'
+
+ // NOTE: Do not place your application dependencies here; they belong
+ // in the individual module build.gradle files
+ }
+
+}
+
+allprojects {
+ repositories {
+ jcenter()
+ }
+}
+
+task clean(type: Delete) {
+ delete rootProject.buildDir
+}
diff --git a/gradle.properties b/gradle.properties
new file mode 100644
index 0000000..aac7c9b
--- /dev/null
+++ b/gradle.properties
@@ -0,0 +1,17 @@
+# Project-wide Gradle settings.
+
+# IDE (e.g. Android Studio) users:
+# Gradle settings configured through the IDE *will override*
+# any settings specified in this file.
+
+# For more details on how to configure your build environment visit
+# http://www.gradle.org/docs/current/userguide/build_environment.html
+
+# Specifies the JVM arguments used for the daemon process.
+# The setting is particularly useful for tweaking memory settings.
+org.gradle.jvmargs=-Xmx1536m
+
+# When configured, Gradle will run in incubating parallel mode.
+# This option should only be used with decoupled projects. More details, visit
+# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
+# org.gradle.parallel=true
diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000..13372ae
Binary files /dev/null and b/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..6c3fbad
--- /dev/null
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Tue Sep 20 09:44:51 CST 2016
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.14.1-all.zip
diff --git a/gradlew b/gradlew
new file mode 100644
index 0000000..9d82f78
--- /dev/null
+++ b/gradlew
@@ -0,0 +1,160 @@
+#!/usr/bin/env bash
+
+##############################################################################
+##
+## Gradle start up script for UN*X
+##
+##############################################################################
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS=""
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn ( ) {
+ echo "$*"
+}
+
+die ( ) {
+ echo
+ echo "$*"
+ echo
+ exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+case "`uname`" in
+ CYGWIN* )
+ cygwin=true
+ ;;
+ Darwin* )
+ darwin=true
+ ;;
+ MINGW* )
+ msys=true
+ ;;
+esac
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG=`dirname "$PRG"`"/$link"
+ fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >/dev/null
+APP_HOME="`pwd -P`"
+cd "$SAVED" >/dev/null
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD="java"
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
+ MAX_FD_LIMIT=`ulimit -H -n`
+ if [ $? -eq 0 ] ; then
+ if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+ MAX_FD="$MAX_FD_LIMIT"
+ fi
+ ulimit -n $MAX_FD
+ if [ $? -ne 0 ] ; then
+ warn "Could not set maximum file descriptor limit: $MAX_FD"
+ fi
+ else
+ warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+ fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+ GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin ; then
+ APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+ CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+ JAVACMD=`cygpath --unix "$JAVACMD"`
+
+ # We build the pattern for arguments to be converted via cygpath
+ ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+ SEP=""
+ for dir in $ROOTDIRSRAW ; do
+ ROOTDIRS="$ROOTDIRS$SEP$dir"
+ SEP="|"
+ done
+ OURCYGPATTERN="(^($ROOTDIRS))"
+ # Add a user-defined pattern to the cygpath arguments
+ if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+ OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+ fi
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ i=0
+ for arg in "$@" ; do
+ CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+ CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
+
+ if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
+ eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+ else
+ eval `echo args$i`="\"$arg\""
+ fi
+ i=$((i+1))
+ done
+ case $i in
+ (0) set -- ;;
+ (1) set -- "$args0" ;;
+ (2) set -- "$args0" "$args1" ;;
+ (3) set -- "$args0" "$args1" "$args2" ;;
+ (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+ (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+ (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+ (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+ (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+ (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+ esac
+fi
+
+# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
+function splitJvmOpts() {
+ JVM_OPTS=("$@")
+}
+eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
+JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
+
+exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
diff --git a/gradlew.bat b/gradlew.bat
new file mode 100644
index 0000000..8a0b282
--- /dev/null
+++ b/gradlew.bat
@@ -0,0 +1,90 @@
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS=
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto init
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto init
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:init
+@rem Get command-line arguments, handling Windowz variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+if "%@eval[2+2]" == "4" goto 4NT_args
+
+:win9xME_args
+@rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+set _SKIP=2
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+set CMD_LINE_ARGS=%*
+goto execute
+
+:4NT_args
+@rem Get arguments from the 4NT Shell from JP Software
+set CMD_LINE_ARGS=%$
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/qsvideoplayer/.gitignore b/qsvideoplayer/.gitignore
new file mode 100644
index 0000000..796b96d
--- /dev/null
+++ b/qsvideoplayer/.gitignore
@@ -0,0 +1 @@
+/build
diff --git a/qsvideoplayer/build.gradle b/qsvideoplayer/build.gradle
new file mode 100644
index 0000000..09ddfac
--- /dev/null
+++ b/qsvideoplayer/build.gradle
@@ -0,0 +1,56 @@
+apply plugin: 'com.android.library'
+
+android {
+ compileSdkVersion 22
+ buildToolsVersion '23.0.3'
+
+ defaultConfig {
+ minSdkVersion 9
+ targetSdkVersion 22
+ versionCode 1
+ versionName "1.0"
+ }
+ sourceSets {
+ main() {
+ manifest.srcFile 'src/main/AndroidManifest.xml'
+ java.srcDirs = ['src/main/java']
+ resources.srcDirs = ['src/main/resources']
+ res.srcDirs = ['src/main/res']
+ assets.srcDirs = ['src/main/assets']
+ jniLibs.srcDirs = ['libs']
+ jni.srcDirs = ['src/main/jni']
+
+ }
+ }
+ buildTypes {
+ release {
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+ }
+ }
+ lintOptions {
+ abortOnError false
+ }
+}
+
+dependencies {
+ compile fileTree(dir: 'libs', include: ['*.jar'])
+ testCompile 'junit:junit:4.12'
+
+
+ compile 'tv.danmaku.ijk.media:ijkplayer-java:0.7.7.1'
+ compile 'tv.danmaku.ijk.media:ijkplayer-armv7a:0.7.7.1'
+ //Other ABIs: optional
+ //compile 'tv.danmaku.ijk.media:ijkplayer-armv5:0.7.7.1'
+ compile 'tv.danmaku.ijk.media:ijkplayer-arm64:0.7.7.1'
+ compile 'tv.danmaku.ijk.media:ijkplayer-x86:0.7.7.1'
+ compile 'tv.danmaku.ijk.media:ijkplayer-x86_64:0.7.7.1'
+ //ExoPlayer as IMediaPlayer: optional, experimental
+ compile 'tv.danmaku.ijk.media:ijkplayer-exo:0.7.7.1'
+
+
+ //这个以jar方式依赖了 不支持两个版本同时依赖
+ //compile 'com.google.android.exoplayer:exoplayer:r2.0.4'
+
+}
+//apply from: '../gradle/maven_push.gradle'
diff --git a/qsvideoplayer/libs/exo-2.0.4.jar b/qsvideoplayer/libs/exo-2.0.4.jar
new file mode 100644
index 0000000..4636f9c
Binary files /dev/null and b/qsvideoplayer/libs/exo-2.0.4.jar differ
diff --git a/qsvideoplayer/proguard-rules.pro b/qsvideoplayer/proguard-rules.pro
new file mode 100644
index 0000000..19da422
--- /dev/null
+++ b/qsvideoplayer/proguard-rules.pro
@@ -0,0 +1,17 @@
+# Add project specific ProGuard rules here.
+# By default, the flags in this file are appended to flags specified
+# in /Users/Nathen/WorkEnv/android-sdk-macosx/tools/proguard/proguard-android.txt
+# You can edit the include path and order by changing the proguardFiles
+# directive in build.gradle.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# Add any project specific keep options here:
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+# public *;
+#}
diff --git a/qsvideoplayer/src/main/AndroidManifest.xml b/qsvideoplayer/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..779ab04
--- /dev/null
+++ b/qsvideoplayer/src/main/AndroidManifest.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/qsvideoplayer/src/main/java/org/song/videoplayer/BaseVideoView.java b/qsvideoplayer/src/main/java/org/song/videoplayer/BaseVideoView.java
new file mode 100644
index 0000000..02647dd
--- /dev/null
+++ b/qsvideoplayer/src/main/java/org/song/videoplayer/BaseVideoView.java
@@ -0,0 +1,749 @@
+package org.song.videoplayer;
+
+import android.content.Context;
+import android.media.AudioManager;
+import android.os.Handler;
+import android.os.Looper;
+import android.provider.Settings;
+import android.text.TextUtils;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.Gravity;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.WindowManager;
+import android.widget.FrameLayout;
+import android.widget.ImageView;
+import android.widget.SeekBar;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import org.song.videoplayer.media.IMediaCallback;
+import org.song.videoplayer.media.IMediaControl;
+import org.song.videoplayer.rederview.IRenderView;
+import org.song.videoplayer.rederview.SufaceRenderView;
+
+/**
+ * Created by song on 2017/2/13.
+ * 和UI有关操作子类实现(除了设置进度 时间
+ * UI界面由子类决定
+ */
+public abstract class BaseVideoView extends FrameLayout implements IVideoPlayer, IMediaCallback, View.OnClickListener, SeekBar.OnSeekBarChangeListener, HandleTouchEvent.GestureEvent {
+
+ public static final String TAG = "QSVideoPlayer";
+ public boolean isWindowGesture = false;//是否非全屏下也可以手势调节进度
+ public int enterFullMode = 0;//进入全屏的模式 0横屏 1传感器自动横竖屏
+
+ final int progressMax = 1000;
+
+ protected int currentState = STATE_NORMAL;
+ protected int currentMode = MODE_WINDOW_NORMAL;
+
+ protected String url;
+ //必须提供
+ protected ImageView startButton;//播放按钮
+ protected SeekBar progressBar;//进度条
+ protected TextView currentTimeTextView, totalTimeTextView;//播放时间
+ protected ViewGroup renderViewContainer;//suface容器
+ protected ViewGroup bottomContainer;//底部 拿来判断是不是显示控制view用
+ //子类可不提供
+ protected ImageView startButton2;//播放按钮2
+ //protected SeekBar progressBar2;//进度条2
+ protected ImageView fullscreenButton;//全屏按钮
+ protected ViewGroup topContainer;//顶部
+ protected ViewGroup bufferingContainer;//缓冲
+ protected ViewGroup loadingContainer;//初始化
+ protected ViewGroup errorContainer;//出错了显示的 重试
+
+ protected Handler mHandler;
+ protected PlayListener playListener;
+ private HandleTouchEvent handleTouchEvent;
+
+ private View videoView;
+ //protected boolean controlViewIsShow;//控制view是否显示
+
+
+ public BaseVideoView(Context context) {
+ this(context, null);
+ }
+
+ public BaseVideoView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ init(context);
+ }
+
+ protected void init(Context context) {
+ iMediaControl = ConfigManage.getInstance(getContext()).getIMediaControl(this);
+ videoView = View.inflate(context, getLayoutId(), null);
+ addView(videoView, new LayoutParams(-1, -1));
+ //子类必须提供的控件 id已确定
+ startButton = (ImageView) findViewById(R.id.start);
+ progressBar = (SeekBar) findViewById(R.id.progress);
+ currentTimeTextView = (TextView) findViewById(R.id.current);
+ totalTimeTextView = (TextView) findViewById(R.id.total);
+ bottomContainer = (ViewGroup) findViewById(R.id.layout_bottom);
+ renderViewContainer = (ViewGroup) findViewById(R.id.surface_container);
+ //可不提供
+ fullscreenButton = (ImageView) findViewById(R.id.fullscreen);
+ topContainer = (ViewGroup) findViewById(R.id.layout_top);
+ bufferingContainer = (ViewGroup) findViewById(R.id.buffering_container);
+ loadingContainer = (ViewGroup) findViewById(R.id.loading_container);
+ errorContainer = (ViewGroup) findViewById(R.id.error_container);
+ startButton2 = (ImageView) findViewById(R.id.start2);
+
+ if (fullscreenButton != null)
+ fullscreenButton.setOnClickListener(this);
+ if (startButton2 != null)
+ startButton2.setOnClickListener(this);
+ startButton.setOnClickListener(this);
+ progressBar.setOnSeekBarChangeListener(this);
+ progressBar.setMax(progressMax);
+ bottomContainer.setOnClickListener(this);
+ videoView.setOnClickListener(this);
+ videoView.setOnTouchListener(new OnTouchListener() {
+ @Override
+ public boolean onTouch(View v, MotionEvent event) {
+ return handleTouchEvent.handleEvent(event, BaseVideoView.this);
+ }
+ });
+
+ mHandler = new Handler(Looper.getMainLooper());
+ handleTouchEvent = new HandleTouchEvent(this);
+ audioManager = (AudioManager) getContext().getSystemService(Context.AUDIO_SERVICE);
+
+
+ }
+
+ //-----------给外部调用的start----------
+ @Override
+ public void setUp(String url, Object... objects) {
+ this.url = url;
+ release();
+ setUIWithStateAndMode(STATE_NORMAL, currentMode);
+ }
+
+ @Override
+ public void play() {
+ if (currentState != STATE_PLAYING)
+ clickPlay();
+ }
+
+ @Override
+ public void seekTo(int duration) {
+ if (checkReady()) {
+ if (duration >= 0) {
+ seek(duration);
+ }
+ } else
+ seekToInAdvance = duration;
+ }
+
+ @Override
+ public void pause() {
+ if (currentState == STATE_PLAYING)
+ clickPlay();
+ }
+
+ @Override
+ public void release() {
+ iMediaControl.release();
+ removeRenderView();
+ setUIWithStateAndMode(STATE_NORMAL, currentMode);
+ intiParams();
+ }
+
+ public void setPlayListener(PlayListener playListener) {
+ this.playListener = playListener;
+ }
+
+ @Override
+ public boolean onBackPressed() {
+ if (currentMode != MODE_WINDOW_NORMAL) {
+ quitWindowFullscreen();
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public boolean isPlaying() {
+ return iMediaControl.isPlaying();
+ }
+
+
+ //-----------给外部调用的end----------
+
+ //视频长度/进度
+ protected int duration = -1, position;
+
+ public int getPosition() {
+ return position;
+ }
+
+ public int getDuration() {
+ return duration;
+ }
+
+ //初始化一些变量
+ protected void intiParams() {
+ duration = -1;
+ position = 0;
+ }
+
+ private IRenderView iRenderView;
+
+ //-----------各种流程逻辑-----------------
+ //一开始点击准备播放--初始化
+ protected void prepareMediaPlayer() {
+ Log.d(TAG, "prepareMediaPlayer [" + this.hashCode() + "] ");
+ removeRenderView();
+ iMediaControl.doPrepar(getContext(), url, null);
+ addRenderView();
+ setUIWithStateAndMode(STATE_PREPARING, currentMode);
+ }
+
+ protected void addRenderView() {
+ iRenderView = ConfigManage.getInstance(getContext()).getIRenderView(getContext());
+ iRenderView.addRenderCallback(new IRenderView.IRenderCallback() {
+ @Override
+ public void onSurfaceCreated(IRenderView holder, int width, int height) {
+ holder.bindMedia(iMediaControl);
+ }
+
+ @Override
+ public void onSurfaceChanged(IRenderView holder, int format, int width, int height) {
+
+ }
+
+ @Override
+ public void onSurfaceDestroyed(IRenderView holder) {
+ if (holder instanceof SufaceRenderView) {
+ iMediaControl.setDisplay(null);
+ }
+ }
+ });
+ LayoutParams layoutParams =
+ new LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT,
+ ViewGroup.LayoutParams.MATCH_PARENT,
+ Gravity.CENTER);
+ renderViewContainer.addView(iRenderView.get(), layoutParams);
+ }
+
+ protected void removeRenderView() {
+ renderViewContainer.removeAllViews();
+ if (iRenderView != null) {
+ iRenderView.removeRenderCallback();
+ iRenderView = null;
+ }
+ }
+
+ //根据播放状态设置
+ protected void setUIWithStateAndMode(final int status, final int mode) {
+ Log.e("setUIWithStateAndMode", "status:" + status + " mode:" + mode);
+
+ final int temp_status = this.currentState;
+ final int temp_mode = this.currentMode;
+ switch (status) {
+ case STATE_NORMAL:
+ case STATE_PREPARING:
+ cancelProgressTimer();
+ resetProgressAndTime();
+ break;
+ case STATE_PLAYING:
+ case STATE_PAUSE:
+ startProgressTimer();
+ break;
+ case STATE_ERROR:
+ cancelProgressTimer();
+ break;
+ case STATE_AUTO_COMPLETE:
+ cancelProgressTimer();
+ setCompleProgressAndTime();
+ break;
+ }
+ if (status == STATE_PLAYING)
+ Util.KEEP_SCREEN_ON(getContext());
+ else
+ Util.KEEP_SCREEN_OFF(getContext());
+
+ cancelDismissControlViewTimer();
+ changeUiWithStateAndMode(status, mode);
+
+ this.currentState = status;
+ this.currentMode = mode;
+
+ if (playListener != null) {
+ if (temp_status != status)
+ playListener.onStatus(status);
+ if (temp_mode != mode)
+ playListener.onMode(mode);
+ }
+ }
+
+ private long tempLong;
+ private boolean full_flag;//标记状态栏状态
+
+ //全屏
+ public void enterWindowFullscreen() {
+ if (currentMode != MODE_WINDOW_FULLSCREEN & checkEnterOrFullOK()) {
+ full_flag = Util.SET_FULL(getContext());
+ if (enterFullMode == 1)
+ Util.SET_SENSOR(getContext());
+ else
+ Util.SET_LANDSCAPE(getContext());
+
+ ViewGroup vp = (ViewGroup) videoView.getParent();
+ if (vp != null)
+ vp.removeView(videoView);
+ ViewGroup decorView = (ViewGroup) (Util.scanForActivity(getContext())).getWindow().getDecorView();
+ //.findViewById(Window.ID_ANDROID_CONTENT);
+ decorView.addView(videoView, new LayoutParams(-1, -1));
+ setUIWithStateAndMode(currentState, MODE_WINDOW_FULLSCREEN);
+ }
+ }
+
+ //退出全屏
+ public void quitWindowFullscreen() {
+ if (currentMode != MODE_WINDOW_NORMAL & checkEnterOrFullOK()) {
+ if (!full_flag)
+ Util.CLEAR_FULL(getContext());
+ Util.SET_PORTRAIT(getContext());
+
+ ViewGroup vp = (ViewGroup) videoView.getParent();
+ if (vp != null)
+ vp.removeView(videoView);
+ addView(videoView, new LayoutParams(-1, -1));
+ setUIWithStateAndMode(currentState, MODE_WINDOW_NORMAL);
+ }
+ }
+
+ //防止频繁切换全屏
+ private boolean checkEnterOrFullOK() {
+ long now = System.currentTimeMillis();
+ long d = now - tempLong;
+ if (d > 1000)
+ tempLong = now;
+ return d > 1000;
+ }
+
+ protected void setProgressAndText() {
+ int position = iMediaControl.getCurrentPosition();
+ int duration = iMediaControl.getDuration();
+ int progress = position * progressMax / (duration <= 0 ? 1 : duration);
+ if (progress < 0)
+ progress = 0;
+ progressBar.setProgress(progress);
+ currentTimeTextView.setText(Util.stringForTime(position));
+ if (duration > 1)
+ totalTimeTextView.setText(Util.stringForTime(duration));
+ else {
+ totalTimeTextView.setText("直播");
+ duration = -1;
+ }
+ this.position = position;
+ this.duration = duration;
+ }
+
+ protected void setBufferProgress(int bufferProgress) {
+ progressBar.setSecondaryProgress(bufferProgress);
+ }
+
+ protected void resetProgressAndTime() {
+ progressBar.setProgress(0);
+ progressBar.setSecondaryProgress(0);
+ currentTimeTextView.setText(Util.stringForTime(0));
+ totalTimeTextView.setText(Util.stringForTime(0));
+ }
+
+ protected void setCompleProgressAndTime() {
+ position = duration;
+ progressBar.setProgress(progressMax);
+ currentTimeTextView.setText(totalTimeTextView.getText());
+ }
+
+ //-----------各种流程逻辑end-----------------
+
+ private IMediaControl iMediaControl;
+
+ public void setiMediaControl(int i) {
+ this.iMediaControl = ConfigManage.getInstance(getContext()).getIMediaControl(this, i);
+ }
+
+ private int seekToInAdvance;
+
+ //-----------解码器回调start-----------------
+ @Override
+ public void onPrepared(IMediaControl iMediaControl) {
+ Log.e("MediaCallBack", "onPrepared");
+ setProgressAndText();
+ if (seekToInAdvance > 0) {
+ if (duration > 0)
+ iMediaControl.seekTo(seekToInAdvance);
+ seekToInAdvance = 0;
+ }
+ iMediaControl.doPlay();
+ setUIWithStateAndMode(STATE_PLAYING, currentMode);
+ //prepared(iMediaControl);
+ if (playListener != null)
+ playListener.onEvent(EVENT_PREPARED, 0);
+ }
+
+ //public abstract void prepared(IMediaControl iMediaControl);
+
+ @Override
+ public void onCompletion(IMediaControl iMediaControl) {
+ Log.e("MediaCallBack", "onCompletion");
+ setUIWithStateAndMode(STATE_AUTO_COMPLETE, currentMode);
+ if (playListener != null)
+ playListener.onEvent(EVENT_COMPLETION);
+ }
+
+ @Override
+ public void onSeekComplete(IMediaControl iMediaControl) {
+ Log.e("MediaCallBack", "onSeekComplete");
+
+ }
+
+
+ @Override
+ public void onInfo(IMediaControl iMediaControl, int what, int extra) {
+ Log.e("MediaCallBack", "onInfo" + " what" + what + " extra" + extra);
+ if (what == IMediaControl.MEDIA_INFO_BUFFERING_START) {
+ onBuffering(true);
+ if (playListener != null)
+ playListener.onEvent(EVENT_BUFFERING_START);
+ }
+
+ if (what == IMediaControl.MEDIA_INFO_BUFFERING_END) {
+ onBuffering(false);
+ if (playListener != null)
+ playListener.onEvent(EVENT_BUFFERING_END);
+ }
+ }
+
+ protected abstract void onBuffering(boolean isBuffering);
+
+ @Override
+ public void onVideoSizeChanged(IMediaControl iMediaControl, int width, int height) {
+ Log.e("MediaCallBack", "onVideoSizeChanged" + " width:" + width + " height:" + height);
+ iRenderView.setVideoSize(width, height);
+ if (playListener != null)
+ playListener.onEvent(EVENT_VIDEOSIZECHANGE, width, height);
+ }
+
+ @Override
+ public void onError(IMediaControl iMediaControl, int what, int extra) {
+ Log.e("MediaCallBack", "onError" + "what:" + what + " extra:" + extra);
+ //if (what == 38 | extra == -38 | extra == -19)
+ // return;
+ Toast.makeText(getContext(), "error: " + what + "," + extra, Toast.LENGTH_SHORT).show();
+ iMediaControl.release();
+ seekToInAdvance = position;//记录错误时进度
+ setUIWithStateAndMode(STATE_ERROR, currentMode);
+ if (playListener != null)
+ playListener.onEvent(EVENT_ERROR, what, extra);
+ }
+
+ @Override
+ public void onBufferingUpdate(IMediaControl iMediaControl, float percent) {
+ Log.e("MediaCallBack", "onBufferingUpdate" + percent);
+ setBufferProgress((int) (percent * progressMax));
+ if (playListener != null)
+ playListener.onEvent(EVENT_BUFFERING_UPDATA, (int) (percent * 100));
+ }
+ //-----------解码器回调end-----------------
+
+ //-----------各种UI监听start-----------------
+ @Override
+ public void onClick(View v) {
+ int i = v.getId();
+ if (i == R.id.start || i == R.id.start2) {
+ clickPlay();
+ }
+ if (i == R.id.fullscreen) {
+ clickFull();
+ }
+ if (v == videoView) {
+ if (currentState == STATE_NORMAL || currentState == STATE_ERROR)
+ clickPlay();
+ else {
+ if (bottomContainer.getVisibility() == VISIBLE)
+ dismissControlView();
+ else
+ setUIWithStateAndMode(currentState, currentMode);
+ }
+ }
+ }
+
+ //点击时根据不同状态做出不同的反应
+ protected void clickPlay() {
+ if (TextUtils.isEmpty(url)) {
+ Toast.makeText(getContext(), getResources().getString(R.string.no_url), Toast.LENGTH_SHORT).show();
+ return;
+ }
+ if (currentState == STATE_NORMAL) {
+ if (!url.startsWith("file") && !Util.isWifiConnected(getContext())) {
+ if (showWifiDialog())
+ return;
+ }
+ prepareMediaPlayer();
+ } else if (currentState == STATE_PLAYING) {
+ iMediaControl.doPause();
+ setUIWithStateAndMode(STATE_PAUSE, currentMode);
+ } else if (currentState == STATE_PAUSE) {
+ iMediaControl.doPlay();
+ setUIWithStateAndMode(STATE_PLAYING, currentMode);
+ } else if (currentState == STATE_AUTO_COMPLETE || currentState == STATE_ERROR) {
+ prepareMediaPlayer();
+ }
+ }
+
+ protected void clickFull() {
+ if (currentMode == MODE_WINDOW_FULLSCREEN) {
+ quitWindowFullscreen();
+ } else {
+ enterWindowFullscreen();
+ }
+ }
+
+ @Override
+ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
+ int time = seekBar.getProgress() * iMediaControl.getDuration() / progressMax;
+ currentTimeTextView.setText(Util.stringForTime(time));
+ //Log.i(TAG, "onProgressChanged " + Util.stringForTime(time));
+
+ }
+
+ @Override
+ public void onStartTrackingTouch(SeekBar seekBar) {
+ cancelProgressTimer();
+ cancelDismissControlViewTimer();
+ }
+
+ @Override
+ public void onStopTrackingTouch(SeekBar seekBar) {
+ int time = seekBar.getProgress() * iMediaControl.getDuration() / progressMax;
+ seek(time);
+ startProgressTimer();
+ if (currentState == STATE_PLAYING)
+ startDismissControlViewTimer(1314);
+ }
+
+ protected void seek(int time) {
+ if (currentState == STATE_PLAYING ||
+ currentState == STATE_PAUSE)
+ if (duration > 0)
+ iMediaControl.seekTo(time);
+ if (currentState == STATE_AUTO_COMPLETE) {
+ //seekToInAdvance = time;//播放完成 拖动进度条重新播放
+ //prepareMediaPlayer();
+ if (duration > 0)
+ iMediaControl.seekTo(time);
+ iMediaControl.doPlay();
+ setUIWithStateAndMode(STATE_PLAYING, currentMode);
+ }
+ }
+ //-----------各种UI监听end-----------------
+
+
+ //-----------定时任务更新进度start-----------------
+ protected void startProgressTimer() {
+ cancelProgressTimer();
+ mHandler.postDelayed(updateProgress, 500);
+ }
+
+ protected void cancelProgressTimer() {
+ mHandler.removeCallbacks(updateProgress);
+ }
+
+ private Runnable updateProgress = new Runnable() {
+ @Override
+ public void run() {
+ mHandler.postDelayed(updateProgress, 500);
+ setProgressAndText();
+ }
+ };
+ //-----------定时任务更新进度end-----------------
+
+
+ //-----------定时任务隐藏控制栏end-----------------
+
+ protected void startDismissControlViewTimer() {
+ startDismissControlViewTimer(2500);
+ }
+
+ protected void startDismissControlViewTimer(int delayed) {
+ cancelDismissControlViewTimer();
+ mHandler.postDelayed(dismissControlViewTimerRunnable, delayed);
+ }
+
+ protected void cancelDismissControlViewTimer() {
+ mHandler.removeCallbacks(dismissControlViewTimerRunnable);
+ }
+
+ private Runnable dismissControlViewTimerRunnable = new Runnable() {
+ @Override
+ public void run() {
+ dismissControlView();
+ }
+ };
+
+ protected abstract void dismissControlView();
+
+ //-----------定时任务隐藏控制栏end-----------------
+
+
+ //-----------手势调节弹窗start-----------
+ @Override
+ public void onGestureBegin(int type) {
+ if (!isWindowGesture & currentMode != MODE_WINDOW_FULLSCREEN)
+ return;
+
+ //进度
+ if (type == HandleTouchEvent.GestureEvent.TOUCH_FULL_X & checkReady())
+ tempPosition = position;
+ //亮度
+ if (type == HandleTouchEvent.GestureEvent.TOUCH_LEFT_Y) {
+ tempBrightness = (int) (Util.scanForActivity(getContext()).getWindow().getAttributes().screenBrightness * 255);
+ if (tempBrightness < 0)
+ try {//系统亮度 不能activity取
+ tempBrightness = Settings.System.getInt(getContext().getContentResolver(), Settings.System.SCREEN_BRIGHTNESS);
+ } catch (Settings.SettingNotFoundException e) {
+ e.printStackTrace();
+ tempBrightness = 0;
+ }
+ }
+ //音量
+ if (type == HandleTouchEvent.GestureEvent.TOUCH_RIGHT_Y)
+ tempVolume = audioManager.getStreamVolume(AudioManager.STREAM_MUSIC);
+
+ }
+
+ //调节前的值 亮度退出全屏应原样
+ private int tempPosition;
+ private int tempBrightness;
+ private int tempVolume;
+ protected AudioManager audioManager;
+
+ @Override
+ public void onGestureChange(int type, float level) {
+ if (!isWindowGesture & currentMode != MODE_WINDOW_FULLSCREEN)
+ return;
+ //进度
+ if (type == HandleTouchEvent.GestureEvent.TOUCH_FULL_X & checkReady()) {
+ if (duration <= 0)
+ return;
+ int delta = (int) (level * duration);
+ if (delta < -tempPosition)
+ delta = -tempPosition;
+ if (delta > duration - tempPosition)
+ delta = duration - tempPosition;
+ showProgressDialog(delta, tempPosition, duration);
+ }
+ //亮度
+ if (type == HandleTouchEvent.GestureEvent.TOUCH_LEFT_Y) {
+ WindowManager.LayoutParams params = Util.scanForActivity(getContext()).getWindow().getAttributes();
+ int delta = (int) (level * 255);
+ int nowBrightness = tempBrightness + delta;
+ if (nowBrightness < 0)
+ nowBrightness = 0;
+ if (nowBrightness > 255)
+ nowBrightness = 255;
+ float b = nowBrightness / 255.0f;
+ if (showBrightnessDialog((int) (b * 100), 100)) {
+ params.screenBrightness = b;
+ Util.scanForActivity(getContext()).getWindow().setAttributes(params);
+ }
+ }
+ //音量
+ if (type == HandleTouchEvent.GestureEvent.TOUCH_RIGHT_Y) {
+ int max = audioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
+ int deltaV = (int) (max * level);
+ int nowVolume = tempVolume + deltaV;
+ if (nowVolume < 0)
+ nowVolume = 0;
+ if (nowVolume > max)
+ nowVolume = max;
+ if (showVolumeDialog(nowVolume, max))
+ audioManager.setStreamVolume(AudioManager.STREAM_MUSIC, nowVolume, 0);
+ }
+ }
+
+ @Override
+ public void onGestureEnd(int type, float level) {
+ //双击
+ if (type == HandleTouchEvent.GestureEvent.TOUCH_DOUBLE_C)
+ clickFull();
+
+ if (!isWindowGesture & currentMode != MODE_WINDOW_FULLSCREEN)
+ return;
+ //进度
+ if (type == HandleTouchEvent.GestureEvent.TOUCH_FULL_X & checkReady()) {
+ if (duration <= 0)
+ return;
+ if (!dismissProgressDialog()) return;
+
+ int delta = (int) (level * duration);
+ tempPosition += delta;
+ if (tempPosition > duration)
+ tempPosition = duration;
+ if (tempPosition < 0)
+ tempPosition = 0;
+ seekTo(tempPosition);
+ tempPosition = 0;
+
+ }
+ //亮度
+ if (type == HandleTouchEvent.GestureEvent.TOUCH_LEFT_Y) {
+ dismissBrightnessDialog();
+ }
+ //音量
+ if (type == HandleTouchEvent.GestureEvent.TOUCH_RIGHT_Y) {
+ dismissVolumeDialog();
+ }
+
+
+ }
+
+ //子类写了实现 就返回true
+ protected abstract boolean showWifiDialog();//要弹出非wifi提示框覆盖return true即可
+
+ protected abstract boolean showProgressDialog(int delay, int position, int duration);
+
+ protected abstract boolean dismissProgressDialog();
+
+ protected abstract boolean showVolumeDialog(int nowVolume, int maxVolume);
+
+ protected abstract boolean dismissVolumeDialog();
+
+ protected abstract boolean showBrightnessDialog(int nowBrightness, int maxBrightness);
+
+ protected abstract boolean dismissBrightnessDialog();
+ //-----------各种调节弹窗end-----------
+
+
+ /**
+ * 提供的布局至少需要包含6个控件 见44行
+ * id见ids.xml
+ */
+ protected abstract int getLayoutId();
+
+ protected abstract void changeUiWithStateAndMode(int status, int mode);
+
+ protected boolean checkReady() {
+ return currentState != STATE_NORMAL
+ & currentState != STATE_PREPARING
+ & currentState != STATE_ERROR;
+
+ }
+
+ public int getCurrentMode() {
+ return currentMode;
+ }
+
+ public int getCurrentState() {
+ return currentState;
+ }
+}
diff --git a/qsvideoplayer/src/main/java/org/song/videoplayer/ConfigManage.java b/qsvideoplayer/src/main/java/org/song/videoplayer/ConfigManage.java
new file mode 100644
index 0000000..11af09a
--- /dev/null
+++ b/qsvideoplayer/src/main/java/org/song/videoplayer/ConfigManage.java
@@ -0,0 +1,73 @@
+package org.song.videoplayer;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.os.Build;
+
+import org.song.videoplayer.media.AndroidMedia;
+import org.song.videoplayer.media.ExoMedia;
+import org.song.videoplayer.media.IjkExoMedia;
+import org.song.videoplayer.media.IjkMedia;
+import org.song.videoplayer.media.IMediaCallback;
+import org.song.videoplayer.media.IMediaControl;
+import org.song.videoplayer.rederview.IRenderView;
+import org.song.videoplayer.rederview.SufaceRenderView;
+import org.song.videoplayer.rederview.TextureRenderView;
+
+/**
+ * Created by song on 2017/2/10.
+ */
+
+public class ConfigManage {
+
+ private static ConfigManage instance;
+
+ public static ConfigManage getInstance(Context context) {
+ if (instance == null)
+ instance = new ConfigManage(context);
+ return instance;
+ }
+
+ private SharedPreferences preferences;
+ private int media_mode = 0;
+
+ private ConfigManage(Context context) {
+ preferences = context.getSharedPreferences("cfg_qsvideo",
+ Context.MODE_PRIVATE);
+ media_mode = preferences.getInt("media_mode", 0);
+ }
+
+
+ public IRenderView getIRenderView(Context context) {
+ if (Build.VERSION.SDK_INT >= 14)
+ return new TextureRenderView(context);
+ else
+ return new SufaceRenderView(context);
+ }
+
+
+ //后期扩展其他解码器 exo ijk... exo api需大于16
+ public IMediaControl getIMediaControl(IMediaCallback iMediaCallback, int MEDIA_MODE) {
+ if (MEDIA_MODE == 1)
+ return new IjkMedia(iMediaCallback);
+ if (MEDIA_MODE == 2 & Build.VERSION.SDK_INT >= 16)
+ return new ExoMedia(iMediaCallback);
+ if (MEDIA_MODE == 3 & Build.VERSION.SDK_INT >= 16)
+ return new IjkExoMedia(iMediaCallback);
+ return new AndroidMedia(iMediaCallback);
+ }
+
+ //后期扩展其他解码器 exo ijk... exo api需大于16
+ public IMediaControl getIMediaControl(IMediaCallback iMediaCallback) {
+ return getIMediaControl(iMediaCallback, media_mode);
+ }
+
+ public void setMedia_mode(int media_mode) {
+ this.media_mode = media_mode;
+ preferences.edit().putInt("media_mode", media_mode).commit();
+ }
+
+ public int getMedia_mode() {
+ return media_mode;
+ }
+}
diff --git a/qsvideoplayer/src/main/java/org/song/videoplayer/HandleTouchEvent.java b/qsvideoplayer/src/main/java/org/song/videoplayer/HandleTouchEvent.java
new file mode 100644
index 0000000..be5a2f0
--- /dev/null
+++ b/qsvideoplayer/src/main/java/org/song/videoplayer/HandleTouchEvent.java
@@ -0,0 +1,135 @@
+package org.song.videoplayer;
+
+import android.util.Log;
+import android.view.MotionEvent;
+import android.view.View;
+
+/**
+ * Created by song on 2017/2/14.22:32
+ * 处理触摸监听 响应各种手势
+ */
+
+public class HandleTouchEvent {
+
+ private GestureEvent gestureEvent;
+
+ HandleTouchEvent(GestureEvent touchEvent) {
+ this.gestureEvent = touchEvent;
+ }
+
+ private final int moveLen = 66;
+ private final float bili = 0.75f;//调节进度灵敏度 滑动0.75屏幕可以到底
+
+ private float downX, downY;
+ private int w, h;
+ private int leftX, rightX;
+ private int type = -1;
+ float level = 0;
+
+ long tempTime;
+
+ public boolean handleEvent(MotionEvent event, View view) {
+
+ float x = event.getX();
+ float y = event.getY();
+ switch (event.getAction()) {
+ case MotionEvent.ACTION_DOWN:
+ init();
+ downX = x;
+ downY = y;
+ w = view.getWidth();
+ h = view.getHeight();
+ leftX = (int) (w * 0.25);
+ rightX = (int) (w * 0.75);
+ //Log.e("onTouchEvent", "ACTION_DOWN");
+ break;
+ case MotionEvent.ACTION_MOVE:
+ float deltaX = x - downX;
+ float deltaY = y - downY;
+
+ //判断触发
+ if (type < 0) {
+ float absDeltaX = Math.abs(deltaX);
+ float absDeltaY = Math.abs(deltaY);
+ if (absDeltaX > moveLen) {
+ type = GestureEvent.TOUCH_FULL_X;
+ downX = x;
+ gestureEvent.onGestureBegin(type);
+ }
+ if (absDeltaY > moveLen && downX <= leftX) {
+ type = GestureEvent.TOUCH_LEFT_Y;
+ downY = y;
+ gestureEvent.onGestureBegin(type);
+ }
+ if (absDeltaY > moveLen && downX > rightX) {
+ type = GestureEvent.TOUCH_RIGHT_Y;
+ downY = y;
+ gestureEvent.onGestureBegin(type);
+ }
+ }
+
+ switch (type) {
+ case GestureEvent.TOUCH_FULL_X:
+ level = 1.0f * deltaX / (w * bili);
+ break;
+ case GestureEvent.TOUCH_LEFT_Y:
+ level = 1.0f * -deltaY / (h * bili);
+ break;
+ case GestureEvent.TOUCH_RIGHT_Y:
+ level = 1.0f * -deltaY / (h * bili);
+ break;
+
+ }
+
+ if (type > 0) {
+ if (level < -1)
+ level = -1;
+ if (level > 1)
+ level = 1;
+ gestureEvent.onGestureChange(type, level);
+ }
+ //Log.e("onTouchEvent", "ACTION_MOVE");
+ break;
+ case MotionEvent.ACTION_UP:
+ case MotionEvent.ACTION_CANCEL:
+
+ long l = System.currentTimeMillis();
+ long delay = l - tempTime;
+ tempTime = l;
+ if (delay < 300)
+ type = GestureEvent.TOUCH_DOUBLE_C;
+
+ if (type > 0)
+ view.post(new Runnable() {
+ @Override
+ public void run() {
+ gestureEvent.onGestureEnd(type, level);
+ }
+ });
+ }
+ return type > 0 & type != GestureEvent.TOUCH_DOUBLE_C;//双击不拦截事件
+ }
+
+ private void init() {
+ downX = downY = 0;
+ leftX = rightX = 0;
+ h = w = 0;
+ type = -1;
+ level = 0;
+ }
+
+ public interface GestureEvent {
+
+ int TOUCH_FULL_X = 1;//全屏左右滑动
+ int TOUCH_LEFT_Y = 2;//左边上下滑动
+ int TOUCH_RIGHT_Y = 3;//右边上下滑动
+ int TOUCH_DOUBLE_C = 4;//双击
+ //...
+
+ void onGestureBegin(int type);//触发手势事件
+
+ void onGestureChange(int type, float level);//改变level: -1 ~ 1
+
+ void onGestureEnd(int type, float level);//手势事件结束
+ }
+}
diff --git a/qsvideoplayer/src/main/java/org/song/videoplayer/IVideoPlayer.java b/qsvideoplayer/src/main/java/org/song/videoplayer/IVideoPlayer.java
new file mode 100644
index 0000000..da21c0d
--- /dev/null
+++ b/qsvideoplayer/src/main/java/org/song/videoplayer/IVideoPlayer.java
@@ -0,0 +1,54 @@
+package org.song.videoplayer;
+
+/**
+ * Created by song on 2017/2/10.
+ * 视频播放器
+ */
+
+public interface IVideoPlayer {
+
+ int STATE_NORMAL = 0;//未播放
+ int STATE_PREPARING = 1;//初始化中
+ int STATE_PLAYING = 2;//播放中
+
+ //去掉这个状态 缓冲只是显示个进度 不影响其他状态
+ //int STATE_PLAYING_BUFFERING_START = 3;//缓冲
+
+ int STATE_PAUSE = 5;//暂停中
+ int STATE_AUTO_COMPLETE = 6;//播放完成
+ int STATE_ERROR = 7;//播放出错
+
+ int MODE_WINDOW_NORMAL = 100;//普通模式
+ int MODE_WINDOW_FULLSCREEN = 101;//全屏模式
+ int MODE_WINDOW_TINY = 102;//小窗口模式
+
+ int EVENT_BUFFERING_START = 10;//缓冲
+ int EVENT_BUFFERING_END = 11;//缓冲结束
+ int EVENT_PREPARED = 12;//初始化完成
+ int EVENT_ERROR = 13;//
+ int EVENT_VIDEOSIZECHANGE = 14;//
+ int EVENT_COMPLETION = 15;//
+ int EVENT_BUFFERING_UPDATA = 16;//
+
+
+ void setUp(String url, Object... objects);
+
+ void play();
+
+ void pause();
+
+ void seekTo(int duration);
+
+ void setPlayListener(PlayListener playListener);
+
+ boolean onBackPressed();
+
+ boolean isPlaying();
+
+ int getPosition();
+
+ int getDuration();
+
+ void release();
+
+}
diff --git a/qsvideoplayer/src/main/java/org/song/videoplayer/PlayListener.java b/qsvideoplayer/src/main/java/org/song/videoplayer/PlayListener.java
new file mode 100644
index 0000000..6df884a
--- /dev/null
+++ b/qsvideoplayer/src/main/java/org/song/videoplayer/PlayListener.java
@@ -0,0 +1,23 @@
+package org.song.videoplayer;
+
+/**
+ * Created by song on 2017/2/10.
+ * 播放器监听 值的意义见IVideoPlayer
+ */
+
+public interface PlayListener {
+ /**
+ * 播放器的ui状态
+ */
+ void onStatus(int status);
+
+ /**
+ * 播放器的ui模式[全屏/普通/...
+ */
+ void onMode(int mode);
+
+ /**
+ * 播放事件
+ */
+ void onEvent(int what, Integer... extra);
+}
diff --git a/qsvideoplayer/src/main/java/org/song/videoplayer/QSVideoView.java b/qsvideoplayer/src/main/java/org/song/videoplayer/QSVideoView.java
new file mode 100644
index 0000000..342cad0
--- /dev/null
+++ b/qsvideoplayer/src/main/java/org/song/videoplayer/QSVideoView.java
@@ -0,0 +1,442 @@
+package org.song.videoplayer;
+
+import android.app.AlertDialog;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.graphics.drawable.ColorDrawable;
+import android.util.AttributeSet;
+import android.view.Gravity;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.ImageView;
+import android.widget.PopupWindow;
+import android.widget.ProgressBar;
+import android.widget.TextView;
+
+import org.song.videoplayer.media.IMediaControl;
+
+/**
+ * Created by song on 2017/2/13.
+ * ui设计基于jcplayer
+ */
+public class QSVideoView extends BaseVideoView {
+
+ public boolean isShowWifiDialog = true;//是否显示非wifi提示
+
+ protected ImageView backButton;
+ protected ProgressBar bottomProgressBar;
+ protected TextView titleTextView;
+ protected ImageView coverImageView;
+ protected ImageView tinyBackImageView;
+
+ public QSVideoView(Context context) {
+ super(context);
+ }
+
+ public QSVideoView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+
+ @Override
+ protected int getLayoutId() {
+ return R.layout.video_view;
+ }
+
+ public ImageView getCoverImageView() {
+ return coverImageView;
+ }
+
+ @Override
+ protected void init(Context context) {
+ super.init(context);
+
+ bottomProgressBar = (ProgressBar) findViewById(R.id.bottom_progressbar);
+ titleTextView = (TextView) findViewById(R.id.title);
+ backButton = (ImageView) findViewById(R.id.back);
+ coverImageView = (ImageView) findViewById(R.id.cover);
+ tinyBackImageView = (ImageView) findViewById(R.id.back_tiny);
+
+ bottomProgressBar.setMax(progressMax);
+ backButton.setOnClickListener(this);
+ tinyBackImageView.setOnClickListener(this);
+
+ setUIWithStateAndMode(STATE_NORMAL, currentMode);
+ }
+
+ @Override
+ public void setUp(String url, Object... objects) {
+ super.setUp(url, objects);
+ if (objects != null)
+ titleTextView.setText(String.valueOf(objects[0]));
+ }
+
+
+ @Override
+ public void onClick(View v) {
+ super.onClick(v);
+ int i = v.getId();
+ if (i == R.id.back)
+ onBackPressed();
+ }
+
+ //---底部进度条 覆盖父类设置数据
+ @Override
+ protected void setProgressAndText() {
+ super.setProgressAndText();
+ bottomProgressBar.setProgress(progressBar.getProgress());
+ }
+
+ @Override
+ protected void setBufferProgress(int bufferProgress) {
+ super.setBufferProgress(bufferProgress);
+ bottomProgressBar.setSecondaryProgress(progressBar.getSecondaryProgress());
+ }
+
+ @Override
+ protected void resetProgressAndTime() {
+ super.resetProgressAndTime();
+ bottomProgressBar.setProgress(0);
+ bottomProgressBar.setSecondaryProgress(0);
+ }
+
+ @Override
+ protected void setCompleProgressAndTime() {
+ super.setCompleProgressAndTime();
+ bottomProgressBar.setProgress(progressMax);
+ }
+ //------底部进度条end
+
+
+ @Override//缓冲
+ protected void onBuffering(boolean isBuffering) {
+ bufferingContainer.setVisibility(isBuffering ? VISIBLE : INVISIBLE);
+ }
+
+ //隐藏控制ui
+ @Override
+ protected void dismissControlView() {
+ bottomContainer.setVisibility(View.INVISIBLE);
+ topContainer.setVisibility(View.INVISIBLE);
+ bottomProgressBar.setVisibility(View.VISIBLE);
+ if (currentState != STATE_AUTO_COMPLETE)
+ startButton.setVisibility(View.INVISIBLE);
+ }
+
+
+ //--------------根据各种状态设置ui是否显示(控制view也显示)------------------
+ @Override
+ protected void changeUiWithStateAndMode(final int status, final int mode) {
+ //立即隐藏控制ui标记 初始化好播放立即隐藏控制ui / 切换全屏如果原来是隐藏的也立即隐藏
+ boolean flag = (status == STATE_PLAYING & currentState == STATE_PREPARING) |
+ (mode != currentMode & bottomContainer.getVisibility() != VISIBLE);
+ switch (status) {
+ case STATE_NORMAL:
+ changeUiToNormal(mode);
+ break;
+ case STATE_PREPARING:
+ changeUiToPreparingShow(mode);
+ break;
+ case STATE_PLAYING:
+ changeUiToPlayingShow(mode);
+ startDismissControlViewTimer();
+ break;
+ case STATE_PAUSE:
+ changeUiToPauseShow(mode);
+ break;
+ case STATE_ERROR:
+ changeUiToError(mode);
+ break;
+ case STATE_AUTO_COMPLETE:
+ changeUiToCompleteShow(mode);
+ break;
+ }
+ updateViewImage(status, mode);
+ if (flag) dismissControlView();
+ }
+
+ protected void changeUiToNormal(int currentMode) {
+ bufferingContainer.setVisibility(INVISIBLE);
+ switch (currentMode) {
+ case MODE_WINDOW_NORMAL:
+ setAllControlsVisible(4, 4, 0, 0, 4, 4);
+ break;
+ case MODE_WINDOW_FULLSCREEN:
+ setAllControlsVisible(0, 4, 0, 0, 4, 4);
+ break;
+ case MODE_WINDOW_TINY:
+ break;
+ }
+ }
+
+ protected void changeUiToPreparingShow(int currentMode) {
+ bufferingContainer.setVisibility(INVISIBLE);
+ switch (currentMode) {
+ case MODE_WINDOW_NORMAL:
+ setAllControlsVisible(4, 4, 4, 4, 4, 0);
+
+ break;
+ case MODE_WINDOW_FULLSCREEN:
+ setAllControlsVisible(4, 4, 4, 4, 4, 0);
+ break;
+ case MODE_WINDOW_TINY:
+ break;
+ }
+ }
+
+ protected void changeUiToPlayingShow(int currentMode) {
+ switch (currentMode) {
+ case MODE_WINDOW_NORMAL:
+ setAllControlsVisible(4, 0, 0, 4, 4, 4);
+ break;
+ case MODE_WINDOW_FULLSCREEN:
+ setAllControlsVisible(0, 0, 0, 4, 4, 4);
+ break;
+ case MODE_WINDOW_TINY:
+ break;
+ }
+
+ }
+
+ protected void changeUiToPauseShow(int currentMode) {
+ switch (currentMode) {
+ case MODE_WINDOW_NORMAL:
+ setAllControlsVisible(4, 0, 0, 4, 4, 4);
+ break;
+ case MODE_WINDOW_FULLSCREEN:
+ setAllControlsVisible(0, 0, 0, 4, 4, 4);
+ break;
+ case MODE_WINDOW_TINY:
+ break;
+ }
+
+ }
+
+
+ protected void changeUiToCompleteShow(int currentMode) {
+ bufferingContainer.setVisibility(INVISIBLE);
+ switch (currentMode) {
+ case MODE_WINDOW_NORMAL:
+ setAllControlsVisible(4, 0, 0, 4, 4, 4);
+ break;
+ case MODE_WINDOW_FULLSCREEN:
+ setAllControlsVisible(0, 0, 0, 4, 4, 4);
+ break;
+ case MODE_WINDOW_TINY:
+ break;
+ }
+
+ }
+
+ protected void changeUiToError(int currentMode) {
+ bufferingContainer.setVisibility(INVISIBLE);
+ switch (currentMode) {
+ case MODE_WINDOW_NORMAL:
+ setAllControlsVisible(4, 4, 0, 4, 4, 4);
+ break;
+ case MODE_WINDOW_FULLSCREEN:
+ setAllControlsVisible(4, 4, 0, 4, 4, 4);
+ break;
+ case MODE_WINDOW_TINY:
+ break;
+ }
+
+ }
+
+ /**
+ * 0 VISIBLE 4 INVISIBLE 8 GONE
+ * 参数分别为
+ * 0顶部 1底部 2按钮 3封面 4小进度条 5初始化界面
+ */
+ protected void setAllControlsVisible(Integer... arr) {
+ topContainer.setVisibility(arr[0]);
+ bottomContainer.setVisibility(arr[1]);
+ startButton.setVisibility(arr[2]);
+ coverImageView.setVisibility(arr[3]);
+ bottomProgressBar.setVisibility(arr[4]);
+ loadingContainer.setVisibility(arr[5]);
+ //errorContainer.setVisibility(arr[6]);
+ }
+ //--------------根据各种状态设置ui是否显示 end------------------
+
+
+ protected void updateViewImage(int status, int mode) {
+ if (status == STATE_ERROR) {
+ startButton.setImageResource(R.drawable.jc_click_error_selector);
+ } else if (status == STATE_PLAYING) {
+ startButton.setImageResource(R.drawable.jc_click_pause_selector);
+ } else {
+ startButton.setImageResource(R.drawable.jc_click_play_selector);
+ }
+
+ if (mode == MODE_WINDOW_NORMAL)
+ fullscreenButton.setImageResource(R.drawable.jc_enlarge);
+ else
+ fullscreenButton.setImageResource(R.drawable.jc_shrink);
+
+ }
+
+
+ //-----------------弹窗---------------
+ @Override
+ protected boolean showWifiDialog() {
+ if (!isShowWifiDialog)
+ return false;
+ AlertDialog.Builder builder = new AlertDialog.Builder(getContext());
+ 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();
+ prepareMediaPlayer();
+ }
+ });
+ 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();
+ return true;
+ }
+
+ protected PopupWindow mProgressDialog;
+ protected ProgressBar mDialogProgressBar;
+ protected TextView tv_current;
+ protected TextView tv_duration;
+ protected TextView tv_delta;
+ protected ImageView mDialogIcon;
+
+ @Override
+ protected boolean showProgressDialog(int delta, int position, int duration) {
+ if (mProgressDialog == null) {
+ View localView = LayoutInflater.from(getContext()).inflate(R.layout.jc_dialog_progress, null);
+ mDialogProgressBar = ((ProgressBar) localView.findViewById(R.id.duration_progressbar));
+ tv_current = ((TextView) localView.findViewById(R.id.tv_current));
+ tv_duration = ((TextView) localView.findViewById(R.id.tv_duration));
+ tv_delta = ((TextView) localView.findViewById(R.id.tv_delta));
+ mDialogIcon = ((ImageView) localView.findViewById(R.id.duration_image_tip));
+ mProgressDialog = getPopupWindow(localView);
+
+ }
+ if (!mProgressDialog.isShowing()) {
+ mProgressDialog.showAtLocation(this, Gravity.CENTER, 0, 0);
+ }
+
+ tv_delta.setText(
+ (delta > 0 ? "+" : "") +
+ delta / 1000 + "秒");
+ tv_current.setText(Util.stringForTime(position + delta) + "/");
+ tv_duration.setText(Util.stringForTime(duration));
+ mDialogProgressBar.setProgress((position + delta) * 100 / duration);
+ if (delta > 0) {
+ mDialogIcon.setBackgroundResource(R.drawable.jc_forward_icon);
+ } else {
+ mDialogIcon.setBackgroundResource(R.drawable.jc_backward_icon);
+ }
+ return true;
+ }
+
+ @Override
+ protected boolean dismissProgressDialog() {
+ if (mProgressDialog != null) {
+ mProgressDialog.dismiss();
+ }
+ return true;
+ }
+
+
+ protected PopupWindow mVolumeDialog;
+ protected ProgressBar mDialogVolumeProgressBar;
+ protected TextView mDialogVolumeTextView;
+ protected ImageView mDialogVolumeImageView;
+
+
+ @Override
+ protected boolean showVolumeDialog(int nowVolume, int maxVolume) {
+
+ if (mVolumeDialog == null) {
+ View localView = LayoutInflater.from(getContext()).inflate(R.layout.jc_dialog_volume, null);
+ mDialogVolumeImageView = ((ImageView) localView.findViewById(R.id.volume_image_tip));
+ mDialogVolumeTextView = ((TextView) localView.findViewById(R.id.tv_volume));
+ mDialogVolumeProgressBar = ((ProgressBar) localView.findViewById(R.id.volume_progressbar));
+ mDialogVolumeProgressBar.setMax(maxVolume);
+ mVolumeDialog = getPopupWindow(localView);
+ }
+ if (!mVolumeDialog.isShowing())
+ mVolumeDialog.showAtLocation(this, Gravity.TOP, 0, Util.dp2px(getContext(), 50));
+
+ mDialogVolumeTextView.setText(nowVolume + "");
+ mDialogVolumeProgressBar.setProgress(nowVolume);
+ return true;
+ }
+
+ @Override
+ protected boolean dismissVolumeDialog() {
+ if (mVolumeDialog != null) {
+ mVolumeDialog.dismiss();
+ }
+ return true;
+ }
+
+ protected PopupWindow mBrightnessDialog;
+ protected ProgressBar mDialogBrightnessProgressBar;
+ protected TextView mDialogBrightnessTextView;
+
+ @Override
+ protected boolean showBrightnessDialog(int brightnessPercent, int max) {
+ if (mBrightnessDialog == null) {
+ View localView = LayoutInflater.from(getContext()).inflate(R.layout.jc_dialog_brightness, null);
+ mDialogBrightnessTextView = ((TextView) localView.findViewById(R.id.tv_brightness));
+ mDialogBrightnessProgressBar = ((ProgressBar) localView.findViewById(R.id.brightness_progressbar));
+ mDialogBrightnessProgressBar.setMax(max);
+ //mBrightnessDialog = getDialog(Gravity.TOP, 0, Util.dp2px(getContext(), 50));
+ //mBrightnessDialog.setContentView(localView);
+
+ mBrightnessDialog = getPopupWindow(localView);
+ }
+ if (!mBrightnessDialog.isShowing())
+ mBrightnessDialog.showAtLocation(this, Gravity.TOP, 0, Util.dp2px(getContext(), 50));
+
+ mDialogBrightnessTextView.setText(brightnessPercent + "");
+ mDialogBrightnessProgressBar.setProgress(brightnessPercent);
+ return true;
+ }
+
+ @Override
+ protected boolean dismissBrightnessDialog() {
+ if (mBrightnessDialog != null) {
+ mBrightnessDialog.dismiss();
+ }
+ return true;
+ }
+
+//
+// private Dialog getDialog(int graviaty, int marginX, int marginY) {
+// Dialog dialog = new Dialog(getContext(), R.style.jc_style_dialog_progress);
+// dialog.getWindow().addFlags(Window.FEATURE_ACTION_BAR);
+// dialog.getWindow().addFlags(32);
+// dialog.getWindow().addFlags(16);
+// dialog.getWindow().setLayout(-2, -2);
+// WindowManager.LayoutParams localLayoutParams = dialog.getWindow().getAttributes();
+// localLayoutParams.gravity = graviaty;
+// if (marginX > 0)
+// localLayoutParams.x = marginX;
+// if (marginY > 0)
+// localLayoutParams.y = marginY;
+// dialog.getWindow().setAttributes(localLayoutParams);
+//
+// return dialog;
+// }
+
+ private PopupWindow getPopupWindow(View popupView) {
+ PopupWindow mPopupWindow = new PopupWindow(popupView, LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT, true);
+ mPopupWindow.setTouchable(true);
+ mPopupWindow.setOutsideTouchable(true);
+ mPopupWindow.setBackgroundDrawable(new ColorDrawable(0));
+ mPopupWindow.setAnimationStyle(R.style.jc_popup_toast_anim);
+ return mPopupWindow;
+ }
+
+}
diff --git a/qsvideoplayer/src/main/java/org/song/videoplayer/Util.java b/qsvideoplayer/src/main/java/org/song/videoplayer/Util.java
new file mode 100644
index 0000000..234bd16
--- /dev/null
+++ b/qsvideoplayer/src/main/java/org/song/videoplayer/Util.java
@@ -0,0 +1,96 @@
+package org.song.videoplayer;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.ContextWrapper;
+import android.content.pm.ActivityInfo;
+import android.net.ConnectivityManager;
+import android.net.NetworkInfo;
+import android.view.Window;
+import android.view.WindowManager;
+
+/**
+ * Created by song on 2017/2/13.
+ */
+
+public class Util {
+ //单位毫秒
+ public static String stringForTime(int timeMs) {
+ if (timeMs <= 0) {
+ return "00:00";
+ }
+ int totalSeconds = timeMs / 1000;
+ int seconds = totalSeconds % 60;
+ int minutes = (totalSeconds / 60) % 60;
+ return String.format("%02d:%02d", minutes, seconds);
+ }
+
+ public static boolean isWifiConnected(Context context) {
+ ConnectivityManager connectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
+ NetworkInfo networkInfo = connectivityManager.getActiveNetworkInfo();
+ return networkInfo != null && networkInfo.getType() == ConnectivityManager.TYPE_WIFI;
+ }
+
+ //保持常亮
+ public static void KEEP_SCREEN_ON(Context context) {
+ scanForActivity(context).getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
+ }
+
+ //取消常亮
+ public static void KEEP_SCREEN_OFF(Context context) {
+ scanForActivity(context).getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
+ }
+
+
+ //全屏
+ public static boolean SET_FULL(Context context) {
+ Window w = scanForActivity(context).getWindow();
+ boolean b = (w.getAttributes().flags & WindowManager.LayoutParams.FLAG_FULLSCREEN) == WindowManager.LayoutParams.FLAG_FULLSCREEN;
+ w.setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
+ return b;
+
+ }
+
+ //取消全屏
+ public static void CLEAR_FULL(Context context) {
+ scanForActivity(context).getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
+ }
+
+
+ //横屏
+ public static void SET_LANDSCAPE(Context context) {
+ scanForActivity(context).setRequestedOrientation
+ (ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
+ }
+
+ //竖屏屏
+ public static void SET_PORTRAIT(Context context) {
+ scanForActivity(context).setRequestedOrientation
+ (ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
+ }
+
+ //重力感应
+ public static void SET_SENSOR(Context context) {
+ scanForActivity(context).setRequestedOrientation
+ (ActivityInfo.SCREEN_ORIENTATION_SENSOR);
+ }
+
+ public static int dp2px(Context context, float value) {
+ final float scale = context.getResources().getDisplayMetrics().densityDpi;
+ return (int) (value * (scale / 160) + 0.5f);
+ }
+
+
+ public static Activity scanForActivity(Context context) {
+ if (context instanceof Activity) {
+ Activity a = (Activity) context;
+ if (a.getParent() != null)
+ return a.getParent();
+ else
+ return a;
+ } else if (context instanceof ContextWrapper) {
+ return scanForActivity(((ContextWrapper) context).getBaseContext());
+ }
+ throw new IllegalStateException("context得不到activity");
+ }
+}
diff --git a/qsvideoplayer/src/main/java/org/song/videoplayer/media/AndroidMedia.java b/qsvideoplayer/src/main/java/org/song/videoplayer/media/AndroidMedia.java
new file mode 100644
index 0000000..4b061ea
--- /dev/null
+++ b/qsvideoplayer/src/main/java/org/song/videoplayer/media/AndroidMedia.java
@@ -0,0 +1,196 @@
+package org.song.videoplayer.media;
+
+import android.annotation.TargetApi;
+import android.content.Context;
+import android.media.AudioManager;
+import android.media.MediaPlayer;
+import android.view.Surface;
+import android.view.SurfaceHolder;
+
+import java.lang.reflect.Method;
+import java.util.Map;
+
+/**
+ * Created by song on 2017/2/10.
+ * 安卓系统硬解
+ */
+
+public class AndroidMedia extends BaseMedia implements MediaPlayer.OnPreparedListener, MediaPlayer.OnCompletionListener, MediaPlayer.OnBufferingUpdateListener, MediaPlayer.OnSeekCompleteListener, MediaPlayer.OnErrorListener, MediaPlayer.OnInfoListener, MediaPlayer.OnVideoSizeChangedListener {
+
+ public MediaPlayer mediaPlayer;
+
+ public AndroidMedia(IMediaCallback iMediaCallback) {
+ super(iMediaCallback);
+ }
+
+ /////////////以下MediaPlayer控制/////////////
+ @Override
+ public void doPrepar(Context context, String url, Map headers) {
+ try {
+ release();
+ mediaPlayer = new MediaPlayer();
+ mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
+ Class clazz = MediaPlayer.class;
+ Method method = clazz.getDeclaredMethod("setDataSource", String.class, Map.class);
+ method.invoke(mediaPlayer, url, headers);//反射不传context
+ mediaPlayer.setLooping(false);
+ mediaPlayer.setOnPreparedListener(this);
+ mediaPlayer.setOnCompletionListener(this);
+ mediaPlayer.setOnBufferingUpdateListener(this);
+ mediaPlayer.setScreenOnWhilePlaying(true);
+ mediaPlayer.setOnSeekCompleteListener(this);
+ mediaPlayer.setOnErrorListener(this);
+ mediaPlayer.setOnInfoListener(this);
+ mediaPlayer.setOnVideoSizeChangedListener(this);
+ mediaPlayer.prepareAsync();
+ //mediaPlayer.setDisplay();
+ } catch (Exception e) {
+ e.printStackTrace();
+ onError(mediaPlayer, 10086, 10086);
+ }
+ }
+
+ @TargetApi(14)
+ @Override
+ public void setSurface(Surface surface) {
+ try {
+ if (mediaPlayer != null)
+ mediaPlayer.setSurface(surface);
+ this.surface = surface;
+ } catch (Exception e) {
+ e.printStackTrace();
+ onError(mediaPlayer, 10010, 10010);
+ }
+ }
+
+ @Override
+ public void setDisplay(SurfaceHolder surfaceHolder) {
+ try {
+ if (mediaPlayer != null)
+ mediaPlayer.setDisplay(surfaceHolder);
+ if (surfaceHolder != null)
+ this.surface = surfaceHolder.getSurface();
+ } catch (Exception e) {
+ e.printStackTrace();
+ onError(mediaPlayer, 10010, 10010);
+ }
+ }
+
+ @Override
+ public void doPlay() {
+ if (!isPrepar)
+ return;
+ mediaPlayer.start();
+ }
+
+ @Override
+ public void doPause() {
+ if (!isPrepar)
+ return;
+ mediaPlayer.pause();
+ }
+
+ @Override
+ public void seekTo(int duration) {
+ if (!isPrepar)
+ return;
+ mediaPlayer.seekTo(duration);
+ }
+
+ @Override
+ public int getCurrentPosition() {
+ if (!isPrepar)
+ return 0;
+ try {
+ return mediaPlayer.getCurrentPosition();
+ } catch (Exception e) {
+ e.printStackTrace();
+ return 0;
+ }
+ }
+
+ @Override
+ public int getDuration() {
+ if (!isPrepar)
+ return 0;
+ try {
+ return mediaPlayer.getDuration();
+ } catch (Exception e) {
+ e.printStackTrace();
+ return 0;
+ }
+ }
+
+ @Override
+ public int getVideoHeight() {
+ if (!isPrepar)
+ return 0;
+ return mediaPlayer.getVideoHeight();
+ }
+
+ @Override
+ public int getVideowidth() {
+ if (!isPrepar)
+ return 0;
+ return mediaPlayer.getVideoWidth();
+ }
+
+ @Override
+ public boolean isPlaying() {
+ if (!isPrepar)
+ return false;
+ return mediaPlayer.isPlaying();
+ }
+
+ @Override
+ public void release() {
+ if (mediaPlayer != null)
+ mediaPlayer.release();
+ mediaPlayer = null;
+ this.surface = null;
+ isPrepar = false;
+ }
+
+ /////////////以下MediaPlayer回调//////////////
+ @Override
+ public void onPrepared(MediaPlayer mp) {
+ isPrepar = true;
+ iMediaCallback.onPrepared(this);
+ }
+
+
+ @Override
+ public void onCompletion(MediaPlayer mp) {
+ iMediaCallback.onCompletion(this);
+ }
+
+ @Override
+ public boolean onError(MediaPlayer mp, int what, int extra) {
+ isPrepar = false;
+ iMediaCallback.onError(this, what, extra);
+ return true;
+ }
+
+ @Override
+ public void onBufferingUpdate(MediaPlayer mp, int percent) {
+ iMediaCallback.onBufferingUpdate(this, percent / 100.0f);
+ }
+
+ @Override
+ public boolean onInfo(MediaPlayer mp, int what, int extra) {
+ iMediaCallback.onInfo(this, what, extra);
+ return false;
+ }
+
+
+ @Override
+ public void onSeekComplete(MediaPlayer mp) {
+ iMediaCallback.onSeekComplete(this);
+ }
+
+ @Override
+ public void onVideoSizeChanged(MediaPlayer mp, int width, int height) {
+ iMediaCallback.onVideoSizeChanged(this, width, height);
+ }
+
+}
diff --git a/qsvideoplayer/src/main/java/org/song/videoplayer/media/BaseMedia.java b/qsvideoplayer/src/main/java/org/song/videoplayer/media/BaseMedia.java
new file mode 100644
index 0000000..e16ae63
--- /dev/null
+++ b/qsvideoplayer/src/main/java/org/song/videoplayer/media/BaseMedia.java
@@ -0,0 +1,27 @@
+package org.song.videoplayer.media;
+
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Looper;
+import android.view.Surface;
+
+/**
+ * Created by song on 2017/2/10.
+ */
+
+public abstract class BaseMedia implements IMediaControl {
+
+ protected IMediaCallback iMediaCallback;
+ protected Surface surface;
+ protected boolean isPrepar;
+ Handler mainThreadHandler;
+
+ public Surface getSurface() {
+ return surface;
+ }
+
+ public BaseMedia(IMediaCallback iMediaCallback) {
+ this.iMediaCallback = iMediaCallback;
+ mainThreadHandler=new Handler(Looper.getMainLooper());
+ }
+}
diff --git a/qsvideoplayer/src/main/java/org/song/videoplayer/media/ExoMedia.java b/qsvideoplayer/src/main/java/org/song/videoplayer/media/ExoMedia.java
new file mode 100644
index 0000000..2a81330
--- /dev/null
+++ b/qsvideoplayer/src/main/java/org/song/videoplayer/media/ExoMedia.java
@@ -0,0 +1,283 @@
+package org.song.videoplayer.media;
+
+import android.content.Context;
+import android.net.Uri;
+import android.util.Log;
+import android.view.Surface;
+import android.view.SurfaceHolder;
+
+import com.google.android.exoplayer2.C;
+import com.google.android.exoplayer2.DefaultLoadControl;
+import com.google.android.exoplayer2.ExoPlaybackException;
+import com.google.android.exoplayer2.ExoPlayer;
+import com.google.android.exoplayer2.ExoPlayerFactory;
+import com.google.android.exoplayer2.SimpleExoPlayer;
+import com.google.android.exoplayer2.Timeline;
+import com.google.android.exoplayer2.extractor.DefaultExtractorsFactory;
+import com.google.android.exoplayer2.source.ExtractorMediaSource;
+import com.google.android.exoplayer2.source.MediaSource;
+import com.google.android.exoplayer2.source.dash.DashMediaSource;
+import com.google.android.exoplayer2.source.dash.DefaultDashChunkSource;
+import com.google.android.exoplayer2.source.hls.HlsMediaSource;
+import com.google.android.exoplayer2.source.smoothstreaming.DefaultSsChunkSource;
+import com.google.android.exoplayer2.source.smoothstreaming.SsMediaSource;
+import com.google.android.exoplayer2.trackselection.AdaptiveVideoTrackSelection;
+import com.google.android.exoplayer2.trackselection.DefaultTrackSelector;
+import com.google.android.exoplayer2.trackselection.TrackSelection;
+import com.google.android.exoplayer2.upstream.DefaultBandwidthMeter;
+import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory;
+import com.google.android.exoplayer2.upstream.DefaultHttpDataSourceFactory;
+
+import java.util.Map;
+
+//import com.google.android.exoplayer2.trackselection.TrackSelectionArray;
+
+/**
+ * Created by song on 2017/2/22.
+ * exo-2.0.4
+ */
+
+public class ExoMedia extends BaseMedia implements ExoPlayer.EventListener, SimpleExoPlayer.VideoListener {
+
+ private final String USER_AGENT = "(¬_¬)";
+
+ private SimpleExoPlayer simpleExoPlayer;
+ private int currentVideoWidth = 0;
+ private int currentVideoHeight = 0;
+
+ public ExoMedia(IMediaCallback iMediaCallback) {
+ super(iMediaCallback);
+ }
+
+ private final DefaultBandwidthMeter BANDWIDTH_METER = new DefaultBandwidthMeter();
+
+ @Override
+ public void doPrepar(Context context, String url, Map headers) {
+ release();
+ TrackSelection.Factory videoTrackSelectionFactory =
+ new AdaptiveVideoTrackSelection.Factory(BANDWIDTH_METER);
+ DefaultTrackSelector trackSelector = new DefaultTrackSelector(mainThreadHandler, videoTrackSelectionFactory);
+ simpleExoPlayer = ExoPlayerFactory.newSimpleInstance(context, trackSelector, new DefaultLoadControl(),
+ null, false);
+ simpleExoPlayer.setPlayWhenReady(true);
+ MediaSource mediaSource = buildMediaSource(context, Uri.parse(url));
+ simpleExoPlayer.addListener(ExoMedia.this);
+ simpleExoPlayer.setVideoListener(ExoMedia.this);
+ simpleExoPlayer.prepare(mediaSource, true, true);
+ }
+
+ private MediaSource buildMediaSource(Context context, Uri uri) {
+ int type = getUrlType(uri.toString());
+ switch (type) {
+ case C.TYPE_SS:
+ return new SsMediaSource(uri, new DefaultDataSourceFactory(context, null,
+ new DefaultHttpDataSourceFactory(USER_AGENT, null)),
+ new DefaultSsChunkSource.Factory(new DefaultDataSourceFactory(context, BANDWIDTH_METER,
+ new DefaultHttpDataSourceFactory(USER_AGENT, BANDWIDTH_METER))), mainThreadHandler, null);
+ case C.TYPE_DASH:
+ return new DashMediaSource(uri, new DefaultDataSourceFactory(context, null,
+ new DefaultHttpDataSourceFactory(USER_AGENT, null)),
+ new DefaultDashChunkSource.Factory(new DefaultDataSourceFactory(context, BANDWIDTH_METER,
+ new DefaultHttpDataSourceFactory(USER_AGENT, BANDWIDTH_METER))), mainThreadHandler, null);
+ case C.TYPE_HLS:
+ return new HlsMediaSource(uri, new DefaultDataSourceFactory(context, BANDWIDTH_METER,
+ new DefaultHttpDataSourceFactory(USER_AGENT, BANDWIDTH_METER)), mainThreadHandler, null);
+ case C.TYPE_OTHER:
+ return new ExtractorMediaSource(uri, new DefaultDataSourceFactory(context, BANDWIDTH_METER,
+ new DefaultHttpDataSourceFactory(USER_AGENT, BANDWIDTH_METER)), new DefaultExtractorsFactory(),
+ mainThreadHandler, null);
+ default: {
+ throw new IllegalStateException("Unsupported type: " + type);
+ }
+ }
+ }
+
+ private static int getUrlType(String url) {
+ if (url.contains(".mpd")) {
+ return C.TYPE_DASH;
+ } else if (url.contains(".ism") || url.contains(".isml")) {
+ return C.TYPE_SS;
+ } else if (url.contains(".m3u8")) {
+ return C.TYPE_HLS;
+ } else {
+ return C.TYPE_OTHER;
+ }
+ }
+
+ @Override
+ public void setSurface(Surface surface) {
+ try {
+ if (simpleExoPlayer != null)
+ simpleExoPlayer.setVideoSurface(surface);
+ this.surface = surface;
+ } catch (Exception e) {
+ e.printStackTrace();
+ iMediaCallback.onError(this, 10010, 10010);
+ }
+ }
+
+ @Override
+ public void setDisplay(SurfaceHolder surfaceHolder) {
+ try {
+ if (simpleExoPlayer != null)
+ simpleExoPlayer.setVideoSurfaceHolder(surfaceHolder);
+ if (surfaceHolder != null)
+ this.surface = surfaceHolder.getSurface();
+ } catch (Exception e) {
+ e.printStackTrace();
+ iMediaCallback.onError(this, 10010, 10010);
+ }
+ }
+
+ @Override
+ public void doPlay() {
+ if (!isPrepar)
+ return;
+ simpleExoPlayer.setPlayWhenReady(true);
+ }
+
+ @Override
+ public void doPause() {
+ if (!isPrepar)
+ return;
+ simpleExoPlayer.setPlayWhenReady(false);
+ }
+
+ @Override
+ public void seekTo(int du) {
+ if (!isPrepar)
+ return;
+ simpleExoPlayer.seekTo(du);
+ }
+
+ @Override
+ public int getCurrentPosition() {
+ if (!isPrepar)
+ return 0;
+ return (int) simpleExoPlayer.getCurrentPosition();
+ }
+
+ @Override
+ public int getDuration() {
+ if (!isPrepar)
+ return -1;
+ return (int) simpleExoPlayer.getDuration();
+ }
+
+ @Override
+ public int getVideoHeight() {
+ if (!isPrepar)
+ return 0;
+ return currentVideoHeight;//simpleExoPlayer.getVideoFormat().height;
+ }
+
+ @Override
+ public int getVideowidth() {
+ if (!isPrepar)
+ return 0;
+ return currentVideoWidth;//simpleExoPlayer.getVideoFormat().width;
+ }
+
+ @Override
+ public boolean isPlaying() {
+ return isPrepar && simpleExoPlayer.getPlayWhenReady();
+ }
+
+ @Override
+ public void release() {
+ if (simpleExoPlayer != null) {
+ simpleExoPlayer.release();
+ }
+ mainThreadHandler.removeCallbacks(runnable);
+
+ simpleExoPlayer = null;
+ surface = null;
+ currentVideoWidth = 0;
+ currentVideoHeight = 0;
+ isPrepar = false;
+ }
+
+ ///////////// media 回调 ///////////////////
+
+ private Runnable runnable = new Runnable() {
+ @Override
+ public void run() {
+ if (isPrepar)
+ iMediaCallback.onBufferingUpdate(ExoMedia.this, simpleExoPlayer.getBufferedPercentage() / 100.f);
+ mainThreadHandler.postDelayed(runnable, 1000);
+ }
+ };
+
+
+ @Override
+ public void onPlayerStateChanged(boolean b, int i) {
+ Log.e("ExoMedia", "onPlayerStateChanged " + b + i);
+
+ if (i == ExoPlayer.STATE_READY) {
+ if (!isPrepar) {
+ isPrepar = true;
+ iMediaCallback.onPrepared(this);//第一次初始化
+ }
+ //缓冲好了
+ iMediaCallback.onInfo(this, MEDIA_INFO_BUFFERING_END, MEDIA_INFO_BUFFERING_END);
+ mainThreadHandler.postDelayed(runnable, 500);
+ }
+ //播放完毕
+ if (i == ExoPlayer.STATE_ENDED) {
+ mainThreadHandler.removeCallbacks(runnable);
+ iMediaCallback.onCompletion(this);
+ }
+ if (i == ExoPlayer.STATE_BUFFERING)
+ iMediaCallback.onInfo(this, MEDIA_INFO_BUFFERING_START, MEDIA_INFO_BUFFERING_START);
+
+ }
+
+
+ @Override
+ public void onPlayerError(ExoPlaybackException e) {
+ iMediaCallback.onError(this, MEDIA_ERROR_UNKNOWN, MEDIA_ERROR_UNKNOWN);
+ }
+
+
+ @Override
+ public void onVideoSizeChanged(int width, int height, int i2, float v) {
+ currentVideoWidth = width;
+ currentVideoHeight = height;
+ iMediaCallback.onVideoSizeChanged(this, width, height);
+ }
+
+ //加载缓冲调用 用来更新缓冲进度..
+ @Override
+ public void onLoadingChanged(boolean b) {
+ iMediaCallback.onBufferingUpdate(ExoMedia.this, simpleExoPlayer.getBufferedPercentage() / 100.f);
+ Log.e("ExoMedia", "onLoadingChanged " + b + simpleExoPlayer.getBufferedPercentage());
+ }
+
+ @Override
+ public void onTimelineChanged(Timeline timeline, Object o) {
+ Log.e("ExoMedia", "onTimelineChanged");
+
+ }
+
+// @Override
+// public void onTracksChanged(TrackGroupArray trackGroups, TrackSelectionArray trackSelections) {
+// Log.e("ExoMedia", "onTracksChanged");
+// }
+
+ @Override
+ public void onPositionDiscontinuity() {
+ Log.e("ExoMedia", "onPositionDiscontinuity");
+
+ }
+
+ @Override
+ public void onRenderedFirstFrame() {
+ Log.e("ExoMedia", "onRenderedFirstFrame");
+
+ }
+
+ @Override
+ public void onVideoTracksDisabled() {
+
+ }
+}
diff --git a/qsvideoplayer/src/main/java/org/song/videoplayer/media/IMediaCallback.java b/qsvideoplayer/src/main/java/org/song/videoplayer/media/IMediaCallback.java
new file mode 100644
index 0000000..6bd2eee
--- /dev/null
+++ b/qsvideoplayer/src/main/java/org/song/videoplayer/media/IMediaCallback.java
@@ -0,0 +1,24 @@
+package org.song.videoplayer.media;
+
+/**
+ * Created by song on 2017/2/10.
+ * 解码器回调
+ */
+
+public interface IMediaCallback {
+
+
+ void onPrepared(IMediaControl iMediaControl);//准备完毕
+
+ void onCompletion(IMediaControl iMediaControl);//播放完毕
+
+ void onSeekComplete(IMediaControl iMediaControl);//拖动进度条完毕
+
+ void onInfo(IMediaControl iMediaControl, int what, int extra);//播放事件 [缓冲 缓冲完毕
+
+ void onVideoSizeChanged(IMediaControl iMediaControl, int width, int height);//视频尺寸变化
+
+ void onError(IMediaControl iMediaControl, int what, int extra);//播放出错
+
+ void onBufferingUpdate(IMediaControl iMediaControl, final float percent);//缓冲进度 0~1
+}
diff --git a/qsvideoplayer/src/main/java/org/song/videoplayer/media/IMediaControl.java b/qsvideoplayer/src/main/java/org/song/videoplayer/media/IMediaControl.java
new file mode 100644
index 0000000..df8d5c5
--- /dev/null
+++ b/qsvideoplayer/src/main/java/org/song/videoplayer/media/IMediaControl.java
@@ -0,0 +1,63 @@
+package org.song.videoplayer.media;
+
+import android.content.Context;
+import android.view.Surface;
+import android.view.SurfaceHolder;
+
+import java.util.Map;
+
+/**
+ * Created by song on 2017/2/10.
+ * 解码器控制
+ */
+
+public interface IMediaControl {
+
+ int MEDIA_INFO_UNKNOWN = 1;
+ int MEDIA_INFO_STARTED_AS_NEXT = 2;
+ int MEDIA_INFO_VIDEO_RENDERING_START = 3;
+ int MEDIA_INFO_VIDEO_TRACK_LAGGING = 700;
+ int MEDIA_INFO_BUFFERING_START = 701;
+ int MEDIA_INFO_BUFFERING_END = 702;
+ int MEDIA_INFO_NETWORK_BANDWIDTH = 703;
+ int MEDIA_INFO_BAD_INTERLEAVING = 800;
+ int MEDIA_INFO_NOT_SEEKABLE = 801;
+ int MEDIA_INFO_METADATA_UPDATE = 802;
+ int MEDIA_INFO_TIMED_TEXT_ERROR = 900;
+ int MEDIA_INFO_UNSUPPORTED_SUBTITLE = 901;
+ int MEDIA_INFO_SUBTITLE_TIMED_OUT = 902;
+ int MEDIA_INFO_VIDEO_ROTATION_CHANGED = 10001;
+ int MEDIA_INFO_AUDIO_RENDERING_START = 10002;
+ int MEDIA_ERROR_UNKNOWN = 1;
+ int MEDIA_ERROR_SERVER_DIED = 100;
+ int MEDIA_ERROR_NOT_VALID_FOR_PROGRESSIVE_PLAYBACK = 200;
+ int MEDIA_ERROR_IO = -1004;
+ int MEDIA_ERROR_MALFORMED = -1007;
+ int MEDIA_ERROR_UNSUPPORTED = -1010;
+ int MEDIA_ERROR_TIMED_OUT = -110;
+
+ void doPrepar(Context context, String url, Map headers);//准备播放
+
+ void setSurface(Surface surface);//api14以上用这个 可以无缝全屏切换
+
+ void setDisplay(SurfaceHolder surfaceHolder);//api14一下用这个 全屏切换会顿一下
+
+ void doPlay();//播放
+
+ void doPause();//暂停
+
+ void seekTo(int du);//调整播放进度
+
+ int getCurrentPosition();//当前播放进度
+
+ int getDuration();//视频长度
+
+ int getVideoHeight();
+
+ int getVideowidth();
+
+ boolean isPlaying();
+
+ void release();//销毁
+
+}
diff --git a/qsvideoplayer/src/main/java/org/song/videoplayer/media/IjkBaseMedia.java b/qsvideoplayer/src/main/java/org/song/videoplayer/media/IjkBaseMedia.java
new file mode 100644
index 0000000..2031265
--- /dev/null
+++ b/qsvideoplayer/src/main/java/org/song/videoplayer/media/IjkBaseMedia.java
@@ -0,0 +1,204 @@
+package org.song.videoplayer.media;
+
+import android.content.Context;
+import android.media.AudioManager;
+import android.os.HandlerThread;
+import android.view.Surface;
+import android.view.SurfaceHolder;
+
+import java.util.Map;
+
+import tv.danmaku.ijk.media.player.IMediaPlayer;
+
+/**
+ * Created by song on 2017/2/22.
+ */
+
+public abstract class IjkBaseMedia extends BaseMedia implements IMediaPlayer.OnPreparedListener, IMediaPlayer.OnCompletionListener,
+ IMediaPlayer.OnBufferingUpdateListener, IMediaPlayer.OnSeekCompleteListener, IMediaPlayer.OnErrorListener,
+ IMediaPlayer.OnVideoSizeChangedListener, IMediaPlayer.OnInfoListener {
+
+
+ public IMediaPlayer mediaPlayer;
+
+ public IjkBaseMedia(IMediaCallback iMediaCallback) {
+ super(iMediaCallback);
+ }
+
+ @Override
+ public void doPrepar(final Context context, final String url, final Map headers) {
+// new Thread(new Runnable() {
+// @Override
+// public void run() {
+ try {
+ release();
+ mediaPlayer = getMedia(context, url, headers);
+ mediaPlayer.setOnPreparedListener(IjkBaseMedia.this);
+ mediaPlayer.setOnVideoSizeChangedListener(IjkBaseMedia.this);
+ mediaPlayer.setOnCompletionListener(IjkBaseMedia.this);
+ mediaPlayer.setOnErrorListener(IjkBaseMedia.this);
+ mediaPlayer.setOnInfoListener(IjkBaseMedia.this);
+ mediaPlayer.setOnBufferingUpdateListener(IjkBaseMedia.this);
+
+ mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
+ mediaPlayer.setScreenOnWhilePlaying(true);
+ mediaPlayer.prepareAsync();
+ } catch (Exception e) {
+ e.printStackTrace();
+ onError(mediaPlayer, 10086, 10086);
+ }
+ //} }).start();
+
+
+ }
+
+ abstract IMediaPlayer getMedia(Context context, String url, Map headers) throws Exception;
+
+ @Override
+ public void setSurface(Surface surface) {
+ try {
+ if (mediaPlayer != null)
+ mediaPlayer.setSurface(surface);
+ this.surface = surface;
+ } catch (Exception e) {
+ e.printStackTrace();
+ iMediaCallback.onError(this, 10010, 10010);
+ }
+ }
+
+ @Override
+ public void setDisplay(SurfaceHolder surfaceHolder) {
+ try {
+ if (mediaPlayer != null)
+ mediaPlayer.setDisplay(surfaceHolder);
+ if (surfaceHolder != null)
+ this.surface = surfaceHolder.getSurface();
+ } catch (Exception e) {
+ e.printStackTrace();
+ iMediaCallback.onError(this, 10010, 10010);
+ }
+ }
+
+ @Override
+ public void doPlay() {
+ if (!isPrepar)
+ return;
+ mediaPlayer.start();
+ }
+
+ @Override
+ public void doPause() {
+ if (!isPrepar)
+ return;
+ mediaPlayer.pause();
+ }
+
+ @Override
+ public void seekTo(int duration) {
+ if (!isPrepar)
+ return;
+ mediaPlayer.seekTo(duration);
+ }
+
+ @Override
+ public int getCurrentPosition() {
+ if (!isPrepar)
+ return 0;
+ try {
+ return (int) mediaPlayer.getCurrentPosition();
+ } catch (Exception e) {
+ e.printStackTrace();
+ return 0;
+ }
+ }
+
+ @Override
+ public int getDuration() {
+ if (!isPrepar)
+ return 0;
+ try {
+ return (int) mediaPlayer.getDuration();
+ } catch (Exception e) {
+ e.printStackTrace();
+ return 0;
+ }
+ }
+
+ @Override
+ public int getVideoHeight() {
+ if (!isPrepar)
+ return 0;
+ return mediaPlayer.getVideoHeight();
+ }
+
+ @Override
+ public int getVideowidth() {
+ if (!isPrepar)
+ return 0;
+ return mediaPlayer.getVideoWidth();
+ }
+
+ @Override
+ public boolean isPlaying() {
+ if (!isPrepar)
+ return false;
+ return mediaPlayer.isPlaying();
+ }
+
+ @Override
+ public void release() {
+ if (mediaPlayer != null)
+ new Thread(new Runnable() {
+ @Override
+ public void run() {
+ mediaPlayer.release();
+ mediaPlayer = null;
+ }
+ }).start();
+
+ this.surface = null;
+ isPrepar = false;
+ }
+
+ /////////////以下MediaPlayer回调//////////////
+
+ @Override
+ public void onPrepared(IMediaPlayer iMediaPlayer) {
+ isPrepar = true;
+ iMediaCallback.onPrepared(this);
+ }
+
+ @Override
+ public void onBufferingUpdate(IMediaPlayer iMediaPlayer, int i) {
+ iMediaCallback.onBufferingUpdate(this, i / 100.0f);
+ }
+
+ @Override
+ public void onCompletion(IMediaPlayer iMediaPlayer) {
+ iMediaCallback.onCompletion(this);
+ }
+
+ @Override
+ public boolean onError(IMediaPlayer iMediaPlayer, int what, int extra) {
+ isPrepar = false;
+ iMediaCallback.onError(this, what, extra);
+ return true;
+ }
+
+ @Override
+ public boolean onInfo(IMediaPlayer iMediaPlayer, int what, int extra) {
+ iMediaCallback.onInfo(this, what, extra);
+ return false;
+ }
+
+
+ @Override
+ public void onSeekComplete(IMediaPlayer iMediaPlayer) {
+ iMediaCallback.onSeekComplete(this);
+ }
+
+ @Override
+ public void onVideoSizeChanged(IMediaPlayer iMediaPlayer, int width, int height, int i2, int i3) {
+ iMediaCallback.onVideoSizeChanged(this, width, height);
+ }
+}
\ No newline at end of file
diff --git a/qsvideoplayer/src/main/java/org/song/videoplayer/media/IjkExoMedia.java b/qsvideoplayer/src/main/java/org/song/videoplayer/media/IjkExoMedia.java
new file mode 100644
index 0000000..809e915
--- /dev/null
+++ b/qsvideoplayer/src/main/java/org/song/videoplayer/media/IjkExoMedia.java
@@ -0,0 +1,46 @@
+package org.song.videoplayer.media;
+
+import android.content.Context;
+import android.net.Uri;
+
+import java.util.Map;
+
+import tv.danmaku.ijk.media.exo.IjkExoMediaPlayer;
+import tv.danmaku.ijk.media.player.IMediaPlayer;
+
+/**
+ * Created by song on 2017/2/22.
+ */
+
+public class IjkExoMedia extends IjkBaseMedia {
+
+ public IjkExoMedia(IMediaCallback iMediaCallback) {
+ super(iMediaCallback);
+ }
+
+ @Override
+ IMediaPlayer getMedia(Context context, String url, Map headers) throws Exception {
+ IjkExoMediaPlayer mediaPlayer = new IjkExoMediaPlayer(context);
+ mediaPlayer.setDataSource(context, Uri.parse(url), headers);
+ mainThreadHandler.postDelayed(runnable, 500);
+ return mediaPlayer;
+ }
+
+ Runnable runnable = new Runnable() {
+ @Override
+ public void run() {
+ if (isPrepar)
+ onBufferingUpdate(mediaPlayer, ((IjkExoMediaPlayer) mediaPlayer).getBufferedPercentage());
+ mainThreadHandler.postDelayed(runnable, 1000);
+ }
+ };
+
+
+ @Override
+ public void release() {
+ super.release();
+ mainThreadHandler.removeCallbacks(runnable);
+ }
+
+
+}
diff --git a/qsvideoplayer/src/main/java/org/song/videoplayer/media/IjkMedia.java b/qsvideoplayer/src/main/java/org/song/videoplayer/media/IjkMedia.java
new file mode 100644
index 0000000..d2cb474
--- /dev/null
+++ b/qsvideoplayer/src/main/java/org/song/videoplayer/media/IjkMedia.java
@@ -0,0 +1,62 @@
+package org.song.videoplayer.media;
+
+import android.content.Context;
+import android.media.AudioManager;
+import android.text.TextUtils;
+import android.view.Surface;
+import android.view.SurfaceHolder;
+
+import java.util.Map;
+
+import tv.danmaku.ijk.media.player.IMediaPlayer;
+import tv.danmaku.ijk.media.player.IjkMediaPlayer;
+
+/**
+ * Created by song on 2017/2/22.
+ * 哔哩哔哩播放器
+ */
+
+public class IjkMedia extends IjkBaseMedia {
+
+ public IjkMedia(IMediaCallback iMediaCallback) {
+ super(iMediaCallback);
+ }
+
+ @Override
+ IMediaPlayer getMedia(Context context, String url, Map headers) throws Exception {
+ IjkMediaPlayer mediaPlayer = new IjkMediaPlayer();
+ mediaPlayer.setDataSource(url, headers);
+
+
+// if (mSettings.getUsingMediaCodec()) {
+// mediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec", 1);
+// if (mSettings.getUsingMediaCodecAutoRotate()) {
+// mediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec-auto-rotate", 1);
+// } else {
+// mediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec-auto-rotate", 0);
+// }
+// } else {
+// mediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec", 0);
+// }
+//
+// if (mSettings.getUsingOpenSLES()) {
+// mediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "opensles", 1);
+// } else {
+// mediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "opensles", 0);
+// }
+//
+// String pixelFormat = mSettings.getPixelFormat();
+// if (TextUtils.isEmpty(pixelFormat)) {
+// mediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "overlay-format", IjkMediaPlayer.SDL_FCC_RV32);
+// } else {
+// mediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "overlay-format", pixelFormat);
+// }
+// mediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "framedrop", 1);
+// mediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "start-on-prepared", 0);
+//
+// mediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "http-detect-range-support", 0);
+//
+// mediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_CODEC, "skip_loop_filter", 48);
+ return mediaPlayer;
+ }
+}
diff --git a/qsvideoplayer/src/main/java/org/song/videoplayer/rederview/IRenderView.java b/qsvideoplayer/src/main/java/org/song/videoplayer/rederview/IRenderView.java
new file mode 100644
index 0000000..41a7a1b
--- /dev/null
+++ b/qsvideoplayer/src/main/java/org/song/videoplayer/rederview/IRenderView.java
@@ -0,0 +1,50 @@
+package org.song.videoplayer.rederview;
+
+import android.view.Surface;
+import android.view.SurfaceHolder;
+import android.view.View;
+
+import org.song.videoplayer.media.IMediaControl;
+
+/**
+ * 接口化渲染view
+ */
+public interface IRenderView {
+ int AR_ASPECT_FIT_PARENT = 0; // without clip
+ int AR_ASPECT_FILL_PARENT = 1; // may clip
+ int AR_ASPECT_WRAP_CONTENT = 2;
+ int AR_MATCH_PARENT = 3;
+ int AR_16_9_FIT_PARENT = 4;
+ int AR_4_3_FIT_PARENT = 5;
+
+ //boolean shouldWaitForResize();
+
+ View get();
+
+ void setVideoSize(int videoWidth, int videoHeight);
+
+
+ //void setVideoSampleAspectRatio(int videoSarNum, int videoSarDen);
+
+ void setVideoRotation(int degree);
+
+ //void setAspectRatio(int aspectRatio);
+
+ void addRenderCallback(IRenderCallback callback);
+
+ void removeRenderCallback();
+
+ SurfaceHolder getSurfaceHolder();
+
+ Surface openSurface();
+
+ void bindMedia(IMediaControl iMediaControl);
+
+ interface IRenderCallback {
+ void onSurfaceCreated(IRenderView holder, int width, int height);
+
+ void onSurfaceChanged(IRenderView holder, int format, int width, int height);
+
+ void onSurfaceDestroyed(IRenderView holder);
+ }
+}
diff --git a/qsvideoplayer/src/main/java/org/song/videoplayer/rederview/SufaceRenderView.java b/qsvideoplayer/src/main/java/org/song/videoplayer/rederview/SufaceRenderView.java
new file mode 100644
index 0000000..37c4674
--- /dev/null
+++ b/qsvideoplayer/src/main/java/org/song/videoplayer/rederview/SufaceRenderView.java
@@ -0,0 +1,203 @@
+package org.song.videoplayer.rederview;
+
+import android.annotation.TargetApi;
+import android.content.Context;
+import android.os.Build;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.Surface;
+import android.view.SurfaceHolder;
+import android.view.SurfaceView;
+import android.view.View;
+
+import org.song.videoplayer.media.IMediaControl;
+
+/**
+ * 小于4.0用这个绘制view
+ * ps:切换全屏效果不好 会停顿
+ */
+public class SufaceRenderView extends SurfaceView implements SurfaceHolder.Callback, IRenderView {
+
+ protected static final String TAG = "SufaceRenderView";
+
+ int videoWidth;
+ int videoHeight;
+
+ private IRenderCallback callback;
+
+ public SufaceRenderView(Context context) {
+ super(context);
+ init();
+ }
+
+ public SufaceRenderView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ init();
+ }
+
+ private void init() {
+ getHolder().addCallback(this);
+ }
+
+
+ @Override
+ public View get() {
+ return this;
+ }
+
+ @Override
+ public void setVideoSize(int videoWidth, int videoHeight) {
+ if (videoWidth >= 0 && videoHeight >= 0) {
+ this.videoWidth = videoWidth;
+ this.videoHeight = videoHeight;
+ if (holder != null)
+ holder.setFixedSize(videoWidth, videoHeight);
+
+ requestLayout();
+ }
+ }
+
+
+ @Override
+ public void setVideoRotation(int degree) {
+// if (degree != getRotation()) {
+// super.setRotation(degree);
+// requestLayout();
+// }
+ }
+
+ @Override
+ public void addRenderCallback(IRenderCallback callback) {
+ this.callback = callback;
+ }
+
+ @Override
+ public void removeRenderCallback() {
+ this.callback = null;
+ }
+
+
+ @Override
+ public SurfaceHolder getSurfaceHolder() {
+ return holder;
+ }
+
+ @Override
+ public Surface openSurface() {
+ if (holder != null)
+ return holder.getSurface();
+ return null;
+ }
+
+ @Override
+ public void bindMedia(IMediaControl iMediaControl) {
+ //iMediaControl.setDisplay(null);
+ holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
+ iMediaControl.setDisplay(getSurfaceHolder());
+ }
+
+ private SurfaceHolder holder;
+
+
+ @Override
+ public void surfaceCreated(SurfaceHolder holder) {
+ Log.i(TAG, "surfaceCreated");
+ this.holder = holder;
+ if (callback != null)
+ callback.onSurfaceCreated(this, 0, 0);
+ }
+
+ @Override
+ public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
+ Log.i(TAG, "surfaceChanged " + width + "-" + height);
+ if (callback != null)
+ callback.onSurfaceChanged(this, 0, width, height);
+ }
+
+ @Override
+ public void surfaceDestroyed(SurfaceHolder holder) {
+ Log.i(TAG, "surfaceDestroyed");
+ if (callback != null)
+ callback.onSurfaceDestroyed(this);
+
+ }
+
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ Log.i(TAG, "onMeasure " + " [" + this.hashCode() + "] ");
+ int viewRotation = 0;//(int) getRotation();
+
+ Log.i(TAG, "videoWidth = " + videoWidth + ", " + "videoHeight = " + videoHeight);
+ Log.i(TAG, "viewRotation = " + viewRotation);
+
+ // 如果判断成立,则说明显示的TextureView和本身的位置是有90度的旋转的,所以需要交换宽高参数。
+ if (viewRotation == 90 || viewRotation == 270) {
+ int tempMeasureSpec = widthMeasureSpec;
+ widthMeasureSpec = heightMeasureSpec;
+ heightMeasureSpec = tempMeasureSpec;
+ }
+
+ int width = getDefaultSize(videoWidth, widthMeasureSpec);
+ int height = getDefaultSize(videoHeight, heightMeasureSpec);
+ if (videoWidth > 0 && videoHeight > 0) {
+
+ int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);
+ int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec);
+ int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);
+ int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec);
+
+ Log.i(TAG, "widthMeasureSpec [" + MeasureSpec.toString(widthMeasureSpec) + "]");
+ Log.i(TAG, "heightMeasureSpec [" + MeasureSpec.toString(heightMeasureSpec) + "]");
+
+ if (widthSpecMode == MeasureSpec.EXACTLY && heightSpecMode == MeasureSpec.EXACTLY) {
+ // the size is fixed
+ width = widthSpecSize;
+ height = heightSpecSize;
+ // for compatibility, we adjust size based on aspect ratio
+ if (videoWidth * height < width * videoHeight) {
+ width = height * videoWidth / videoHeight;
+ } else if (videoWidth * height > width * videoHeight) {
+ height = width * videoHeight / videoWidth;
+ }
+ } else if (widthSpecMode == MeasureSpec.EXACTLY) {
+ // only the width is fixed, adjust the height to match aspect ratio if possible
+ width = widthSpecSize;
+ height = width * videoHeight / videoWidth;
+ if (heightSpecMode == MeasureSpec.AT_MOST && height > heightSpecSize) {
+ // couldn't match aspect ratio within the constraints
+ height = heightSpecSize;
+ width = height * videoWidth / videoHeight;
+ }
+ } else if (heightSpecMode == MeasureSpec.EXACTLY) {
+ // only the height is fixed, adjust the width to match aspect ratio if possible
+ height = heightSpecSize;
+ width = height * videoWidth / videoHeight;
+ if (widthSpecMode == MeasureSpec.AT_MOST && width > widthSpecSize) {
+ // couldn't match aspect ratio within the constraints
+ width = widthSpecSize;
+ height = width * videoHeight / videoWidth;
+ }
+ } else {
+ // neither the width nor the height are fixed, try to use actual video size
+ width = videoWidth;
+ height = videoHeight;
+ if (heightSpecMode == MeasureSpec.AT_MOST && height > heightSpecSize) {
+ // too tall, decrease both width and height
+ height = heightSpecSize;
+ width = height * videoWidth / videoHeight;
+ }
+ if (widthSpecMode == MeasureSpec.AT_MOST && width > widthSpecSize) {
+ // too wide, decrease both width and height
+ width = widthSpecSize;
+ height = width * videoHeight / videoWidth;
+ }
+ }
+ } else {
+ // no size yet, just adopt the given spec sizes
+ }
+ setMeasuredDimension(width, height);
+ }
+
+
+}
diff --git a/qsvideoplayer/src/main/java/org/song/videoplayer/rederview/TextureRenderView.java b/qsvideoplayer/src/main/java/org/song/videoplayer/rederview/TextureRenderView.java
new file mode 100644
index 0000000..fb34cbb
--- /dev/null
+++ b/qsvideoplayer/src/main/java/org/song/videoplayer/rederview/TextureRenderView.java
@@ -0,0 +1,208 @@
+package org.song.videoplayer.rederview;
+
+import android.annotation.TargetApi;
+import android.content.Context;
+import android.graphics.SurfaceTexture;
+import android.os.Build;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.Surface;
+import android.view.SurfaceHolder;
+import android.view.TextureView;
+import android.view.View;
+
+import org.song.videoplayer.media.IMediaControl;
+
+/**
+ * 大于4.0用这个绘制view
+ */
+@TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
+public class TextureRenderView extends TextureView implements TextureView.SurfaceTextureListener, IRenderView {
+
+ protected static final String TAG = "TextureRenderView";
+
+ int videoWidth;
+ int videoHeight;
+
+ private IRenderCallback callback;
+
+ public TextureRenderView(Context context) {
+ super(context);
+ init();
+ }
+
+ public TextureRenderView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ init();
+ }
+
+ private void init() {
+ setSurfaceTextureListener(this);
+ }
+
+
+ @Override
+ public View get() {
+ return this;
+ }
+
+ @Override
+ public void setVideoSize(int videoWidth, int videoHeight) {
+ if (videoWidth >= 0 && videoHeight >= 0) {
+ this.videoWidth = videoWidth;
+ this.videoHeight = videoHeight;
+ requestLayout();
+ }
+ }
+
+ @Override
+ public SurfaceHolder getSurfaceHolder() {
+ return null;
+ }
+
+ @Override
+ public void setVideoRotation(int degree) {
+ if (degree != getRotation()) {
+ super.setRotation(degree);
+ requestLayout();
+ }
+ }
+
+ @Override
+ public void addRenderCallback(IRenderCallback callback) {
+ this.callback = callback;
+ }
+
+ @Override
+ public void removeRenderCallback() {
+ this.callback = null;
+ }
+
+ @Override
+ public Surface openSurface() {
+ if (surface != null)
+ return new Surface(surface);
+ return null;
+ }
+
+ @Override
+ public void bindMedia(IMediaControl iMediaControl) {
+ iMediaControl.setSurface(openSurface());
+ }
+
+ private SurfaceTexture surface;
+
+ @TargetApi(16)
+ @Override//改变大小时会重建Surface
+ public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
+ Log.i(TAG, "onSurfaceTextureAvailable " + width + "-" + height);
+ if (this.surface == null || Build.VERSION.SDK_INT < 16) {
+ this.surface = surface;
+ if (callback != null)
+ callback.onSurfaceCreated(this, width, height);
+ } else {
+ //当api大于16 不用重新set解码器的suface 把原来的更新进view就行
+ setSurfaceTexture(this.surface);
+ }
+
+ }
+
+ @Override
+ public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
+ Log.i(TAG, "onSurfaceTextureSizeChanged " + width + "-" + height);
+ if (callback != null)
+ callback.onSurfaceChanged(this, 0, width, height);
+ }
+
+ @Override//返回值待研究...
+ public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
+ Log.i(TAG, "onSurfaceTextureDestroyed");
+ if (callback != null)
+ callback.onSurfaceDestroyed(this);
+ return this.surface == null;
+ }
+
+ @Override
+ public void onSurfaceTextureUpdated(SurfaceTexture surface) {
+ //Log.i(TAG, "onSurfaceTextureUpdated");
+ }
+
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ Log.i(TAG, "onMeasure " + " [" + this.hashCode() + "] ");
+ int viewRotation = (int) getRotation();
+
+ Log.i(TAG, "videoWidth = " + videoWidth + ", " + "videoHeight = " + videoHeight);
+ Log.i(TAG, "viewRotation = " + viewRotation);
+
+ // 如果判断成立,则说明显示的TextureView和本身的位置是有90度的旋转的,所以需要交换宽高参数。
+ if (viewRotation == 90 || viewRotation == 270) {
+ int tempMeasureSpec = widthMeasureSpec;
+ widthMeasureSpec = heightMeasureSpec;
+ heightMeasureSpec = tempMeasureSpec;
+ }
+
+ int width = getDefaultSize(videoWidth, widthMeasureSpec);
+ int height = getDefaultSize(videoHeight, heightMeasureSpec);
+ if (videoWidth > 0 && videoHeight > 0) {
+
+ int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);
+ int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec);
+ int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);
+ int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec);
+
+ Log.i(TAG, "widthMeasureSpec [" + MeasureSpec.toString(widthMeasureSpec) + "]");
+ Log.i(TAG, "heightMeasureSpec [" + MeasureSpec.toString(heightMeasureSpec) + "]");
+
+ if (widthSpecMode == MeasureSpec.EXACTLY && heightSpecMode == MeasureSpec.EXACTLY) {
+ // the size is fixed
+ width = widthSpecSize;
+ height = heightSpecSize;
+ // for compatibility, we adjust size based on aspect ratio
+ if (videoWidth * height < width * videoHeight) {
+ width = height * videoWidth / videoHeight;
+ } else if (videoWidth * height > width * videoHeight) {
+ height = width * videoHeight / videoWidth;
+ }
+ } else if (widthSpecMode == MeasureSpec.EXACTLY) {
+ // only the width is fixed, adjust the height to match aspect ratio if possible
+ width = widthSpecSize;
+ height = width * videoHeight / videoWidth;
+ if (heightSpecMode == MeasureSpec.AT_MOST && height > heightSpecSize) {
+ // couldn't match aspect ratio within the constraints
+ height = heightSpecSize;
+ width = height * videoWidth / videoHeight;
+ }
+ } else if (heightSpecMode == MeasureSpec.EXACTLY) {
+ // only the height is fixed, adjust the width to match aspect ratio if possible
+ height = heightSpecSize;
+ width = height * videoWidth / videoHeight;
+ if (widthSpecMode == MeasureSpec.AT_MOST && width > widthSpecSize) {
+ // couldn't match aspect ratio within the constraints
+ width = widthSpecSize;
+ height = width * videoHeight / videoWidth;
+ }
+ } else {
+ // neither the width nor the height are fixed, try to use actual video size
+ width = videoWidth;
+ height = videoHeight;
+ if (heightSpecMode == MeasureSpec.AT_MOST && height > heightSpecSize) {
+ // too tall, decrease both width and height
+ height = heightSpecSize;
+ width = height * videoWidth / videoHeight;
+ }
+ if (widthSpecMode == MeasureSpec.AT_MOST && width > widthSpecSize) {
+ // too wide, decrease both width and height
+ width = widthSpecSize;
+ height = width * videoHeight / videoWidth;
+ }
+ }
+ } else {
+ // no size yet, just adopt the given spec sizes
+ }
+ setMeasuredDimension(width, height);
+ }
+
+
+}
diff --git a/qsvideoplayer/src/main/res/drawable-xhdpi/jc_back.png b/qsvideoplayer/src/main/res/drawable-xhdpi/jc_back.png
new file mode 100644
index 0000000..65ecff6
Binary files /dev/null and b/qsvideoplayer/src/main/res/drawable-xhdpi/jc_back.png differ
diff --git a/qsvideoplayer/src/main/res/drawable-xhdpi/jc_back_tiny_normal.png b/qsvideoplayer/src/main/res/drawable-xhdpi/jc_back_tiny_normal.png
new file mode 100644
index 0000000..e6ca6f4
Binary files /dev/null and b/qsvideoplayer/src/main/res/drawable-xhdpi/jc_back_tiny_normal.png differ
diff --git a/qsvideoplayer/src/main/res/drawable-xhdpi/jc_back_tiny_pressed.png b/qsvideoplayer/src/main/res/drawable-xhdpi/jc_back_tiny_pressed.png
new file mode 100644
index 0000000..2ca8252
Binary files /dev/null and b/qsvideoplayer/src/main/res/drawable-xhdpi/jc_back_tiny_pressed.png differ
diff --git a/qsvideoplayer/src/main/res/drawable-xhdpi/jc_backward_icon.png b/qsvideoplayer/src/main/res/drawable-xhdpi/jc_backward_icon.png
new file mode 100644
index 0000000..51f5ca3
Binary files /dev/null and b/qsvideoplayer/src/main/res/drawable-xhdpi/jc_backward_icon.png differ
diff --git a/qsvideoplayer/src/main/res/drawable-xhdpi/jc_brightness_video.png b/qsvideoplayer/src/main/res/drawable-xhdpi/jc_brightness_video.png
new file mode 100644
index 0000000..df25316
Binary files /dev/null and b/qsvideoplayer/src/main/res/drawable-xhdpi/jc_brightness_video.png differ
diff --git a/qsvideoplayer/src/main/res/drawable-xhdpi/jc_enlarge.png b/qsvideoplayer/src/main/res/drawable-xhdpi/jc_enlarge.png
new file mode 100644
index 0000000..dcf9b88
Binary files /dev/null and b/qsvideoplayer/src/main/res/drawable-xhdpi/jc_enlarge.png differ
diff --git a/qsvideoplayer/src/main/res/drawable-xhdpi/jc_error_normal.png b/qsvideoplayer/src/main/res/drawable-xhdpi/jc_error_normal.png
new file mode 100644
index 0000000..b588160
Binary files /dev/null and b/qsvideoplayer/src/main/res/drawable-xhdpi/jc_error_normal.png differ
diff --git a/qsvideoplayer/src/main/res/drawable-xhdpi/jc_error_pressed.png b/qsvideoplayer/src/main/res/drawable-xhdpi/jc_error_pressed.png
new file mode 100644
index 0000000..1af4fc2
Binary files /dev/null and b/qsvideoplayer/src/main/res/drawable-xhdpi/jc_error_pressed.png differ
diff --git a/qsvideoplayer/src/main/res/drawable-xhdpi/jc_forward_icon.png b/qsvideoplayer/src/main/res/drawable-xhdpi/jc_forward_icon.png
new file mode 100644
index 0000000..f7e3188
Binary files /dev/null and b/qsvideoplayer/src/main/res/drawable-xhdpi/jc_forward_icon.png differ
diff --git a/qsvideoplayer/src/main/res/drawable-xhdpi/jc_loading_bg.png b/qsvideoplayer/src/main/res/drawable-xhdpi/jc_loading_bg.png
new file mode 100644
index 0000000..bc811b1
Binary files /dev/null and b/qsvideoplayer/src/main/res/drawable-xhdpi/jc_loading_bg.png differ
diff --git a/qsvideoplayer/src/main/res/drawable-xhdpi/jc_pause_normal.png b/qsvideoplayer/src/main/res/drawable-xhdpi/jc_pause_normal.png
new file mode 100644
index 0000000..4a6ae51
Binary files /dev/null and b/qsvideoplayer/src/main/res/drawable-xhdpi/jc_pause_normal.png differ
diff --git a/qsvideoplayer/src/main/res/drawable-xhdpi/jc_pause_pressed.png b/qsvideoplayer/src/main/res/drawable-xhdpi/jc_pause_pressed.png
new file mode 100644
index 0000000..0bcc5c4
Binary files /dev/null and b/qsvideoplayer/src/main/res/drawable-xhdpi/jc_pause_pressed.png differ
diff --git a/qsvideoplayer/src/main/res/drawable-xhdpi/jc_play_normal.png b/qsvideoplayer/src/main/res/drawable-xhdpi/jc_play_normal.png
new file mode 100644
index 0000000..a2343d4
Binary files /dev/null and b/qsvideoplayer/src/main/res/drawable-xhdpi/jc_play_normal.png differ
diff --git a/qsvideoplayer/src/main/res/drawable-xhdpi/jc_play_pressed.png b/qsvideoplayer/src/main/res/drawable-xhdpi/jc_play_pressed.png
new file mode 100644
index 0000000..67af103
Binary files /dev/null and b/qsvideoplayer/src/main/res/drawable-xhdpi/jc_play_pressed.png differ
diff --git a/qsvideoplayer/src/main/res/drawable-xhdpi/jc_shrink.png b/qsvideoplayer/src/main/res/drawable-xhdpi/jc_shrink.png
new file mode 100644
index 0000000..c600c81
Binary files /dev/null and b/qsvideoplayer/src/main/res/drawable-xhdpi/jc_shrink.png differ
diff --git a/qsvideoplayer/src/main/res/drawable-xhdpi/jc_volume_icon.png b/qsvideoplayer/src/main/res/drawable-xhdpi/jc_volume_icon.png
new file mode 100644
index 0000000..deb5358
Binary files /dev/null and b/qsvideoplayer/src/main/res/drawable-xhdpi/jc_volume_icon.png differ
diff --git a/qsvideoplayer/src/main/res/drawable/bottom_backgroud.xml b/qsvideoplayer/src/main/res/drawable/bottom_backgroud.xml
new file mode 100644
index 0000000..4cc0231
--- /dev/null
+++ b/qsvideoplayer/src/main/res/drawable/bottom_backgroud.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/qsvideoplayer/src/main/res/drawable/jc_click_back_tiny_selector.xml b/qsvideoplayer/src/main/res/drawable/jc_click_back_tiny_selector.xml
new file mode 100644
index 0000000..e79a912
--- /dev/null
+++ b/qsvideoplayer/src/main/res/drawable/jc_click_back_tiny_selector.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
diff --git a/qsvideoplayer/src/main/res/drawable/jc_click_error_selector.xml b/qsvideoplayer/src/main/res/drawable/jc_click_error_selector.xml
new file mode 100644
index 0000000..94914ea
--- /dev/null
+++ b/qsvideoplayer/src/main/res/drawable/jc_click_error_selector.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/qsvideoplayer/src/main/res/drawable/jc_click_pause_selector.xml b/qsvideoplayer/src/main/res/drawable/jc_click_pause_selector.xml
new file mode 100644
index 0000000..a417704
--- /dev/null
+++ b/qsvideoplayer/src/main/res/drawable/jc_click_pause_selector.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
diff --git a/qsvideoplayer/src/main/res/drawable/jc_click_play_selector.xml b/qsvideoplayer/src/main/res/drawable/jc_click_play_selector.xml
new file mode 100644
index 0000000..f9f42fe
--- /dev/null
+++ b/qsvideoplayer/src/main/res/drawable/jc_click_play_selector.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/qsvideoplayer/src/main/res/drawable/jc_dialog_progress.xml b/qsvideoplayer/src/main/res/drawable/jc_dialog_progress.xml
new file mode 100644
index 0000000..6a30b01
--- /dev/null
+++ b/qsvideoplayer/src/main/res/drawable/jc_dialog_progress.xml
@@ -0,0 +1,17 @@
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+
+
+
diff --git a/qsvideoplayer/src/main/res/drawable/jc_dialog_progress_bg.xml b/qsvideoplayer/src/main/res/drawable/jc_dialog_progress_bg.xml
new file mode 100644
index 0000000..8ab0b44
--- /dev/null
+++ b/qsvideoplayer/src/main/res/drawable/jc_dialog_progress_bg.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/qsvideoplayer/src/main/res/drawable/jc_loading.xml b/qsvideoplayer/src/main/res/drawable/jc_loading.xml
new file mode 100644
index 0000000..9ee8e9b
--- /dev/null
+++ b/qsvideoplayer/src/main/res/drawable/jc_loading.xml
@@ -0,0 +1,7 @@
+
+
diff --git a/qsvideoplayer/src/main/res/drawable/jc_progress.xml b/qsvideoplayer/src/main/res/drawable/jc_progress.xml
new file mode 100644
index 0000000..6a610e9
--- /dev/null
+++ b/qsvideoplayer/src/main/res/drawable/jc_progress.xml
@@ -0,0 +1,28 @@
+
+
+ -
+
+
+
+
+
+
+ -
+
+
+
+
+
+
+
+
+ -
+
+
+
+
+
+
+
+
+
diff --git a/qsvideoplayer/src/main/res/drawable/jc_seek_progress.xml b/qsvideoplayer/src/main/res/drawable/jc_seek_progress.xml
new file mode 100644
index 0000000..4b390d1
--- /dev/null
+++ b/qsvideoplayer/src/main/res/drawable/jc_seek_progress.xml
@@ -0,0 +1,28 @@
+
+
+ -
+
+
+
+
+
+
+ -
+
+
+
+
+
+
+
+
+ -
+
+
+
+
+
+
+
+
+
diff --git a/qsvideoplayer/src/main/res/drawable/jc_seek_thumb.xml b/qsvideoplayer/src/main/res/drawable/jc_seek_thumb.xml
new file mode 100644
index 0000000..b4cb947
--- /dev/null
+++ b/qsvideoplayer/src/main/res/drawable/jc_seek_thumb.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/qsvideoplayer/src/main/res/drawable/jc_seek_thumb_normal.xml b/qsvideoplayer/src/main/res/drawable/jc_seek_thumb_normal.xml
new file mode 100644
index 0000000..347dd38
--- /dev/null
+++ b/qsvideoplayer/src/main/res/drawable/jc_seek_thumb_normal.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
diff --git a/qsvideoplayer/src/main/res/drawable/jc_seek_thumb_pressed.xml b/qsvideoplayer/src/main/res/drawable/jc_seek_thumb_pressed.xml
new file mode 100644
index 0000000..23a89e2
--- /dev/null
+++ b/qsvideoplayer/src/main/res/drawable/jc_seek_thumb_pressed.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
diff --git a/qsvideoplayer/src/main/res/drawable/jc_volume_progress_bg.xml b/qsvideoplayer/src/main/res/drawable/jc_volume_progress_bg.xml
new file mode 100644
index 0000000..7b6da09
--- /dev/null
+++ b/qsvideoplayer/src/main/res/drawable/jc_volume_progress_bg.xml
@@ -0,0 +1,19 @@
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+
+
+
diff --git a/qsvideoplayer/src/main/res/drawable/top_backgroud.xml b/qsvideoplayer/src/main/res/drawable/top_backgroud.xml
new file mode 100644
index 0000000..afd8a77
--- /dev/null
+++ b/qsvideoplayer/src/main/res/drawable/top_backgroud.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/qsvideoplayer/src/main/res/layout/jc_dialog_brightness.xml b/qsvideoplayer/src/main/res/layout/jc_dialog_brightness.xml
new file mode 100644
index 0000000..d4122e0
--- /dev/null
+++ b/qsvideoplayer/src/main/res/layout/jc_dialog_brightness.xml
@@ -0,0 +1,43 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/qsvideoplayer/src/main/res/layout/jc_dialog_progress.xml b/qsvideoplayer/src/main/res/layout/jc_dialog_progress.xml
new file mode 100644
index 0000000..be83df9
--- /dev/null
+++ b/qsvideoplayer/src/main/res/layout/jc_dialog_progress.xml
@@ -0,0 +1,64 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/qsvideoplayer/src/main/res/layout/jc_dialog_volume.xml b/qsvideoplayer/src/main/res/layout/jc_dialog_volume.xml
new file mode 100644
index 0000000..9eeb487
--- /dev/null
+++ b/qsvideoplayer/src/main/res/layout/jc_dialog_volume.xml
@@ -0,0 +1,44 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/qsvideoplayer/src/main/res/layout/video_view.xml b/qsvideoplayer/src/main/res/layout/video_view.xml
new file mode 100644
index 0000000..259f4de
--- /dev/null
+++ b/qsvideoplayer/src/main/res/layout/video_view.xml
@@ -0,0 +1,147 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/qsvideoplayer/src/main/res/values-pt/strings.xml b/qsvideoplayer/src/main/res/values-pt/strings.xml
new file mode 100644
index 0000000..de281bc
--- /dev/null
+++ b/qsvideoplayer/src/main/res/values-pt/strings.xml
@@ -0,0 +1,7 @@
+
+
+ Você está usando a rede móvel, você deseja mesmo ver o video?
+ Continuar
+ Parar
+ Sem Vídeo
+
diff --git a/qsvideoplayer/src/main/res/values-tr/strings.xml b/qsvideoplayer/src/main/res/values-tr/strings.xml
new file mode 100644
index 0000000..267f06e
--- /dev/null
+++ b/qsvideoplayer/src/main/res/values-tr/strings.xml
@@ -0,0 +1,7 @@
+
+
+ Şu anda mobil veriyi kullanıyorsunuz, yüksek veri kaybına yol açabilir
+ Devam Et
+ Durdur
+ URL Bulunamadı
+
diff --git a/qsvideoplayer/src/main/res/values-zh/strings.xml b/qsvideoplayer/src/main/res/values-zh/strings.xml
new file mode 100644
index 0000000..2792ea2
--- /dev/null
+++ b/qsvideoplayer/src/main/res/values-zh/strings.xml
@@ -0,0 +1,7 @@
+
+
+ 您当前正在使用移动网络,继续播放将消耗流量
+ 继续播放
+ 停止播放
+ 播放地址无效
+
diff --git a/qsvideoplayer/src/main/res/values/colors.xml b/qsvideoplayer/src/main/res/values/colors.xml
new file mode 100644
index 0000000..d002bd5
--- /dev/null
+++ b/qsvideoplayer/src/main/res/values/colors.xml
@@ -0,0 +1,4 @@
+
+
+ #2CA298
+
diff --git a/qsvideoplayer/src/main/res/values/dimens.xml b/qsvideoplayer/src/main/res/values/dimens.xml
new file mode 100644
index 0000000..7abc06d
--- /dev/null
+++ b/qsvideoplayer/src/main/res/values/dimens.xml
@@ -0,0 +1 @@
+
diff --git a/qsvideoplayer/src/main/res/values/ids.xml b/qsvideoplayer/src/main/res/values/ids.xml
new file mode 100644
index 0000000..efc224b
--- /dev/null
+++ b/qsvideoplayer/src/main/res/values/ids.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/qsvideoplayer/src/main/res/values/strings.xml b/qsvideoplayer/src/main/res/values/strings.xml
new file mode 100644
index 0000000..e7fddf0
--- /dev/null
+++ b/qsvideoplayer/src/main/res/values/strings.xml
@@ -0,0 +1,7 @@
+
+
+ You are currently using the mobile network, the player will continue to consume traffic
+ Resume
+ Stop play
+ No mUrl
+
diff --git a/qsvideoplayer/src/main/res/values/styles.xml b/qsvideoplayer/src/main/res/values/styles.xml
new file mode 100644
index 0000000..236b5a1
--- /dev/null
+++ b/qsvideoplayer/src/main/res/values/styles.xml
@@ -0,0 +1,26 @@
+
+
+
+
+
+
+
+
diff --git a/settings.gradle b/settings.gradle
new file mode 100644
index 0000000..382e279
--- /dev/null
+++ b/settings.gradle
@@ -0,0 +1 @@
+include ':app',':qsvideoplayer'