├── .gitignore
├── LICENSE
├── README.md
├── VERSIONS.md
├── app
├── .gitignore
├── build.gradle
├── proguard-rules.pro
└── src
│ ├── androidTest
│ └── java
│ │ └── tcking
│ │ └── github
│ │ └── com
│ │ └── giraffeplayer
│ │ └── ApplicationTest.java
│ ├── main
│ ├── AndroidManifest.xml
│ ├── assets
│ │ ├── sample.json
│ │ └── sample2.json
│ ├── java
│ │ └── tcking
│ │ │ └── github
│ │ │ └── com
│ │ │ └── giraffeplayer
│ │ │ └── example
│ │ │ ├── ListExample2Activity.java
│ │ │ ├── ListExampleActivity.java
│ │ │ ├── MainActivity.java
│ │ │ ├── MainFragment.java
│ │ │ ├── SimpleMediaController.java
│ │ │ └── VideoAdapter.java
│ └── res
│ │ ├── drawable
│ │ ├── cover1.jpeg
│ │ ├── loading.png
│ │ ├── pause.png
│ │ └── progress_loading.xml
│ │ ├── layout
│ │ ├── activity_list.xml
│ │ ├── fragment_main.xml
│ │ ├── item_comment.xml
│ │ ├── item_video.xml
│ │ └── simple_media_controller.xml
│ │ ├── mipmap-hdpi
│ │ └── ic_launcher.png
│ │ ├── mipmap-mdpi
│ │ └── ic_launcher.png
│ │ ├── mipmap-xhdpi
│ │ └── ic_launcher.png
│ │ ├── mipmap-xxhdpi
│ │ └── ic_launcher.png
│ │ ├── mipmap-xxxhdpi
│ │ └── ic_launcher.png
│ │ ├── values-w820dp
│ │ └── dimens.xml
│ │ └── values
│ │ ├── colors.xml
│ │ ├── dimens.xml
│ │ ├── strings.xml
│ │ └── styles.xml
│ └── test
│ └── java
│ └── tcking
│ └── github
│ └── com
│ └── giraffeplayer
│ └── ExampleUnitTest.java
├── build.gradle
├── demo
└── demo.apk
├── giraffeplayer2
├── .gitignore
├── build.gradle
├── gradle-on-demand.gradle
├── proguard-rules.pro
├── project.properties
├── publish.gradle
└── src
│ ├── androidTest
│ └── java
│ │ └── tcking
│ │ └── github
│ │ └── com
│ │ └── giraffeplayer
│ │ └── ApplicationTest.java
│ ├── main
│ ├── AndroidManifest.xml
│ ├── java
│ │ ├── com
│ │ │ └── github
│ │ │ │ └── kevinsawicki
│ │ │ │ └── http
│ │ │ │ └── HttpRequest.java
│ │ └── tcking
│ │ │ └── github
│ │ │ └── com
│ │ │ └── giraffeplayer2
│ │ │ ├── BaseMediaController.java
│ │ │ ├── BasePlayerActivity.java
│ │ │ ├── DefaultMediaController.java
│ │ │ ├── DefaultPlayerListener.java
│ │ │ ├── GiraffePlayer.java
│ │ │ ├── LazyLoadManager.java
│ │ │ ├── MeasureHelper.java
│ │ │ ├── MediaController.java
│ │ │ ├── Option.java
│ │ │ ├── PlayerActivity.java
│ │ │ ├── PlayerListener.java
│ │ │ ├── PlayerManager.java
│ │ │ ├── ProxyPlayerListener.java
│ │ │ ├── ScalableDisplay.java
│ │ │ ├── ScalableTextureView.java
│ │ │ ├── SoInstaller.java
│ │ │ ├── UIHelper.java
│ │ │ ├── VideoInfo.java
│ │ │ ├── VideoView.java
│ │ │ └── trackselector
│ │ │ ├── TrackGroup.java
│ │ │ ├── TrackInfoWrapper.java
│ │ │ ├── TrackSelectorFragment.java
│ │ │ └── TracksAdapter.java
│ └── res
│ │ ├── drawable-hdpi
│ │ ├── ic_brightness_6_white_36dp.png
│ │ ├── ic_chevron_left_white_36dp.png
│ │ ├── ic_fullscreen_exit_white_24dp.png
│ │ ├── ic_fullscreen_exit_white_36dp.png
│ │ ├── ic_fullscreen_white_24dp.png
│ │ ├── ic_play_arrow_white_24dp.png
│ │ ├── ic_play_circle_outline_white_36dp.png
│ │ ├── ic_stop_white_24dp.png
│ │ ├── ic_volume_off_white_36dp.png
│ │ └── ic_volume_up_white_36dp.png
│ │ ├── drawable-mdpi
│ │ ├── ic_brightness_6_white_36dp.png
│ │ ├── ic_chevron_left_white_36dp.png
│ │ ├── ic_fullscreen_exit_white_24dp.png
│ │ ├── ic_fullscreen_exit_white_36dp.png
│ │ ├── ic_fullscreen_white_24dp.png
│ │ ├── ic_play_arrow_white_24dp.png
│ │ ├── ic_play_circle_outline_white_36dp.png
│ │ ├── ic_stop_white_24dp.png
│ │ ├── ic_volume_off_white_36dp.png
│ │ └── ic_volume_up_white_36dp.png
│ │ ├── drawable-xhdpi
│ │ ├── ic_brightness_6_white_36dp.png
│ │ ├── ic_chevron_left_white_36dp.png
│ │ ├── ic_fullscreen_exit_white_24dp.png
│ │ ├── ic_fullscreen_exit_white_36dp.png
│ │ ├── ic_fullscreen_white_24dp.png
│ │ ├── ic_play_arrow_white_24dp.png
│ │ ├── ic_play_circle_outline_white_36dp.png
│ │ ├── ic_stop_white_24dp.png
│ │ ├── ic_volume_off_white_36dp.png
│ │ └── ic_volume_up_white_36dp.png
│ │ ├── drawable-xxhdpi
│ │ ├── ic_brightness_6_white_36dp.png
│ │ ├── ic_chevron_left_white_36dp.png
│ │ ├── ic_close_white_24dp.png
│ │ ├── ic_fullscreen_exit_white_24dp.png
│ │ ├── ic_fullscreen_exit_white_36dp.png
│ │ ├── ic_fullscreen_white_24dp.png
│ │ ├── ic_play_arrow_white_24dp.png
│ │ ├── ic_play_circle_outline_white_36dp.png
│ │ ├── ic_setting_white_24dp.png
│ │ ├── ic_stop_white_24dp.png
│ │ ├── ic_volume_off_white_36dp.png
│ │ └── ic_volume_up_white_36dp.png
│ │ ├── drawable-xxxhdpi
│ │ ├── ic_brightness_6_white_36dp.png
│ │ ├── ic_chevron_left_white_36dp.png
│ │ ├── ic_fullscreen_exit_white_24dp.png
│ │ ├── ic_fullscreen_exit_white_36dp.png
│ │ ├── ic_fullscreen_white_24dp.png
│ │ ├── ic_play_arrow_white_24dp.png
│ │ ├── ic_play_circle_outline_white_36dp.png
│ │ ├── ic_stop_white_24dp.png
│ │ ├── ic_volume_off_white_36dp.png
│ │ └── ic_volume_up_white_36dp.png
│ │ ├── drawable
│ │ └── app_video_center_bg.xml
│ │ ├── layout
│ │ ├── giraffe_float_box.xml
│ │ ├── giraffe_media_controller.xml
│ │ ├── giraffe_player_activity.xml
│ │ ├── giraffe_track_selector.xml
│ │ ├── giraffe_track_selector_child.xml
│ │ └── giraffe_track_selector_group.xml
│ │ ├── values-ch
│ │ └── strings.xml
│ │ ├── values-es
│ │ └── strings.xml
│ │ ├── values-ro
│ │ └── strings.xml
│ │ ├── values-tr
│ │ └── strings.xml
│ │ └── values
│ │ ├── color.xml
│ │ ├── ids.xml
│ │ └── strings.xml
│ └── test
│ └── java
│ └── tcking
│ └── github
│ └── com
│ └── giraffeplayer
│ └── ExampleUnitTest.java
├── gradle.properties
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── screencap
├── s10.gif
├── s6.gif
└── s7.gif
└── settings.gradle
/.gitignore:
--------------------------------------------------------------------------------
1 | .gradle
2 | /local.properties
3 | /.idea
4 | /.idea/libraries
5 | .DS_Store
6 | /build
7 | /captures
8 | **/*.iml
9 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "{}"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright {yyyy} {name of copyright owner}
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
202 |
203 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # GiraffePlayer2
2 |
3 |
4 | Out of the box android video player base on [ijkplayer 0.8.8](https://github.com/Bilibili/ijkplayer) (for flutter please visit [GPlayer](https://github.com/tcking/GPlayer))
5 |
6 | this project is total refactor of [GiraffePlayer](https://github.com/tcking/GiraffePlayer/) to support in ListView/RecyclerView and improve the performance,all player tasks do in worker thread.
7 |
8 | [release history](https://github.com/tcking/GiraffePlayer2/blob/master/VERSIONS.md)
9 |
10 | [download demo apk (~700kb)](https://raw.githubusercontent.com/tcking/GiraffePlayer2/master/demo/demo.apk)
11 |
12 |
13 | # features
14 | 1. base on ijkplayer,support RTMP , HLS (http & https) , MP4,M4A etc.
15 | 2. gestures for volume control
16 | 3. gestures for brightness control
17 | 4. gestures for forward or backward
18 | 5. fullscreen by manual or sensor (with animation)
19 | 6. try to replay when error(only for live video)
20 | 7. specify video scale type
21 | 8. support in ListView/RecyclerView (in Activity or Fragment)
22 | 9. never block UI thread
23 | 10. support select track
24 | 11. support float window
25 | 12. support lazy load (download player on demand,increase apk size only about 180K)
26 | 13. easy to custom media controller (without change this project source code)
27 |
28 | # how to import library
29 | ``` gradle
30 | //step 1: add jcenter repositories in your root poject build file
31 | repositories {
32 | ...
33 | jcenter()
34 | }
35 |
36 | //step 2: add dependency,there are 3 type of aar,see the flow table
37 | api 'com.github.tcking:giraffeplayer2:0.1.25-lazyLoad'
38 |
39 | ```
40 |
41 | aar type|aar size|decoders|support abi
42 | ----|-----|-----|----
43 | `api 'com.github.tcking:giraffeplayer2:0.1.25'`| 2.2M |common decoders|default armeabi,can add manually
44 | `api 'com.github.tcking:giraffeplayer2:0.1.25-lazyLoad'`| 180K| all decoders|download player(so files) on demand by device abi
45 | `api 'com.github.tcking:giraffeplayer2:0.1.25-full'`| 5.4M | all decoders|default armeabi,can add manually
46 |
47 |
48 | if you using `compile 'com.github.tcking:giraffeplayer2:0.1.25'` or `compile 'com.github.tcking:giraffeplayer2:0.1.25-full'` and want to support more ABI:
49 |
50 |
51 | ``` gradle
52 | //for common decoders
53 | api 'com.github.tcking:ijkplayer-arm64:0.8.8' //support arm64
54 | api 'com.github.tcking:ijkplayer-armv5:0.8.8' //support armv5
55 | api 'com.github.tcking:ijkplayer-x86:0.8.8' //support x86
56 | api 'com.github.tcking:ijkplayer-x86_64:0.8.8' //support x86_64
57 |
58 | //for all decoders
59 | api 'com.github.tcking:ijkplayer-arm64:0.8.8-full' //support arm64
60 | api 'com.github.tcking:ijkplayer-armv5:0.8.8-full' //support armv5
61 | api 'com.github.tcking:ijkplayer-x86:0.8.8-full' //support x86
62 | api 'com.github.tcking:ijkplayer-x86_64:0.8.8-full' //support x86_64
63 |
64 | ```
65 |
66 |
67 |
68 | # How to use ([example code](https://github.com/tcking/GiraffePlayer2/blob/master/app/src/main/java/tcking/github/com/giraffeplayer/example/MainFragment.java))
69 | ## case 1: only want to play a video fullscreen
70 | just call `GiraffePlayer.play(getContext(), new VideoInfo("video url"));`,all is done.
71 |
72 | ## case 2: embed a player in a layout (ListView/RecyclerView)
73 | ### step 1: add `VideoView` in your layout xml file
74 | ``` xml
75 |
76 |
80 |
81 | ```
82 |
83 | ### step 2: get player and play
84 | ``` java
85 |
86 | VideoView videoView = (VideoView) findViewById(R.id.video_view);
87 | videoView.setVideoPath(videoUri).getPlayer().start();
88 |
89 |
90 | ```
91 |
92 | # player in ListView or RecyclerView [example code](https://github.com/tcking/GiraffePlayer2/blob/master/app/src/main/java/tcking/github/com/giraffeplayer/example/ListExampleActivity.java)
93 | in ListView or RecyclerView,you need do one more thing: call `videoView.setFingerprint()`,
94 | the fingerprint is the key that player distinguish list items,you can using list `position` or list data's `hashcode` as `fingerprint`,eg:
95 |
96 | ``` java
97 | public void onBindViewHolder(VideoItemHolder holder, int position) {
98 | VideoItem videoItem = data.get(position);
99 | holder.name.setText(videoItem.name);
100 | holder.url.setText(videoItem.uri);
101 | holder.videoView.setVideoPath(videoItem.uri).setFingerprint(position);// or using:setFingerprint(videoItem.hashCode())
102 | }
103 |
104 | ```
105 |
106 | # config player
107 | all the configurations in VideoInfo,you can get VideoInfo and then set configurations,eg:
108 | ``` java
109 | //standalone player
110 | VideoInfo videoInfo = new VideoInfo("http://xxx.mp4")
111 | .setTitle("test video") //config title
112 | .setAspectRatio(aspectRatio) //aspectRatio
113 | .setShowTopBar(true) //show mediacontroller top bar
114 | .setPortraitWhenFullScreen(true);//portrait when full screen
115 |
116 | GiraffePlayer.play(getContext(), videoInfo);
117 |
118 | //in RecyclerView or embed player
119 | public void onBindViewHolder(VideoItemHolder holder, int position) {
120 | VideoItem videoItem = data.get(position);
121 | holder.name.setText(videoItem.name);
122 | holder.url.setText(videoItem.uri);
123 | holder.videoView.getVideoInfo().setBgColor(Color.GRAY).setAspectRatio(VideoInfo.AR_MATCH_PARENT);//config player
124 | holder.videoView.setVideoPath(videoItem.uri).setFingerprint(position);
125 | }
126 | ```
127 |
128 | # Proguard Configurationr
129 |
130 | ```
131 | -dontwarn tv.danmaku.ijk.media.player.**
132 | -keep class tv.danmaku.ijk.media.player.** { *; }
133 | -keep interface tv.danmaku.ijk.media.player.* { *; }
134 | ```
135 |
136 | # screenshot
137 |
138 | lazy load
139 |
140 | 
141 |
142 |
143 | 
144 |
145 |
146 | 
147 |
--------------------------------------------------------------------------------
/VERSIONS.md:
--------------------------------------------------------------------------------
1 | # 0.1.19
2 | 1. ijkplayer update to 0.8.8
3 | 2. support right-to-left
4 |
5 | # 0.1.18
6 | 1. support lazy load
7 |
8 | # 0.1.17
9 | 1. bug fix: controller status error when player was released
10 |
11 | # 0.1.16
12 | 1. fix [#42](https://github.com/tcking/GiraffePlayer2/issues/42)
13 |
14 | # 0.1.15
15 | 1. bug fix when set full screen model
16 |
17 | # 0.1.14
18 | 1. fix [#35](https://github.com/tcking/GiraffePlayer2/issues/35)
19 | 1. support looping
20 | 1. cover scale type follow video aspect ratio
21 | 1. VideoInfo add config:set current video as cover image when player released (setCurrentVideoAsCover)
22 |
23 | # 0.1.13
24 | 1. fix [#28](https://github.com/tcking/GiraffePlayer2/issues/28)
25 | 1. fix [#32](https://github.com/tcking/GiraffePlayer2/issues/32)
26 | 1. keep screen on while playing
27 |
28 | # 0.1.12
29 | 1. fix [#21](https://github.com/tcking/GiraffePlayer2/issues/21)
30 | 2. fix [#19](https://github.com/tcking/GiraffePlayer2/issues/19)
31 | 2. fix [#16](https://github.com/tcking/GiraffePlayer2/issues/16)
32 |
33 |
34 | # 0.1.11
35 | 1. support float window: `player.setDisplayModel(GiraffePlayer.DISPLAY_FLOAT)`
36 |
37 | # 0.1.10
38 | 1. bug fix: crash when call `videoView.setVideoPath()` to change video path [issues15](https://github.com/tcking/GiraffePlayer2/issues/15)
39 |
40 |
41 | # 0.1.9
42 |
43 | 1. stop network request when app go to background
44 | 1. keep play status when activity resume
45 | 1. add some api:`deselectTrack`,`setMute`,`isMute`
46 | 1. bug fix
47 |
48 |
49 | # 0.1.8
50 |
51 | 1.ijkplayer update to 0.8.4
52 |
53 | 2.support more decoder: using `compile 'com.github.tcking:giraffeplayer2:0.1.8-full'`
54 |
55 | # 0.1.7
56 |
57 | 1. FIX:seekbar secondary progress always 0
58 | 2. FIX:log error when using android android media player impl
59 |
60 | # 0.1.6
61 |
62 | disable seek when playing live stream
63 |
64 | # 0.1.5
65 |
66 | support select track
67 |
68 | # 0.1.4
69 |
70 | add animation for enter/exit full screen
71 |
72 | # 0.1.3
73 |
74 | some bug fix
--------------------------------------------------------------------------------
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 |
3 | android {
4 | compileSdkVersion COMPILESDKVERSION
5 | buildToolsVersion BUILDTOOLSVERSION
6 |
7 | lintOptions {
8 | checkReleaseBuilds false
9 | // Or, if you prefer, you can continue to check for errors in release builds,
10 | // but continue the build even when errors are found:
11 | abortOnError false
12 | }
13 |
14 |
15 | defaultConfig {
16 | applicationId "com.github.tcking.giraffeplayer.example"
17 | minSdkVersion MINSDKVERSION
18 | targetSdkVersion TARGETSDKVERSION
19 | versionCode 1
20 | versionName "1.0"
21 |
22 | ndk {
23 | abiFilters "armeabi-v7a","x86"
24 | }
25 | }
26 | signingConfigs {
27 | release {
28 | storeFile file("../../mykeystore")
29 | storePassword "123456"
30 | keyAlias "os"
31 | keyPassword "123456"
32 | }
33 | }
34 |
35 | buildTypes {
36 | release {
37 | minifyEnabled true
38 | shrinkResources true
39 | signingConfig signingConfigs.release
40 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
41 | }
42 | }
43 | }
44 |
45 | dependencies {
46 | implementation fileTree(dir: 'libs', include: ['*.jar'])
47 | implementation "com.android.support:recyclerview-v7:27.0.2"
48 | implementation 'com.android.support:appcompat-v7:27.0.2'
49 |
50 |
51 | // api 'com.github.tcking:giraffeplayer2:0.1.24-lazyLoad'
52 | api project(':giraffeplayer2')
53 |
54 | testImplementation 'junit:junit:4.12'
55 | }
56 |
57 |
58 | task depsize {
59 | group 'Help'
60 | description 'show dependencies aars size'
61 | doLast {
62 | final formatStr = "%,10.2f"
63 | final conf = configurations._releaseApk
64 | final size = conf.collect { it.length() / (1024 * 1024) }.sum()
65 | final out = new StringBuffer()
66 | out << 'Total dependencies size:'.padRight(50)
67 | out << "${String.format(formatStr, size)} Mb\n\n"
68 | conf.sort { -it.length() }
69 | .each {
70 | out << "${it.name}".padRight(50)
71 | out << "${String.format(formatStr, (it.length() / 1024))} kb\n"
72 | }
73 | println(out)
74 | }
75 | }
--------------------------------------------------------------------------------
/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # By default, the flags in this file are appended to flags specified
3 | # in /Users/tangchao/Library/Android/sdk/tools/proguard/proguard-android.txt
4 | # You can edit the include path and order by changing the proguardFiles
5 | # directive in build.gradle.
6 | #
7 | # For more details, see
8 | # http://developer.android.com/guide/developing/tools/proguard.html
9 |
10 | # Add any project specific keep options here:
11 |
12 | # If your project uses WebView with JS, uncomment the following
13 | # and specify the fully qualified class title to the JavaScript interface
14 | # class:
15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
16 | # public *;
17 | #}
18 |
19 | -dontwarn tv.danmaku.ijk.media.player.**
20 | -keep class tv.danmaku.ijk.media.player.** { *; }
21 | -keep interface tv.danmaku.ijk.media.player.* { *; }
--------------------------------------------------------------------------------
/app/src/androidTest/java/tcking/github/com/giraffeplayer/ApplicationTest.java:
--------------------------------------------------------------------------------
1 | package tcking.github.com.giraffeplayer;
2 |
3 | import android.app.Application;
4 | import android.test.ApplicationTestCase;
5 |
6 | /**
7 | * Testing Fundamentals
8 | */
9 | public class ApplicationTest extends ApplicationTestCase {
10 | public ApplicationTest() {
11 | super(Application.class);
12 | }
13 | }
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
8 |
13 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
28 |
29 |
33 |
34 |
35 |
36 |
--------------------------------------------------------------------------------
/app/src/main/assets/sample.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "name": "bunny",
4 | "uri": "http://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4"
5 | },{
6 | "name": "bipbop basic master playlist",
7 | "uri": "http://devimages.apple.com.edgekey.net/streaming/examples/bipbop_4x3/bipbop_4x3_variant.m3u8"
8 | },{
9 | "name": "live hls",
10 | "uri": "http://live.hkstv.hk.lxdns.com/live/hks/playlist.m3u8"
11 | },{
12 | "name": "Apple ID3 metadata",
13 | "uri": "http://devimages.apple.com/samplecode/adDemo/ad.m3u8"
14 | },
15 | {
16 | "name": "Apple 4x3 basic stream",
17 | "uri": "https://devimages.apple.com.edgekey.net/streaming/examples/bipbop_4x3/bipbop_4x3_variant.m3u8"
18 | },{
19 | "name": "cat",
20 | "uri": "http://html5demos.com/assets/dizzy.mp4"
21 | },
22 | {
23 | "name": "Apple TS media playlist",
24 | "uri": "https://devimages.apple.com.edgekey.net/streaming/examples/bipbop_4x3/gear1/prog_index.m3u8"
25 | },
26 | {
27 | "name": "dog",
28 | "uri": "http://storage.googleapis.com/exoplayer-test-media-1/mkv/android-screens-lavf-56.36.100-aac-avc-main-1280x720.mkv"
29 | }
30 | ]
--------------------------------------------------------------------------------
/app/src/main/assets/sample2.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "name": "hkstv",
4 | "uri": "http://zhibo.hkstv.tv/livestream/zb2yhapo/playlist.m3u8"
5 | },
6 | {
7 | "name": "comment1",
8 | "type": 1
9 | },
10 | {
11 | "name": "comment2",
12 | "type": 1
13 | },
14 | {
15 | "name": "comment3",
16 | "type": 1
17 | },
18 | {
19 | "name": "comment4",
20 | "type": 1
21 | },
22 | {
23 | "name": "comment5",
24 | "type": 1
25 | },
26 | {
27 | "name": "comment6",
28 | "type": 1
29 | },
30 | {
31 | "name": "comment7",
32 | "type": 1
33 | },
34 | {
35 | "name": "comment8",
36 | "type": 1
37 | },
38 | {
39 | "name": "comment9",
40 | "type": 1
41 | },
42 | {
43 | "name": "comment10",
44 | "type": 1
45 | },
46 | {
47 | "name": "comment11",
48 | "type": 1
49 | },
50 | {
51 | "name": "comment12",
52 | "type": 1
53 | }
54 | ]
--------------------------------------------------------------------------------
/app/src/main/java/tcking/github/com/giraffeplayer/example/ListExample2Activity.java:
--------------------------------------------------------------------------------
1 | package tcking.github.com.giraffeplayer.example;
2 |
3 | import android.os.Bundle;
4 | import android.support.annotation.Nullable;
5 | import android.support.v7.widget.LinearLayoutManager;
6 | import android.support.v7.widget.RecyclerView;
7 |
8 | import java.util.List;
9 |
10 | import tcking.github.com.giraffeplayer2.GiraffePlayer;
11 | import tcking.github.com.giraffeplayer2.VideoView;
12 |
13 | import static android.support.v7.widget.RecyclerView.SCROLL_STATE_IDLE;
14 |
15 | /**
16 | * Created by tcking on 2017
17 | */
18 |
19 | public class ListExample2Activity extends ListExampleActivity {
20 | private VideoView videoView;
21 | private int[] f_xy = new int[2];
22 | private int[] recyclerView_xy = new int[2];
23 |
24 |
25 | @Override
26 | protected void onCreate(@Nullable Bundle savedInstanceState) {
27 | fileName = "sample2.json";
28 | super.onCreate(savedInstanceState);
29 | }
30 |
31 | @Override
32 | protected void init() {
33 | final RecyclerView recyclerView = $.id(R.id.list).view();
34 | final LinearLayoutManager layoutManager = new LinearLayoutManager(this);
35 | recyclerView.setLayoutManager(layoutManager);
36 | final VideoAdapter videoAdapter = new VideoAdapter();
37 | recyclerView.setAdapter(videoAdapter);
38 | //auto stop & play after recyclerView scroll
39 | recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
40 |
41 | @Override
42 | public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
43 | if (newState == SCROLL_STATE_IDLE) {
44 | if (videoView != null) {
45 | if (layoutManager.findFirstVisibleItemPosition() == 0) {//No visible part > 50%,play float
46 | videoView.getPlayer().setDisplayModel(GiraffePlayer.DISPLAY_NORMAL);
47 | } else {
48 | videoView.getPlayer().setDisplayModel(GiraffePlayer.DISPLAY_FLOAT);
49 | }
50 | }
51 | }
52 | }
53 |
54 | @Override
55 | public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
56 |
57 | }
58 |
59 | });
60 |
61 | getSampleData(new Tom() {
62 | @Override
63 | public void onNext(List items) {
64 | videoAdapter.load(items);
65 | //play first video
66 | recyclerView.post(new Runnable() {
67 | @Override
68 | public void run() {
69 | recyclerView.getLocationInWindow(recyclerView_xy);
70 | videoView = (VideoView) layoutManager.findViewByPosition(0).findViewById(R.id.video_view);
71 | if (videoView != null) {
72 | videoView.getPlayer().start();
73 | }
74 | }
75 | });
76 | }
77 | });
78 | }
79 | }
80 |
--------------------------------------------------------------------------------
/app/src/main/java/tcking/github/com/giraffeplayer/example/ListExampleActivity.java:
--------------------------------------------------------------------------------
1 | package tcking.github.com.giraffeplayer.example;
2 |
3 | import android.os.Bundle;
4 | import android.os.Handler;
5 | import android.os.Looper;
6 | import android.support.annotation.NonNull;
7 | import android.support.annotation.Nullable;
8 | import android.support.v7.widget.LinearLayoutManager;
9 | import android.support.v7.widget.RecyclerView;
10 | import android.view.View;
11 | import android.widget.Toast;
12 |
13 | import com.github.tcking.viewquery.ViewQuery;
14 |
15 | import org.json.JSONArray;
16 | import org.json.JSONException;
17 | import org.json.JSONObject;
18 |
19 | import java.io.IOException;
20 | import java.io.InputStream;
21 | import java.util.ArrayList;
22 | import java.util.List;
23 |
24 | import tcking.github.com.giraffeplayer2.BasePlayerActivity;
25 | import tcking.github.com.giraffeplayer2.VideoView;
26 |
27 | import static android.support.v7.widget.RecyclerView.SCROLL_STATE_IDLE;
28 |
29 | /**
30 | * Created by tcking on 2017
31 | */
32 |
33 | public class ListExampleActivity extends BasePlayerActivity {
34 | protected ViewQuery $;
35 | protected String fileName = "sample.json";
36 |
37 | @Override
38 | protected void onCreate(@Nullable Bundle savedInstanceState) {
39 | super.onCreate(savedInstanceState);
40 | setContentView(R.layout.activity_list);
41 | $ = new ViewQuery(this);
42 | init();
43 | }
44 |
45 |
46 |
47 | protected void init() {
48 | final RecyclerView recyclerView= $.id(R.id.list).view();
49 | final LinearLayoutManager layoutManager = new LinearLayoutManager(this);
50 | recyclerView.setLayoutManager(layoutManager);
51 | final VideoAdapter videoAdapter = new VideoAdapter();
52 | recyclerView.setAdapter(videoAdapter);
53 |
54 | //auto stop & play after recyclerView scroll
55 | recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
56 | @Override
57 | public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
58 | if (newState == SCROLL_STATE_IDLE) {
59 | int playPosition = layoutManager.findFirstVisibleItemPosition();
60 | if (playPosition == -1) {//no visible item
61 | return;
62 | }
63 | int firstCompletelyVisibleItemPosition = layoutManager.findFirstCompletelyVisibleItemPosition();
64 | int lastCompletelyVisibleItemPosition = layoutManager.findLastCompletelyVisibleItemPosition();
65 |
66 | for (int i = firstCompletelyVisibleItemPosition; i <=lastCompletelyVisibleItemPosition; i++) {
67 | View viewByPosition = layoutManager.findViewByPosition(i);
68 | if (viewByPosition != null) {
69 | VideoView videoView = (VideoView) viewByPosition.findViewById(R.id.video_view);
70 | if (videoView!=null && videoView.isCurrentActivePlayer()) {
71 | return;//current active player is visible,do nothing
72 | }
73 | }
74 | }
75 |
76 |
77 | //try find first visible item (visible part > 50%)
78 | if (firstCompletelyVisibleItemPosition >= 0 && playPosition != firstCompletelyVisibleItemPosition) {
79 | int[] recyclerView_xy = new int[2];
80 | int[] f_xy = new int[2];
81 |
82 | VideoView videoView = (VideoView) layoutManager.findViewByPosition(playPosition).findViewById(R.id.video_view);
83 | videoView.getLocationInWindow(f_xy);
84 | recyclerView.getLocationInWindow(recyclerView_xy);
85 | int unVisibleY = f_xy[1] - recyclerView_xy[1];
86 |
87 | if (unVisibleY < 0 && Math.abs(unVisibleY) * 1.0 / videoView.getHeight() > 0.5) {//No visible part > 50%,play next
88 | playPosition = firstCompletelyVisibleItemPosition;
89 | }
90 | }
91 | VideoView videoView = (VideoView) layoutManager.findViewByPosition(playPosition).findViewById(R.id.video_view);
92 | if (videoView != null) {
93 | videoView.getPlayer().start();
94 | }
95 |
96 | }
97 |
98 | }
99 |
100 | });
101 |
102 | getSampleData(new Tom() {
103 | @Override
104 | public void onNext(List items) {
105 | videoAdapter.load(items);
106 | //play first video
107 | recyclerView.post(new Runnable() {
108 | @Override
109 | public void run() {
110 | VideoView videoView = (VideoView) layoutManager.findViewByPosition(0).findViewById(R.id.video_view);
111 | if (videoView != null) {
112 | videoView.getPlayer().start();
113 | }
114 | }
115 | });
116 | }
117 | });
118 | }
119 |
120 | @NonNull
121 | protected void getSampleData(final Tom tom) {
122 | new Thread(new Runnable() {
123 | @Override
124 | public void run() {
125 | try {
126 | InputStream open = getAssets().open(fileName);
127 | byte[] buf = new byte[open.available()];
128 | open.read(buf);
129 | JSONArray ja = new JSONArray(new String(buf, "UTF-8"));
130 | final List sample = new ArrayList<>(ja.length());
131 | for (int i = 0; i < ja.length(); i++) {
132 | JSONObject jb = ja.optJSONObject(i);
133 | VideoAdapter.VideoItem videoItem = new VideoAdapter.VideoItem();
134 | videoItem.type = jb.optInt("type",0);
135 | videoItem.name = jb.optString("name");
136 | videoItem.uri = jb.optString("uri");
137 | sample.add(videoItem);
138 | }
139 | new Handler(Looper.getMainLooper()).post(new Runnable() {
140 | @Override
141 | public void run() {
142 | tom.onNext(sample);
143 | }
144 | });
145 | } catch (IOException | JSONException e) {
146 | e.printStackTrace();
147 | Toast.makeText(getApplicationContext(),"load data error:"+e,Toast.LENGTH_SHORT).show();
148 | }
149 | }
150 | }).start();
151 | }
152 |
153 | interface Tom{
154 | void onNext(List items);
155 | }
156 | }
157 |
--------------------------------------------------------------------------------
/app/src/main/java/tcking/github/com/giraffeplayer/example/MainActivity.java:
--------------------------------------------------------------------------------
1 | package tcking.github.com.giraffeplayer.example;
2 |
3 | import android.Manifest;
4 | import android.content.pm.PackageManager;
5 | import android.content.res.Configuration;
6 | import android.os.Bundle;
7 | import android.support.annotation.NonNull;
8 | import android.support.annotation.Nullable;
9 | import android.support.v4.app.ActivityCompat;
10 | import android.support.v4.content.ContextCompat;
11 | import android.support.v7.app.AppCompatActivity;
12 | import android.widget.Toast;
13 |
14 | import com.github.tcking.viewquery.ViewQuery;
15 |
16 | import tcking.github.com.giraffeplayer2.GiraffePlayer;
17 | import tcking.github.com.giraffeplayer2.PlayerManager;
18 |
19 | /**
20 | * Created by TangChao on 2017/6/15.
21 | */
22 |
23 | public class MainActivity extends AppCompatActivity {
24 | private ViewQuery $;
25 |
26 | @Override
27 | protected void onCreate(@Nullable Bundle savedInstanceState) {
28 | super.onCreate(savedInstanceState);
29 |
30 | GiraffePlayer.debug = true;//show java logs
31 | GiraffePlayer.nativeDebug = false;//not show native logs
32 |
33 | getSupportFragmentManager().beginTransaction().add(android.R.id.content, new MainFragment()).commit();
34 |
35 | if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE)!= PackageManager.PERMISSION_GRANTED) {
36 | ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.READ_EXTERNAL_STORAGE},1);
37 | }
38 | }
39 |
40 |
41 | @Override
42 | public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
43 | if (requestCode == 1) {
44 | if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
45 | // Permission Granted
46 | } else {
47 | Toast.makeText(this, "please grant read permission", Toast.LENGTH_SHORT).show();
48 | }
49 | }
50 | }
51 |
52 | @Override
53 | public void onConfigurationChanged(Configuration newConfig) {
54 | super.onConfigurationChanged(newConfig);
55 | PlayerManager.getInstance().onConfigurationChanged(newConfig);
56 | }
57 |
58 | @Override
59 | public void onBackPressed() {
60 | if (PlayerManager.getInstance().onBackPressed()) {
61 | return;
62 | }
63 | super.onBackPressed();
64 | }
65 |
66 | }
67 |
--------------------------------------------------------------------------------
/app/src/main/java/tcking/github/com/giraffeplayer/example/MainFragment.java:
--------------------------------------------------------------------------------
1 | package tcking.github.com.giraffeplayer.example;
2 |
3 | import android.content.Context;
4 | import android.content.Intent;
5 | import android.net.Uri;
6 | import android.os.Bundle;
7 | import android.support.annotation.IdRes;
8 | import android.support.annotation.Nullable;
9 | import android.support.v4.app.Fragment;
10 | import android.view.LayoutInflater;
11 | import android.view.View;
12 | import android.view.ViewGroup;
13 | import android.widget.CheckBox;
14 | import android.widget.CompoundButton;
15 | import android.widget.RadioGroup;
16 |
17 | import com.github.tcking.viewquery.ViewQuery;
18 |
19 | import tcking.github.com.giraffeplayer2.DefaultMediaController;
20 | import tcking.github.com.giraffeplayer2.GiraffePlayer;
21 | import tcking.github.com.giraffeplayer2.MediaController;
22 | import tcking.github.com.giraffeplayer2.Option;
23 | import tcking.github.com.giraffeplayer2.PlayerManager;
24 | import tcking.github.com.giraffeplayer2.VideoInfo;
25 | import tcking.github.com.giraffeplayer2.VideoView;
26 | import tv.danmaku.ijk.media.player.IjkMediaPlayer;
27 |
28 | /**
29 | * Created by TangChao on 2017/6/15.
30 | */
31 |
32 | public class MainFragment extends Fragment {
33 | private ViewQuery $;
34 | private int aspectRatio = VideoInfo.AR_ASPECT_FIT_PARENT;
35 |
36 | @Override
37 | public void onCreate(@Nullable Bundle savedInstanceState) {
38 | super.onCreate(savedInstanceState);
39 | //set global configuration: turn on multiple_requests
40 | PlayerManager.getInstance().getDefaultVideoInfo().addOption(Option.create(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "multiple_requests", 1L));
41 | // PlayerManager.getInstance().getDefaultVideoInfo().addOptions(Option.preset4Realtime());
42 | }
43 |
44 | @Nullable
45 | @Override
46 | public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
47 | return inflater.inflate(R.layout.fragment_main, container, false);
48 | }
49 |
50 | @Override
51 | public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
52 | $ = new ViewQuery(view);
53 |
54 | String testUrl = "http://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4";
55 | // testUrl = "file:///sdcard/tmp/o.mp4"; //test local file;
56 | // testUrl = "https://tungsten.aaplimg.com/VOD/bipbop_adv_example_v2/master.m3u8"; //test live stream;
57 | // testUrl = "http://playertest.longtailvideo.com/adaptive/oceans_aes/oceans_aes.m3u8"; //test live stream;
58 | testUrl = "http://zhibo.hkstv.tv/livestream/zb2yhapo/playlist.m3u8"; //test live stream;
59 |
60 | final VideoView videoView = $.id(R.id.video_view).view();
61 | videoView.setVideoPath(testUrl);
62 |
63 | $.id(R.id.et_url).text(testUrl);
64 | CheckBox cb = $.id(R.id.cb_pwf).view();
65 | cb.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
66 | @Override
67 | public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
68 | videoView.getVideoInfo().setPortraitWhenFullScreen(isChecked);
69 | }
70 | });
71 |
72 | RadioGroup rb = $.id(R.id.rg_ra).view();
73 | rb.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() {
74 | @Override
75 | public void onCheckedChanged(RadioGroup group, @IdRes int checkedId) {
76 | if (checkedId == R.id.rb_4_3) {
77 | aspectRatio = VideoInfo.AR_4_3_FIT_PARENT;
78 | } else if (checkedId == R.id.rb_16_9) {
79 | aspectRatio = VideoInfo.AR_16_9_FIT_PARENT;
80 | } else if (checkedId == R.id.rb_fill_parent) {
81 | aspectRatio = VideoInfo.AR_ASPECT_FILL_PARENT;
82 | } else if (checkedId == R.id.rb_fit_parent) {
83 | aspectRatio = VideoInfo.AR_ASPECT_FIT_PARENT;
84 | } else if (checkedId == R.id.rb_wrap_content) {
85 | aspectRatio = VideoInfo.AR_ASPECT_WRAP_CONTENT;
86 | } else if (checkedId == R.id.rb_match_parent) {
87 | aspectRatio = VideoInfo.AR_MATCH_PARENT;
88 | }
89 | videoView.getPlayer().aspectRatio(aspectRatio);
90 |
91 | }
92 | });
93 |
94 | RadioGroup rb2 = $.id(R.id.rg_mc).view();
95 | rb2.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() {
96 | @Override
97 | public void onCheckedChanged(RadioGroup group, @IdRes final int checkedId) {
98 | PlayerManager.getInstance().setMediaControllerGenerator(new PlayerManager.MediaControllerGenerator() {
99 | @Override
100 | public MediaController create(Context context, VideoInfo videoInfo) {
101 | return checkedId == R.id.rb_mc_default ? new DefaultMediaController(context) : new SimpleMediaController(context);
102 | }
103 | });
104 | }
105 | });
106 |
107 | View.OnClickListener onClickListener = new View.OnClickListener() {
108 | @Override
109 | public void onClick(View v) {
110 |
111 | if (v.getId() == R.id.btn_play) {
112 | if (videoView.getPlayer().isPlaying()) {
113 | videoView.getPlayer().pause();
114 | } else {
115 | videoView.getPlayer().start();
116 | }
117 | } else if (v.getId() == R.id.btn_full) {
118 | videoView.getPlayer().toggleFullScreen();
119 | } else if (v.getId() == R.id.btn_play_float) {
120 | videoView.getPlayer().setDisplayModel(GiraffePlayer.DISPLAY_FLOAT);
121 | } else if (v.getId() == R.id.btn_list) {
122 | startActivity(new Intent(getActivity(), ListExampleActivity.class));
123 | } else if (v.getId() == R.id.btn_list2) {
124 | startActivity(new Intent(getActivity(), ListExample2Activity.class));
125 | } else if (v.getId() == R.id.btn_play_in_standalone) {
126 | VideoInfo videoInfo = new VideoInfo(Uri.parse($.id(R.id.et_url).text()))
127 | .setTitle("test video")
128 | .setAspectRatio(aspectRatio)
129 | // .setFullScreenOnly(true)
130 | // .addOption(Option.create(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "timeout", 30000000L))
131 | // .addOption(Option.create(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec", 1L))
132 | .addOption(Option.create(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "infbuf", 1L))
133 | .addOption(Option.create(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "multiple_requests", 1L))
134 | // .addOption(Option.create(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "headers", "Connection: keep-alive\r\nuser-agent: okhttp\r\n"))
135 | // .addOption(Option.create(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "reconnect", 1L))
136 | // .addOption(Option.create(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "reconnect_at_eof", 1L))
137 | // .addOption(Option.create(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "reconnect_streamed", 1L))
138 | // .addOption(Option.create(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "reconnect_delay_max", 1L))
139 | // .setPlayerImpl(VideoInfo.PLAYER_IMPL_SYSTEM) //using android media player
140 | .setShowTopBar(true);
141 |
142 | GiraffePlayer.play(getContext(), videoInfo);
143 | getActivity().overridePendingTransition(0, 0);
144 | }
145 | }
146 | };
147 | $.id(R.id.btn_play).view().setOnClickListener(onClickListener);
148 | $.id(R.id.btn_play_float).view().setOnClickListener(onClickListener);
149 | $.id(R.id.btn_full).view().setOnClickListener(onClickListener);
150 | $.id(R.id.btn_play_in_standalone).view().setOnClickListener(onClickListener);
151 | $.id(R.id.btn_list).view().setOnClickListener(onClickListener);
152 | $.id(R.id.btn_list2).view().setOnClickListener(onClickListener);
153 |
154 |
155 | }
156 |
157 |
158 | }
159 |
--------------------------------------------------------------------------------
/app/src/main/java/tcking/github/com/giraffeplayer/example/SimpleMediaController.java:
--------------------------------------------------------------------------------
1 | package tcking.github.com.giraffeplayer.example;
2 |
3 | import android.content.Context;
4 | import android.os.Message;
5 | import android.util.Log;
6 | import android.view.GestureDetector;
7 | import android.view.LayoutInflater;
8 | import android.view.MotionEvent;
9 | import android.view.View;
10 |
11 | import tcking.github.com.giraffeplayer2.DefaultMediaController;
12 | import tcking.github.com.giraffeplayer2.GiraffePlayer;
13 |
14 | public class SimpleMediaController extends DefaultMediaController {
15 |
16 |
17 | public SimpleMediaController(Context context) {
18 | super(context);
19 | }
20 |
21 | @Override
22 | protected View makeControllerView() {
23 | return LayoutInflater.from(context).inflate(R.layout.simple_media_controller, videoView, false);
24 | }
25 |
26 | @Override
27 | protected void initView(View view) {
28 | final GestureDetector gestureDetector = new GestureDetector(context, new GestureDetector.SimpleOnGestureListener() {
29 | @Override
30 | public boolean onSingleTapUp(MotionEvent e) {
31 | GiraffePlayer player = videoView.getPlayer();
32 | if (player != null) {
33 | if (player.isPlaying()) {
34 | player.pause();
35 | } else {
36 | player.start();
37 | }
38 | }
39 | return true;
40 | }
41 |
42 |
43 | @Override
44 | public boolean onDoubleTap(MotionEvent e) {
45 | GiraffePlayer player = videoView.getPlayer();
46 | if (player != null) {
47 | player.toggleFullScreen();
48 | }
49 | return true;
50 | }
51 | });
52 |
53 | view.setFocusable(true);
54 | view.setFocusableInTouchMode(true);
55 | view.setOnTouchListener(new View.OnTouchListener() {
56 | @Override
57 | public boolean onTouch(View v, MotionEvent event) {
58 | gestureDetector.onTouchEvent(event);
59 | return true;
60 | }
61 | });
62 |
63 | }
64 |
65 | @Override
66 | protected void statusChange(int status) {
67 | super.statusChange(status);
68 | Log.d("test","test"+status);
69 | switch (status) {
70 | case STATUS_LOADING:
71 | case STATUS_PLAYING:
72 | case STATUS_ERROR:
73 | $.id(R.id.app_video_pause).gone();
74 | break;
75 | case STATUS_PAUSE:
76 | case STATUS_COMPLETED:
77 | $.id(R.id.app_video_pause).visible();
78 |
79 | default:
80 | }
81 |
82 | }
83 | }
84 |
--------------------------------------------------------------------------------
/app/src/main/java/tcking/github/com/giraffeplayer/example/VideoAdapter.java:
--------------------------------------------------------------------------------
1 | package tcking.github.com.giraffeplayer.example;
2 |
3 | import android.content.Context;
4 | import android.support.v7.widget.RecyclerView;
5 | import android.view.LayoutInflater;
6 | import android.view.View;
7 | import android.view.ViewGroup;
8 | import android.widget.TextView;
9 | import android.widget.Toast;
10 |
11 | import java.util.LinkedList;
12 | import java.util.List;
13 |
14 | import tcking.github.com.giraffeplayer2.DefaultPlayerListener;
15 | import tcking.github.com.giraffeplayer2.GiraffePlayer;
16 | import tcking.github.com.giraffeplayer2.PlayerListener;
17 | import tcking.github.com.giraffeplayer2.VideoView;
18 |
19 | /**
20 | * Created by TangChao on 2017/6/15.
21 | */
22 |
23 | public class VideoAdapter extends RecyclerView.Adapter {
24 | private List data = new LinkedList<>();
25 | private Context context;
26 | private PlayerListener playerListener = new DefaultPlayerListener() {//example of using playerListener
27 | @Override
28 | public void onPreparing(GiraffePlayer giraffePlayer) {
29 | Toast.makeText(context, "start playing:" + giraffePlayer.getVideoInfo().getUri(), Toast.LENGTH_SHORT).show();
30 | }
31 |
32 | @Override
33 | public void onCompletion(GiraffePlayer giraffePlayer) {
34 | Toast.makeText(context, "play completion:" + giraffePlayer.getVideoInfo().getUri(), Toast.LENGTH_SHORT).show();
35 | }
36 | };
37 |
38 | @Override
39 | public VideoItemHolder onCreateViewHolder(ViewGroup parent, int viewType) {
40 | context = parent.getContext();
41 | if (viewType == VideoItem.TYPE_VIDEO) {
42 | return new VideoItemHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.item_video, parent, false));
43 | } else if (viewType == VideoItem.TYPE_COMMENT) {
44 | return new VideoItemHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.item_comment, parent, false));
45 | } else {
46 | throw new RuntimeException("unknown type:" + viewType);
47 | }
48 | }
49 |
50 | @Override
51 | public void onBindViewHolder(VideoItemHolder holder, int position) {
52 | VideoItem videoItem = data.get(position);
53 | if (videoItem.type == VideoItem.TYPE_VIDEO) {
54 | holder.name.setText(videoItem.name);
55 | holder.url.setText(videoItem.uri);
56 | if (holder.videoView.getCoverView() != null) {
57 | holder.videoView.getCoverView().setImageResource(R.drawable.cover1);
58 | }
59 | holder.videoView.getVideoInfo().setPortraitWhenFullScreen(false);
60 | holder.videoView.setVideoPath(videoItem.uri).setFingerprint(position);
61 | } else if (videoItem.type == VideoItem.TYPE_COMMENT) {
62 | holder.name.setText(videoItem.name);
63 | }
64 | }
65 |
66 | @Override
67 | public int getItemCount() {
68 | return data.size();
69 | }
70 |
71 | @Override
72 | public int getItemViewType(int position) {
73 | return data.get(position).type;
74 | }
75 |
76 | public void load(List newData) {
77 | data.clear();
78 | data.addAll(newData);
79 | notifyDataSetChanged();
80 | }
81 |
82 | class VideoItemHolder extends RecyclerView.ViewHolder {
83 | TextView name;
84 | TextView url;
85 | VideoView videoView;
86 |
87 | public VideoItemHolder(View itemView) {
88 | super(itemView);
89 | name = (TextView) itemView.findViewById(R.id.tv_name);
90 | url = (TextView) itemView.findViewById(R.id.tv_url);
91 | videoView = (VideoView) itemView.findViewById(R.id.video_view);
92 | if (videoView != null) {
93 | videoView.setPlayerListener(playerListener);
94 | }
95 | }
96 | }
97 |
98 | static class VideoItem {
99 | public static final int TYPE_VIDEO = 0;
100 | public static final int TYPE_COMMENT = 1;
101 | int type;
102 | String name;
103 | String uri;
104 | }
105 | }
106 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/cover1.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tcking/GiraffePlayer2/ba4add83436737b40ea38e6584aaf042294f6148/app/src/main/res/drawable/cover1.jpeg
--------------------------------------------------------------------------------
/app/src/main/res/drawable/loading.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tcking/GiraffePlayer2/ba4add83436737b40ea38e6584aaf042294f6148/app/src/main/res/drawable/loading.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/pause.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tcking/GiraffePlayer2/ba4add83436737b40ea38e6584aaf042294f6148/app/src/main/res/drawable/pause.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/progress_loading.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | -
4 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_list.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/fragment_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
9 |
10 |
16 |
17 |
21 |
22 |
25 |
26 |
30 |
31 |
37 |
38 |
39 |
43 |
44 |
50 |
51 |
57 |
58 |
59 |
65 |
66 |
70 |
71 |
77 |
78 |
83 |
84 |
88 |
89 |
94 |
95 |
101 |
102 |
107 |
108 |
113 |
114 |
119 |
120 |
125 |
126 |
127 |
128 |
131 |
132 |
138 |
139 |
140 |
144 |
145 |
151 |
152 |
157 |
158 |
159 |
160 |
161 |
162 |
163 |
164 |
170 |
171 |
172 |
173 |
174 |
175 |
176 |
177 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/item_comment.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
14 |
15 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/item_video.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
10 |
15 |
19 |
20 |
21 |
27 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/simple_media_controller.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
13 |
20 |
21 |
22 |
23 |
24 |
30 |
31 |
38 |
39 |
40 |
41 |
50 |
51 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tcking/GiraffePlayer2/ba4add83436737b40ea38e6584aaf042294f6148/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tcking/GiraffePlayer2/ba4add83436737b40ea38e6584aaf042294f6148/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tcking/GiraffePlayer2/ba4add83436737b40ea38e6584aaf042294f6148/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tcking/GiraffePlayer2/ba4add83436737b40ea38e6584aaf042294f6148/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tcking/GiraffePlayer2/ba4add83436737b40ea38e6584aaf042294f6148/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/values-w820dp/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 64dp
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #3F51B5
4 | #303F9F
5 | #FF4081
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 16dp
4 | 16dp
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | GiraffePlayerExample
3 |
4 |
--------------------------------------------------------------------------------
/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/app/src/test/java/tcking/github/com/giraffeplayer/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package tcking.github.com.giraffeplayer;
2 |
3 | import org.junit.Test;
4 |
5 | import static org.junit.Assert.*;
6 |
7 | /**
8 | * To work on unit tests, switch the Test Artifact in the Build Variants view.
9 | */
10 | public class ExampleUnitTest {
11 | @Test
12 | public void addition_isCorrect() throws Exception {
13 | assertEquals(4, 2 + 2);
14 | }
15 | }
--------------------------------------------------------------------------------
/build.gradle:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 |
3 | buildscript {
4 | repositories {
5 | jcenter()
6 | google()
7 | }
8 | dependencies {
9 | classpath 'com.android.tools.build:gradle:3.1.4'
10 |
11 | classpath 'com.github.dcendents:android-maven-gradle-plugin:2.1'
12 | classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.8.4'
13 |
14 | // NOTE: Do not place your application dependencies here; they belong
15 | // in the individual module build.gradle files
16 | }
17 | }
18 |
19 | allprojects {
20 | repositories {
21 | mavenLocal()
22 | jcenter()
23 | google()
24 | }
25 |
26 | ext {
27 | COMPILESDKVERSION = 27
28 | BUILDTOOLSVERSION = "27.0.3"
29 | TARGETSDKVERSION = 25
30 | MINSDKVERSION = 16
31 | }
32 | }
33 |
34 | task clean(type: Delete) {
35 | delete rootProject.buildDir
36 | }
37 |
38 |
39 | wrapper {
40 | gradleVersion = "4.4"
41 | }
42 |
--------------------------------------------------------------------------------
/demo/demo.apk:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tcking/GiraffePlayer2/ba4add83436737b40ea38e6584aaf042294f6148/demo/demo.apk
--------------------------------------------------------------------------------
/giraffeplayer2/.gitignore:
--------------------------------------------------------------------------------
1 | .gradle
2 | /local.properties
3 | /.idea
4 | /.idea/libraries
5 | .DS_Store
6 | /build
7 | /captures
8 |
--------------------------------------------------------------------------------
/giraffeplayer2/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.library'
2 | def bType = "" //lazyLoad,full, blank is normal
3 | def version = "0.1.25"
4 | def ijkVersion = "0.8.8"
5 |
6 | android {
7 | compileSdkVersion COMPILESDKVERSION
8 | buildToolsVersion BUILDTOOLSVERSION
9 |
10 | defaultConfig {
11 | buildConfigField 'boolean', 'lazyLoad', "" + ("lazyLoad" == bType)
12 | minSdkVersion MINSDKVERSION
13 | targetSdkVersion TARGETSDKVERSION
14 | versionCode 29
15 | versionName version + (bType == "" ? "" : "-") + bType
16 | }
17 | buildTypes {
18 | release {
19 | minifyEnabled false
20 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
21 | }
22 | }
23 | }
24 |
25 | dependencies {
26 | api fileTree(include: ['*.jar'], dir: 'libs')
27 | testImplementation 'junit:junit:4.12'
28 | api 'com.github.tcking:viewquery:1.6'
29 | implementation 'com.android.support:appcompat-v7:27.0.2'
30 | api "com.github.tcking:ijkplayer-java:${ijkVersion}"
31 |
32 | if ("" == bType) {
33 | api "com.github.tcking:ijkplayer-armv7a:${ijkVersion}"
34 | } else if ("full" == bType) {
35 | api "com.github.tcking:ijkplayer-armv7a:${ijkVersion}-full"
36 | }
37 | }
38 | apply from: new File(projectDir, "gradle-on-demand.gradle")
39 |
--------------------------------------------------------------------------------
/giraffeplayer2/gradle-on-demand.gradle:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2016 Zhang Rui
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | gradle.startParameter.taskNames.each { task ->
18 | def taskName = task.split(":").last()
19 | switch (taskName) {
20 | case "bU":
21 | case "bintrayUpload":
22 | case "install":
23 | apply from: new File(projectDir, 'publish.gradle')
24 | break;
25 | default:
26 | // do nothing
27 | break;
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/giraffeplayer2/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # By default, the flags in this file are appended to flags specified
3 | # in /Users/tangchao/Library/Android/sdk/tools/proguard/proguard-android.txt
4 | # You can edit the include path and order by changing the proguardFiles
5 | # directive in build.gradle.
6 | #
7 | # For more details, see
8 | # http://developer.android.com/guide/developing/tools/proguard.html
9 |
10 | # Add any project specific keep options here:
11 |
12 | # If your project uses WebView with JS, uncomment the following
13 | # and specify the fully qualified class title to the JavaScript interface
14 | # class:
15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
16 | # public *;
17 | #}
18 |
--------------------------------------------------------------------------------
/giraffeplayer2/project.properties:
--------------------------------------------------------------------------------
1 | project.name = com.github.tcking:giraffeplayer2
2 | project.desc = out of the box android video player base on ijkplayer (support list view,full screen,gesture control etc.)
3 | project.groupId = com.github.tcking
4 | project.artifactId = giraffeplayer2
5 | project.packaging = aar
6 | project.siteUrl = https://github.com/tcking/GiraffePlayer2
7 | project.gitUrl = https://github.com/tcking/GiraffePlayer2.git
8 | project.issueTrackerUrl = https://github.com/tcking/GiraffePlayer2/issues
9 |
10 | developer.name = tcking
11 | developer.email = tc_king@126.com
12 |
13 | javadoc.name = giraffeplayer2
--------------------------------------------------------------------------------
/giraffeplayer2/publish.gradle:
--------------------------------------------------------------------------------
1 | //--------------------------------------------------------------------------------------------------
2 | // Scripts to install plugin to local repository [~/.m2/repository]
3 | // > ./gradlew install
4 | //
5 | // Scripts to upload plugin to Bintray/JCenter [https://bintray.com/]
6 | // > ./gradlew bintrayUpload
7 | //--------------------------------------------------------------------------------------------------
8 | //
9 | //
10 | //buildscript {
11 | // repositories {
12 | // jcenter()
13 | // }
14 | // dependencies {
15 | // classpath 'com.android.tools.build:gradle:2.3.2'
16 | //
17 | // classpath 'com.github.dcendents:android-maven-gradle-plugin:1.5'
18 | // classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.7.3'
19 | //
20 | // // NOTE: Do not place your application dependencies here; they belong
21 | // // in the individual module build.gradle files
22 | // }
23 | //}
24 |
25 | apply plugin: 'com.github.dcendents.android-maven'
26 | apply plugin: 'com.jfrog.bintray'
27 |
28 | // load properties
29 | Properties properties = new Properties()
30 | File localPropertiesFile = project.file("local.properties");
31 | if(localPropertiesFile.exists()){
32 | properties.load(localPropertiesFile.newDataInputStream())
33 | }
34 |
35 | File projectPropertiesFile = project.file("project.properties");
36 | if(projectPropertiesFile.exists()){
37 | properties.load(projectPropertiesFile.newDataInputStream())
38 | }
39 |
40 | Properties bintrayPro = new Properties()
41 | File propFile = "${System.properties['user.home']}${File.separator}.gradle${File.separator}bintray.properties" as File
42 | bintrayPro.load(propFile.newDataInputStream())
43 |
44 | //read properties
45 | def projectName = properties.getProperty("project.name")
46 | def projectDesc = properties.getProperty("project.desc")
47 | def projectGroupId = properties.getProperty("project.groupId")
48 | def projectArtifactId = properties.getProperty("project.artifactId")
49 | def projectVersionName = android.defaultConfig.versionName
50 | def projectPackaging = properties.getProperty("project.packaging")
51 | def projectSiteUrl = properties.getProperty("project.siteUrl")
52 | def projectGitUrl = properties.getProperty("project.gitUrl")
53 | def projectIssueTrackerUrl = properties.getProperty("project.issueTrackerUrl")
54 |
55 | def developerName = properties.getProperty("developer.name")
56 | def developerEmail = properties.getProperty("developer.email")
57 |
58 | def bintrayUser = bintrayPro.getProperty("bintray.user")
59 | def bintrayApikey = bintrayPro.getProperty("bintray.apikey")
60 |
61 | def javadocName = properties.getProperty("javadoc.name")
62 |
63 | version = projectVersionName
64 | group = projectGroupId
65 |
66 | install {
67 | repositories.mavenInstaller {
68 | pom {
69 | project {
70 | name projectName
71 | groupId projectGroupId
72 | artifactId projectArtifactId
73 | version projectVersionName
74 | packaging projectPackaging
75 | url projectSiteUrl
76 | licenses {
77 | license {
78 | name 'The Apache Software License, Version 2.0'
79 | url 'http://www.apache.org/licenses/LICENSE-2.0.txt'
80 | }
81 | }
82 | developers {
83 | developer {
84 | name developerName
85 | email developerEmail
86 | }
87 | }
88 | scm {
89 | connection projectGitUrl
90 | developerConnection projectGitUrl
91 | url projectSiteUrl
92 | }
93 | }
94 | }
95 | }
96 | }
97 |
98 | task sourcesJar(type: Jar) {
99 | from android.sourceSets.main.java.srcDirs
100 | classifier = 'sources'
101 | }
102 |
103 | task javadoc(type: Javadoc) {
104 | source = android.sourceSets.main.java.srcDirs
105 | classpath += rootProject.files(android.getBootClasspath().join(File.pathSeparator))
106 | }
107 |
108 | task javadocJar(type: Jar, dependsOn: javadoc) {
109 | classifier = 'javadoc'
110 | from javadoc.destinationDir
111 | }
112 |
113 | artifacts {
114 | archives javadocJar
115 | archives sourcesJar
116 | }
117 |
118 | javadoc {
119 | failOnError false
120 | options{
121 | encoding 'UTF-8'
122 | charSet 'UTF-8'
123 | author true
124 | version projectVersionName
125 | links "http://docs.oracle.com/javase/7/docs/api"
126 | title javadocName
127 | }
128 | }
129 |
130 | bintray {
131 | user = bintrayUser
132 | key = bintrayApikey
133 | configurations = ['archives']
134 | pkg {
135 | repo = "maven"
136 | name = projectName
137 | desc = projectDesc
138 | websiteUrl = projectSiteUrl
139 | issueTrackerUrl = projectIssueTrackerUrl
140 | vcsUrl = projectGitUrl
141 | labels = ['android', 'video', 'player','ffmpeg']
142 | licenses = ["Apache-2.0"]
143 | publish = true
144 | }
145 | }
--------------------------------------------------------------------------------
/giraffeplayer2/src/androidTest/java/tcking/github/com/giraffeplayer/ApplicationTest.java:
--------------------------------------------------------------------------------
1 | package tcking.github.com.giraffeplayer;
2 |
3 | import android.app.Application;
4 | import android.test.ApplicationTestCase;
5 |
6 | /**
7 | * Testing Fundamentals
8 | */
9 | public class ApplicationTest extends ApplicationTestCase {
10 | public ApplicationTest() {
11 | super(Application.class);
12 | }
13 | }
--------------------------------------------------------------------------------
/giraffeplayer2/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
5 |
6 |
7 |
8 |
9 |
14 |
19 |
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/giraffeplayer2/src/main/java/tcking/github/com/giraffeplayer2/BaseMediaController.java:
--------------------------------------------------------------------------------
1 | package tcking.github.com.giraffeplayer2;
2 |
3 | import android.content.Context;
4 | import android.media.AudioManager;
5 | import android.os.Handler;
6 | import android.os.Looper;
7 | import android.view.View;
8 | import android.view.ViewGroup;
9 | import android.widget.FrameLayout;
10 |
11 | import com.github.tcking.viewquery.ViewQuery;
12 |
13 | /**
14 | * base of media controller
15 | * Created by tcking on 2017.
16 | */
17 |
18 | public abstract class BaseMediaController extends DefaultPlayerListener implements MediaController,Handler.Callback {
19 |
20 | protected static final int MESSAGE_SHOW_PROGRESS = 1;
21 | protected static final int MESSAGE_FADE_OUT = 2;
22 | protected static final int MESSAGE_SEEK_NEW_POSITION = 3;
23 | protected static final int MESSAGE_HIDE_CENTER_BOX = 4;
24 | protected static final int MESSAGE_RESTART_PLAY = 5;
25 |
26 | protected final Context context;
27 | protected final AudioManager audioManager;
28 | protected ViewQuery $;
29 |
30 | protected int defaultTimeout = 3 * 1000;
31 | protected Handler handler;
32 | protected VideoView videoView;
33 | protected View controllerView;
34 |
35 | public BaseMediaController(Context context) {
36 | this.context = context;
37 | audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
38 | handler = new Handler(Looper.getMainLooper(),this);
39 | }
40 |
41 |
42 | @Override
43 | public void bind(VideoView videoView) {
44 | this.videoView = videoView;
45 | controllerView = makeControllerView();
46 | $ = new ViewQuery(controllerView);
47 | initView(controllerView);
48 | FrameLayout.LayoutParams layoutParams = new FrameLayout.LayoutParams(
49 | ViewGroup.LayoutParams.MATCH_PARENT,
50 | ViewGroup.LayoutParams.MATCH_PARENT);
51 | this.videoView.getContainer().addView(controllerView, layoutParams);
52 | }
53 |
54 | protected abstract View makeControllerView();
55 |
56 | protected abstract void initView(View view);
57 |
58 | }
59 |
--------------------------------------------------------------------------------
/giraffeplayer2/src/main/java/tcking/github/com/giraffeplayer2/BasePlayerActivity.java:
--------------------------------------------------------------------------------
1 | package tcking.github.com.giraffeplayer2;
2 |
3 | import android.content.res.Configuration;
4 | import android.support.v7.app.AppCompatActivity;
5 |
6 | /**
7 | * Created by tcking on 2017
8 | */
9 |
10 | public abstract class BasePlayerActivity extends AppCompatActivity {
11 | @Override
12 | public void onConfigurationChanged(Configuration newConfig) {
13 | super.onConfigurationChanged(newConfig);
14 | PlayerManager.getInstance().onConfigurationChanged(newConfig);
15 | }
16 |
17 | @Override
18 | public void onBackPressed() {
19 | if (PlayerManager.getInstance().onBackPressed()) {
20 | return;
21 | }
22 | super.onBackPressed();
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/giraffeplayer2/src/main/java/tcking/github/com/giraffeplayer2/DefaultPlayerListener.java:
--------------------------------------------------------------------------------
1 | package tcking.github.com.giraffeplayer2;
2 |
3 | import tv.danmaku.ijk.media.player.IjkTimedText;
4 |
5 | /**
6 | * Created by tcking on 2017
7 | */
8 |
9 | public class DefaultPlayerListener implements PlayerListener {
10 |
11 | public static final DefaultPlayerListener INSTANCE = new DefaultPlayerListener();
12 |
13 | @Override
14 | public void onPrepared(GiraffePlayer giraffePlayer) {
15 |
16 | }
17 |
18 | @Override
19 | public void onBufferingUpdate(GiraffePlayer giraffePlayer, int percent) {
20 |
21 | }
22 |
23 | @Override
24 | public boolean onInfo(GiraffePlayer giraffePlayer, int what, int extra) {
25 | return true;
26 | }
27 |
28 | @Override
29 | public void onCompletion(GiraffePlayer giraffePlayer) {
30 |
31 | }
32 |
33 | @Override
34 | public void onSeekComplete(GiraffePlayer giraffePlayer) {
35 |
36 | }
37 |
38 | @Override
39 | public boolean onError(GiraffePlayer giraffePlayer, int what, int extra) {
40 | return true;
41 | }
42 |
43 | @Override
44 | public void onPause(GiraffePlayer giraffePlayer) {
45 |
46 | }
47 |
48 | @Override
49 | public void onRelease(GiraffePlayer giraffePlayer) {
50 |
51 | }
52 |
53 | @Override
54 | public void onStart(GiraffePlayer giraffePlayer) {
55 |
56 | }
57 |
58 | @Override
59 | public void onTargetStateChange(int oldState, int newState) {
60 |
61 | }
62 |
63 | @Override
64 | public void onCurrentStateChange(int oldState, int newState) {
65 |
66 | }
67 |
68 | @Override
69 | public void onDisplayModelChange(int oldModel, int newModel) {
70 |
71 | }
72 |
73 | @Override
74 | public void onPreparing(GiraffePlayer giraffePlayer) {
75 |
76 | }
77 |
78 | @Override
79 | public void onTimedText(GiraffePlayer giraffePlayer, IjkTimedText text) {
80 |
81 | }
82 |
83 | @Override
84 | public void onLazyLoadError(GiraffePlayer giraffePlayer, String message) {
85 |
86 | }
87 |
88 | @Override
89 | public void onLazyLoadProgress(GiraffePlayer giraffePlayer,int progress) {
90 |
91 | }
92 | }
93 |
--------------------------------------------------------------------------------
/giraffeplayer2/src/main/java/tcking/github/com/giraffeplayer2/LazyLoadManager.java:
--------------------------------------------------------------------------------
1 | package tcking.github.com.giraffeplayer2;
2 |
3 | import android.app.IntentService;
4 | import android.content.Context;
5 | import android.content.Intent;
6 | import android.content.pm.ApplicationInfo;
7 | import android.os.Build;
8 | import android.os.Message;
9 | import android.support.annotation.Nullable;
10 | import android.util.Log;
11 |
12 | import com.github.kevinsawicki.http.HttpRequest;
13 |
14 | import java.io.BufferedInputStream;
15 | import java.io.File;
16 | import java.io.FileInputStream;
17 | import java.io.FileOutputStream;
18 | import java.io.IOException;
19 | import java.net.HttpURLConnection;
20 | import java.util.Enumeration;
21 | import java.util.zip.ZipEntry;
22 | import java.util.zip.ZipFile;
23 | import java.util.zip.ZipInputStream;
24 |
25 | /**
26 | * Created by TangChao on 2018/2/11.
27 | */
28 |
29 | public class LazyLoadManager extends IntentService {
30 | private static final String TAG = "LazyLoadManager";
31 | private static String KEY_FINGERPRINT = "fingerprint";
32 | private static final int STATUS_IDLE = 0;
33 | private static final int STATUS_LOADING = 1;
34 | private static final int STATUS_OK = 2;
35 | private static final int STATUS_ERROR = -1;
36 | private static int loadStatus = STATUS_IDLE;
37 | private static String lastFingprint;
38 | private static Message lastMessage;
39 | private static final String soVersion = "0.8.8";
40 | private static String abi;
41 | private int progress = -1;
42 |
43 | public static void setSoFetcher(SoFetcher soFetcher) {
44 | if (soFetcher != null) {
45 | LazyLoadManager.soFetcher = soFetcher;
46 | }
47 | }
48 |
49 | private static SoFetcher soFetcher=new SoFetcher(){
50 |
51 | @Override
52 | public String getURL(String abi, String soVersion) {
53 | return String.format("https://raw.githubusercontent.com/tcking/GiraffePlayerLazyLoadFiles/master/%s/%s/so.zip", soVersion, abi);
54 | }
55 | };
56 |
57 | /**
58 | * Creates an IntentService. Invoked by your subclass's constructor.
59 | *
60 | * @param name Used to name the worker thread, important only for debugging.
61 | */
62 | public LazyLoadManager() {
63 | super("GiraffePlayerLazyLoader");
64 | }
65 |
66 | @Override
67 | protected void onHandleIntent(@Nullable Intent intent) {
68 | String fingerprint = intent.getStringExtra(KEY_FINGERPRINT);
69 | log(fingerprint, "loadStatus:" + loadStatus);
70 | if (loadStatus > 0) {
71 | sendLastMessage();
72 | return;
73 | }
74 | loadStatus = STATUS_LOADING;
75 | //https://raw.githubusercontent.com/tcking/GiraffePlayerLazyLoadFiles/master/0.1.17/armeabi/so.zip
76 | Context context = getApplicationContext();
77 | try {
78 | File playerDir = getPlayerRootDir(context);
79 | File soDir = getPlayerSoDir(context);
80 | log(fingerprint,"soDir:"+soDir);
81 | if (new File(soDir, "libijkffmpeg.so").exists()) {
82 | log(fingerprint,"so files downloaded,try install");
83 | installSo(soDir);
84 | return;
85 | }
86 |
87 | String abi = getCurrentAbi(context);
88 | String url = soFetcher.getURL(abi, soVersion);
89 | log(fingerprint, "download so from:" + url);
90 | progress = -1;
91 | HttpRequest request = HttpRequest.get(url);
92 | int code = request.code();
93 | log(fingerprint, "server:" + code);
94 | if (code == HttpURLConnection.HTTP_OK) {
95 | File tmp = new File(new File(playerDir, "tmp"), "so.zip");
96 | tmp.delete();
97 | tmp.getParentFile().mkdirs();
98 | tmp.createNewFile();
99 | FileOutputStream fileOutputStream = new FileOutputStream(tmp);
100 | BufferedInputStream buffer = request.buffer();
101 | byte data[] = new byte[4096];
102 | long total = 0;
103 | int count;
104 | int contentLength = request.contentLength();
105 | while ((count = buffer.read(data)) != -1) {
106 | total += count;
107 | // publishing the progress....
108 | if (contentLength > 0) {
109 | publishProgress((int) (total * 100 / contentLength));
110 | }
111 | fileOutputStream.write(data, 0, count);
112 | }
113 | fileOutputStream.close();
114 |
115 | unZip(tmp, soDir);
116 | tmp.delete();
117 | installSo(soDir);
118 | } else {
119 | error("server response " +code);
120 | }
121 | } catch (Exception e) {
122 | e.printStackTrace();
123 | error(e.getMessage());
124 | }
125 |
126 |
127 | }
128 |
129 | private void error(String message) {
130 | loadStatus = STATUS_ERROR;
131 | if (lastFingprint != null) {
132 | GiraffePlayer player = PlayerManager.getInstance().getPlayerByFingerprint(lastFingprint);
133 | if (player != null) {
134 | player.lazyLoadError(message);
135 | }
136 | }
137 |
138 | }
139 |
140 | private void installSo(File soDir) throws Exception {
141 | SoInstaller.installNativeLibraryPath(GiraffePlayer.class.getClassLoader(), soDir);
142 | log(lastFingprint,"so installed");
143 | loadStatus = STATUS_OK;
144 | sendLastMessage();
145 | }
146 |
147 | private void publishProgress(int _progress) {
148 | if (progress == _progress) {
149 | return;
150 | }
151 | progress = _progress;
152 | if (lastFingprint != null) {
153 |
154 | GiraffePlayer player = PlayerManager.getInstance().getPlayerByFingerprint(lastFingprint);
155 | if (player != null) {
156 | player.lazyLoadProgress(progress);
157 | }
158 | }
159 | }
160 |
161 | /**
162 | * replay last message to the player
163 | */
164 | private void sendLastMessage() {
165 | GiraffePlayer player = PlayerManager.getInstance().getPlayerByFingerprint(lastFingprint);
166 | if (player != null) {
167 | player.doMessage(lastMessage);
168 | }
169 | lastMessage = null;
170 | lastFingprint = null;
171 | }
172 |
173 | public static void Load(Context context, String fingerprint, Message message) {
174 |
175 | lastFingprint = fingerprint;
176 | lastMessage = message;
177 |
178 | Intent service = new Intent(context, LazyLoadManager.class);
179 | service.putExtra(KEY_FINGERPRINT, fingerprint);
180 | context.startService(service);
181 | }
182 |
183 | private void log(String fingerprint, String msg) {
184 | if (GiraffePlayer.debug) {
185 | Log.d(TAG, String.format("[fingerprint:%s] %s", fingerprint, msg));
186 | }
187 | }
188 |
189 |
190 | public static String getCurrentAbi(Context context) {
191 | if (abi != null) {
192 | return abi;
193 | }
194 | ApplicationInfo applicationInfo = context.getApplicationInfo();
195 | String apkPath = applicationInfo.sourceDir;
196 | ZipFile apk;
197 | String[] abis;
198 | if (Build.VERSION.SDK_INT >= 21) {
199 | abis = Build.SUPPORTED_ABIS;
200 | } else {
201 | abis = new String[]{Build.CPU_ABI, Build.CPU_ABI2};
202 | }
203 | try {
204 | apk = new ZipFile(apkPath);
205 | for (String _abi : abis) {
206 | Enumeration extends ZipEntry> entryEnumeration = apk.entries();
207 | while (entryEnumeration.hasMoreElements()) {
208 | ZipEntry entry = entryEnumeration.nextElement();
209 | if (entry == null) {
210 | continue;
211 | }
212 | String entryName = entry.getName();
213 | if (entryName.startsWith("lib/") && entryName.endsWith("so")) {
214 | int start = entryName.indexOf("/");
215 | int end = entryName.lastIndexOf("/");
216 | String temp = entryName.substring(start + 1, end);
217 | if (_abi.equalsIgnoreCase(temp)) {
218 | abi = _abi;
219 | return abi;
220 | }
221 | }
222 | }
223 |
224 | }
225 | } catch (IOException e) {
226 | e.printStackTrace();
227 | }
228 | abi = abis[0];
229 | return abi;
230 | }
231 |
232 |
233 | public static void unZip(File input, File output) throws Exception {
234 | output.mkdirs();
235 | ZipInputStream inZip = new ZipInputStream(new FileInputStream(input));
236 | ZipEntry zipEntry;
237 | String szName;
238 | while ((zipEntry = inZip.getNextEntry()) != null) {
239 | szName = zipEntry.getName();
240 | if (zipEntry.isDirectory()) {
241 | // get the folder name of the widget
242 | szName = szName.substring(0, szName.length() - 1);
243 | File folder = new File(output.getAbsolutePath() + File.separator + szName);
244 | folder.mkdirs();
245 | } else {
246 |
247 | File file = new File(output.getAbsolutePath() + File.separator + szName);
248 | file.createNewFile();
249 | // get the output stream of the file
250 | FileOutputStream out = new FileOutputStream(file);
251 | int len;
252 | byte[] buffer = new byte[1024];
253 | // read (len) bytes into buffer
254 | while ((len = inZip.read(buffer)) != -1) {
255 | // write (len) byte from buffer at the position 0
256 | out.write(buffer, 0, len);
257 | out.flush();
258 | }
259 | out.close();
260 | }
261 | }
262 | inZip.close();
263 | }
264 |
265 | public static File getPlayerRootDir(Context context) {
266 | return context.getDir("giraffePlayer2", Context.MODE_PRIVATE);
267 |
268 | }
269 |
270 | public static File getPlayerSoDir(Context context) {
271 | return new File(getPlayerRootDir(context).getAbsolutePath()+File.separator+soVersion+File.separator+"so");
272 |
273 | }
274 |
275 | public interface SoFetcher{
276 | String getURL(String abi,String soVersion);
277 | }
278 | }
279 |
--------------------------------------------------------------------------------
/giraffeplayer2/src/main/java/tcking/github/com/giraffeplayer2/MeasureHelper.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2015 Zhang Rui
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package tcking.github.com.giraffeplayer2;
18 |
19 | import android.view.View;
20 |
21 | import java.lang.ref.WeakReference;
22 |
23 |
24 |
25 | public final class MeasureHelper {
26 | private WeakReference mWeakView;
27 |
28 | private int mVideoWidth;
29 | private int mVideoHeight;
30 | private int mVideoSarNum;
31 | private int mVideoSarDen;
32 |
33 | private int mVideoRotationDegree;
34 |
35 | private int mMeasuredWidth;
36 | private int mMeasuredHeight;
37 |
38 | private int mCurrentAspectRatio = VideoInfo.AR_ASPECT_FIT_PARENT;
39 |
40 | public MeasureHelper(View view) {
41 | mWeakView = new WeakReference(view);
42 | }
43 |
44 | public View getView() {
45 | if (mWeakView == null)
46 | return null;
47 | return mWeakView.get();
48 | }
49 |
50 | public void setVideoSize(int videoWidth, int videoHeight) {
51 | mVideoWidth = videoWidth;
52 | mVideoHeight = videoHeight;
53 | }
54 |
55 | public void setVideoSampleAspectRatio(int videoSarNum, int videoSarDen) {
56 | mVideoSarNum = videoSarNum;
57 | mVideoSarDen = videoSarDen;
58 | }
59 |
60 | public void setVideoRotation(int videoRotationDegree) {
61 | mVideoRotationDegree = videoRotationDegree;
62 | }
63 |
64 | /**
65 | * Must be called by View.onMeasure(int, int)
66 | *
67 | * @param widthMeasureSpec
68 | * @param heightMeasureSpec
69 | */
70 | public void doMeasure(int widthMeasureSpec, int heightMeasureSpec) {
71 | //Log.i("@@@@", "onMeasure(" + MeasureSpec.toString(widthMeasureSpec) + ", "
72 | // + MeasureSpec.toString(heightMeasureSpec) + ")");
73 | if (mVideoRotationDegree == 90 || mVideoRotationDegree == 270) {
74 | int tempSpec = widthMeasureSpec;
75 | widthMeasureSpec = heightMeasureSpec;
76 | heightMeasureSpec = tempSpec;
77 | }
78 |
79 | int width = View.getDefaultSize(mVideoWidth, widthMeasureSpec);
80 | int height = View.getDefaultSize(mVideoHeight, heightMeasureSpec);
81 | if (mCurrentAspectRatio == VideoInfo.AR_MATCH_PARENT) {
82 | width = widthMeasureSpec;
83 | height = heightMeasureSpec;
84 | } else if (mVideoWidth > 0 && mVideoHeight > 0) {
85 | int widthSpecMode = View.MeasureSpec.getMode(widthMeasureSpec);
86 | int widthSpecSize = View.MeasureSpec.getSize(widthMeasureSpec);
87 | int heightSpecMode = View.MeasureSpec.getMode(heightMeasureSpec);
88 | int heightSpecSize = View.MeasureSpec.getSize(heightMeasureSpec);
89 |
90 | if (widthSpecMode == View.MeasureSpec.AT_MOST && heightSpecMode == View.MeasureSpec.AT_MOST) {
91 | float specAspectRatio = (float) widthSpecSize / (float) heightSpecSize;
92 | float displayAspectRatio;
93 | switch (mCurrentAspectRatio) {
94 | case VideoInfo.AR_16_9_FIT_PARENT:
95 | displayAspectRatio = 16.0f / 9.0f;
96 | if (mVideoRotationDegree == 90 || mVideoRotationDegree == 270)
97 | displayAspectRatio = 1.0f / displayAspectRatio;
98 | break;
99 | case VideoInfo.AR_4_3_FIT_PARENT:
100 | displayAspectRatio = 4.0f / 3.0f;
101 | if (mVideoRotationDegree == 90 || mVideoRotationDegree == 270)
102 | displayAspectRatio = 1.0f / displayAspectRatio;
103 | break;
104 | case VideoInfo.AR_ASPECT_FIT_PARENT:
105 | case VideoInfo.AR_ASPECT_FILL_PARENT:
106 | case VideoInfo.AR_ASPECT_WRAP_CONTENT:
107 | default:
108 | displayAspectRatio = (float) mVideoWidth / (float) mVideoHeight;
109 | if (mVideoSarNum > 0 && mVideoSarDen > 0)
110 | displayAspectRatio = displayAspectRatio * mVideoSarNum / mVideoSarDen;
111 | break;
112 | }
113 | boolean shouldBeWider = displayAspectRatio > specAspectRatio;
114 |
115 | switch (mCurrentAspectRatio) {
116 | case VideoInfo.AR_ASPECT_FIT_PARENT:
117 | case VideoInfo.AR_16_9_FIT_PARENT:
118 | case VideoInfo.AR_4_3_FIT_PARENT:
119 | if (shouldBeWider) {
120 | // too wide, fix width
121 | width = widthSpecSize;
122 | height = (int) (width / displayAspectRatio);
123 | } else {
124 | // too high, fix height
125 | height = heightSpecSize;
126 | width = (int) (height * displayAspectRatio);
127 | }
128 | break;
129 | case VideoInfo.AR_ASPECT_FILL_PARENT:
130 | if (shouldBeWider) {
131 | // not high enough, fix height
132 | height = heightSpecSize;
133 | width = (int) (height * displayAspectRatio);
134 | } else {
135 | // not wide enough, fix width
136 | width = widthSpecSize;
137 | height = (int) (width / displayAspectRatio);
138 | }
139 | break;
140 | case VideoInfo.AR_ASPECT_WRAP_CONTENT:
141 | default:
142 | if (shouldBeWider) {
143 | // too wide, fix width
144 | width = Math.min(mVideoWidth, widthSpecSize);
145 | height = (int) (width / displayAspectRatio);
146 | } else {
147 | // too high, fix height
148 | height = Math.min(mVideoHeight, heightSpecSize);
149 | width = (int) (height * displayAspectRatio);
150 | }
151 | break;
152 | }
153 | } else if (widthSpecMode == View.MeasureSpec.EXACTLY && heightSpecMode == View.MeasureSpec.EXACTLY) {
154 | // the size is fixed
155 | width = widthSpecSize;
156 | height = heightSpecSize;
157 |
158 | // for compatibility, we adjust size based on aspect ratio
159 | if (mVideoWidth * height < width * mVideoHeight) {
160 | //Log.i("@@@", "image too wide, correcting");
161 | width = height * mVideoWidth / mVideoHeight;
162 | } else if (mVideoWidth * height > width * mVideoHeight) {
163 | //Log.i("@@@", "image too tall, correcting");
164 | height = width * mVideoHeight / mVideoWidth;
165 | }
166 | } else if (widthSpecMode == View.MeasureSpec.EXACTLY) {
167 | // only the width is fixed, adjust the height to match aspect ratio if possible
168 | width = widthSpecSize;
169 | height = width * mVideoHeight / mVideoWidth;
170 | if (heightSpecMode == View.MeasureSpec.AT_MOST && height > heightSpecSize) {
171 | // couldn't match aspect ratio within the constraints
172 | height = heightSpecSize;
173 | }
174 | } else if (heightSpecMode == View.MeasureSpec.EXACTLY) {
175 | // only the height is fixed, adjust the width to match aspect ratio if possible
176 | height = heightSpecSize;
177 | width = height * mVideoWidth / mVideoHeight;
178 | if (widthSpecMode == View.MeasureSpec.AT_MOST && width > widthSpecSize) {
179 | // couldn't match aspect ratio within the constraints
180 | width = widthSpecSize;
181 | }
182 | } else {
183 | // neither the width nor the height are fixed, try to use actual video size
184 | width = mVideoWidth;
185 | height = mVideoHeight;
186 | if (heightSpecMode == View.MeasureSpec.AT_MOST && height > heightSpecSize) {
187 | // too tall, decrease both width and height
188 | height = heightSpecSize;
189 | width = height * mVideoWidth / mVideoHeight;
190 | }
191 | if (widthSpecMode == View.MeasureSpec.AT_MOST && width > widthSpecSize) {
192 | // too wide, decrease both width and height
193 | width = widthSpecSize;
194 | height = width * mVideoHeight / mVideoWidth;
195 | }
196 | }
197 | } else {
198 | // no size yet, just adopt the given spec sizes
199 | }
200 |
201 | mMeasuredWidth = width;
202 | mMeasuredHeight = height;
203 | }
204 |
205 | public int getMeasuredWidth() {
206 | return mMeasuredWidth;
207 | }
208 |
209 | public int getMeasuredHeight() {
210 | return mMeasuredHeight;
211 | }
212 |
213 | public void setAspectRatio(int aspectRatio) {
214 | mCurrentAspectRatio = aspectRatio;
215 | }
216 |
217 | // @NonNull
218 | // public static String getAspectRatioText(Context context, int aspectRatio) {
219 | // String text;
220 | // switch (aspectRatio) {
221 | // case IRenderView.AR_ASPECT_FIT_PARENT:
222 | // text = context.getString(R.string.VideoView_ar_aspect_fit_parent);
223 | // break;
224 | // case IRenderView.AR_ASPECT_FILL_PARENT:
225 | // text = context.getString(R.string.VideoView_ar_aspect_fill_parent);
226 | // break;
227 | // case IRenderView.AR_ASPECT_WRAP_CONTENT:
228 | // text = context.getString(R.string.VideoView_ar_aspect_wrap_content);
229 | // break;
230 | // case IRenderView.AR_MATCH_PARENT:
231 | // text = context.getString(R.string.VideoView_ar_match_parent);
232 | // break;
233 | // case IRenderView.AR_16_9_FIT_PARENT:
234 | // text = context.getString(R.string.VideoView_ar_16_9_fit_parent);
235 | // break;
236 | // case IRenderView.AR_4_3_FIT_PARENT:
237 | // text = context.getString(R.string.VideoView_ar_4_3_fit_parent);
238 | // break;
239 | // default:
240 | // text = context.getString(R.string.N_A);
241 | // break;
242 | // }
243 | // return text;
244 | // }
245 | }
246 |
--------------------------------------------------------------------------------
/giraffeplayer2/src/main/java/tcking/github/com/giraffeplayer2/MediaController.java:
--------------------------------------------------------------------------------
1 | package tcking.github.com.giraffeplayer2;
2 |
3 |
4 | /**
5 | * Created by tcking on 2017
6 | */
7 |
8 | public interface MediaController extends PlayerListener {
9 | /**
10 | * bind this media controller to video controllerView
11 | */
12 | void bind(VideoView videoView);
13 | }
14 |
--------------------------------------------------------------------------------
/giraffeplayer2/src/main/java/tcking/github/com/giraffeplayer2/Option.java:
--------------------------------------------------------------------------------
1 | package tcking.github.com.giraffeplayer2;
2 |
3 | import java.io.Serializable;
4 | import java.util.ArrayList;
5 | import java.util.List;
6 |
7 | import tv.danmaku.ijk.media.player.IjkMediaPlayer;
8 |
9 | /**
10 | * Created by tcking on 2017
11 | */
12 |
13 | public class Option implements Serializable, Cloneable {
14 | private int category;
15 | private String name;
16 | private Object value;
17 |
18 | private Option(int category, String name, Object value) {
19 | this.category = category;
20 | this.name = name;
21 | this.value = value;
22 | }
23 |
24 | public static Option create(int category, String name, String value) {
25 | return new Option(category, name, value);
26 | }
27 |
28 | public static Option create(int category, String name, Long value) {
29 | return new Option(category, name, value);
30 | }
31 |
32 | public int getCategory() {
33 | return category;
34 | }
35 |
36 | public String getName() {
37 | return name;
38 | }
39 |
40 | public Object getValue() {
41 | return value;
42 | }
43 |
44 | @Override
45 | public boolean equals(Object o) {
46 | if (this == o) return true;
47 | if (o == null || getClass() != o.getClass()) return false;
48 |
49 | Option option = (Option) o;
50 |
51 | if (category != option.category) return false;
52 | if (name != null ? !name.equals(option.name) : option.name != null) return false;
53 | return value != null ? value.equals(option.value) : option.value == null;
54 |
55 | }
56 |
57 | @Override
58 | public int hashCode() {
59 | int result = category;
60 | result = 31 * result + (name != null ? name.hashCode() : 0);
61 | result = 31 * result + (value != null ? value.hashCode() : 0);
62 | return result;
63 | }
64 |
65 | @Override
66 | public Option clone() throws CloneNotSupportedException {
67 | return (Option) super.clone();
68 | }
69 |
70 | /**
71 | * preset for realtime stream
72 | * @return
73 | */
74 | public static List