├── .github
└── workflows
│ └── android.yml
├── .gitignore
├── .idea
├── .gitignore
├── compiler.xml
├── gradle.xml
└── misc.xml
├── README.md
├── app
├── .gitignore
├── build.gradle
├── libs
│ └── libvlc-all-3.5.1-https.aar
├── proguard-rules.pro
└── src
│ ├── androidTest
│ └── java
│ │ └── org
│ │ └── sifacaii
│ │ └── vlcdlnaplayer
│ │ └── ExampleInstrumentedTest.java
│ ├── main
│ ├── AndroidManifest.xml
│ ├── assets
│ │ └── upnp
│ │ │ ├── description - 副本.xml
│ │ │ ├── description.xml
│ │ │ ├── scpd - 副本.xml
│ │ │ └── scpd.xml
│ ├── ic_launcher-playstore.png
│ ├── java
│ │ └── org
│ │ │ └── sifacaii
│ │ │ └── vlcdlnaplayer
│ │ │ ├── Config.java
│ │ │ ├── MainActivity.java
│ │ │ ├── MessageEvent.java
│ │ │ ├── dlna
│ │ │ ├── AVTransport.java
│ │ │ ├── NOTIFY.java
│ │ │ ├── SSDP.java
│ │ │ └── Utils.java
│ │ │ └── vlcplayer
│ │ │ ├── Controller.java
│ │ │ ├── Player.java
│ │ │ ├── PopMenuP.java
│ │ │ └── Video.java
│ └── res
│ │ ├── drawable
│ │ ├── ic_launcher_background.xml
│ │ ├── ic_launcher_foreground.xml
│ │ ├── popmenu_focus.xml
│ │ └── spacer_medium.xml
│ │ ├── layout
│ │ ├── activity_main.xml
│ │ ├── controller.xml
│ │ ├── popmenu.xml
│ │ └── popmenu_item.xml
│ │ ├── mipmap-anydpi-v26
│ │ ├── ic_launcher.xml
│ │ └── ic_launcher_round.xml
│ │ ├── mipmap-hdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ ├── mipmap-mdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ ├── mipmap-xhdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ ├── mipmap-xxhdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ ├── mipmap-xxxhdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ ├── values-night
│ │ └── themes.xml
│ │ ├── values
│ │ ├── colors.xml
│ │ ├── dimens.xml
│ │ ├── ic_launcher_background.xml
│ │ ├── strings.xml
│ │ └── themes.xml
│ │ └── xml
│ │ ├── backup_rules.xml
│ │ └── data_extraction_rules.xml
│ └── test
│ └── java
│ └── org
│ └── sifacaii
│ └── vlcdlnaplayer
│ └── ExampleUnitTest.java
├── build.gradle
├── gradle.properties
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
└── settings.gradle
/.github/workflows/android.yml:
--------------------------------------------------------------------------------
1 | name: Android CI
2 |
3 | on:
4 | push:
5 | branches: [ "master" ]
6 | pull_request:
7 | branches: [ "master" ]
8 |
9 | jobs:
10 | build:
11 |
12 | runs-on: ubuntu-latest
13 |
14 | steps:
15 | - uses: actions/checkout@v3
16 | - name: set up JDK 17
17 | uses: actions/setup-java@v3
18 | with:
19 | java-version: '17'
20 | distribution: 'temurin'
21 | cache: gradle
22 |
23 | - name: Grant execute permission for gradlew
24 | run: chmod +x gradlew
25 | - name: Build with Gradle
26 | run: ./gradlew build
27 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | /local.properties
4 | /.idea/caches
5 | /.idea/libraries
6 | /.idea/modules.xml
7 | /.idea/workspace.xml
8 | /.idea/navEditor.xml
9 | /.idea/assetWizardSettings.xml
10 | .DS_Store
11 | /build
12 | /captures
13 | .externalNativeBuild
14 | .cxx
15 | local.properties
16 |
--------------------------------------------------------------------------------
/.idea/.gitignore:
--------------------------------------------------------------------------------
1 | # Default ignored files
2 | /shelf/
3 | /workspace.xml
4 |
--------------------------------------------------------------------------------
/.idea/compiler.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/gradle.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
18 |
19 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | DLNA投屏播放器,
2 | 基于LibVLC
3 |
--------------------------------------------------------------------------------
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | plugins {
2 | id 'com.android.application'
3 | }
4 |
5 | android {
6 | namespace 'org.sifacaii.vlcdlnaplayer'
7 | compileSdk 33
8 |
9 | defaultConfig {
10 | applicationId "org.sifacaii.vlcdlnaplayer"
11 | minSdk 17
12 | targetSdk 33
13 | versionCode 1
14 | versionName "1.0"
15 |
16 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
17 | }
18 |
19 | buildTypes {
20 | release {
21 | minifyEnabled false
22 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
23 | }
24 | }
25 |
26 | splits {
27 | abi {
28 | enable true
29 | include 'x86', 'armeabi-v7a', 'x86_64', 'arm64-v8a'
30 | }
31 | }
32 |
33 | compileOptions {
34 | sourceCompatibility JavaVersion.VERSION_1_8
35 | targetCompatibility JavaVersion.VERSION_1_8
36 | }
37 | }
38 |
39 | dependencies {
40 | implementation files('libs/libvlc-all-3.5.1-https.aar')
41 | implementation 'org.greenrobot:eventbus:3.3.1'
42 | implementation 'org.nanohttpd:nanohttpd:2.3.1'
43 | implementation 'androidx.appcompat:appcompat:1.4.1'
44 | implementation 'com.google.android.material:material:1.5.0'
45 | implementation 'com.orhanobut:hawk:2.0.1'
46 | testImplementation 'junit:junit:4.13.2'
47 | androidTestImplementation 'androidx.test.ext:junit:1.1.3'
48 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
49 | }
--------------------------------------------------------------------------------
/app/libs/libvlc-all-3.5.1-https.aar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sifacaii/VlcDlnaPlayer/9fdb6db26056f20201bb246cea116d258ad16d10/app/libs/libvlc-all-3.5.1-https.aar
--------------------------------------------------------------------------------
/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
--------------------------------------------------------------------------------
/app/src/androidTest/java/org/sifacaii/vlcdlnaplayer/ExampleInstrumentedTest.java:
--------------------------------------------------------------------------------
1 | package org.sifacaii.vlcdlnaplayer;
2 |
3 | import android.content.Context;
4 |
5 | import androidx.test.platform.app.InstrumentationRegistry;
6 | import androidx.test.ext.junit.runners.AndroidJUnit4;
7 |
8 | import org.junit.Test;
9 | import org.junit.runner.RunWith;
10 |
11 | import static org.junit.Assert.*;
12 |
13 | /**
14 | * Instrumented test, which will execute on an Android device.
15 | *
16 | * @see Testing documentation
17 | */
18 | @RunWith(AndroidJUnit4.class)
19 | public class ExampleInstrumentedTest {
20 | @Test
21 | public void useAppContext() {
22 | // Context of the app under test.
23 | Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
24 | assertEquals("org.sifacaii.vlcdlnaplayer", appContext.getPackageName());
25 | }
26 | }
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
21 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
--------------------------------------------------------------------------------
/app/src/main/assets/upnp/description - 副本.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 1
5 | 0
6 |
7 |
8 |
9 | DMS-1.50
10 |
11 | M-DMS-1.50
12 |
13 | urn:schemas-upnp-org:device:MediaRenderer:1
14 | sifacai
15 | https://github.com/
16 | DLNA Media Server For VLC
17 |
18 |
19 | https://github.com
20 |
21 | smi,DCM10,getMediaInfo.sec,getCaptionInfo.sec
22 | smi,DCM10,getMediaInfo.sec,getCaptionInfo.sec
23 |
24 |
25 | image/jpeg
26 | 48
27 | 48
28 | 24
29 | /icon/smallJPEG
30 |
31 |
32 | image/png
33 | 48
34 | 48
35 | 24
36 | /icon/smallPNG
37 |
38 |
39 | image/png
40 | 120
41 | 120
42 | 24
43 | /icon/largePNG
44 |
45 |
46 | image/jpeg
47 | 120
48 | 120
49 | 24
50 | /icon/largeJPEG
51 |
52 |
53 |
54 |
55 | urn:schemas-upnp-org:service:ContentDirectory:1
56 | urn:upnp-org:serviceId:ContentDirectory
57 | /contentDirectory.xml
58 | /serviceControl
59 |
60 |
61 |
62 | urn:schemas-upnp-org:service:ConnectionManager:1
63 | urn:upnp-org:serviceId:ConnectionManager
64 | /connectionManager.xml
65 | /serviceControl
66 |
67 |
68 |
69 | urn:schemas-upnp-org:service:X_MS_MediaReceiverRegistrar:1
70 | urn:microsoft.com:serviceId:X_MS_MediaReceiverRegistrar
71 | /MSMediaReceiverRegistrar.xml
72 | /serviceControl
73 |
74 |
75 |
76 |
77 |
--------------------------------------------------------------------------------
/app/src/main/assets/upnp/description.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 1
5 | 0
6 |
7 |
8 | VLC投屏播放器
9 | urn:schemas-upnp-org:device:MediaRenderer:1
10 | DMR-1.50
11 |
12 |
13 | urn:schemas-upnp-org:service:AVTransport:1
14 | urn:upnp-org:serviceId:AVTransport
15 | control
16 | scpd.xml
17 | event
18 |
19 |
20 | LIBVLC
21 | http://
22 | VLC Media Renderer
23 | VLC Media Player
24 | http://
25 |
26 |
27 | image/png
28 | 94
29 | 94
30 | 8
31 |
32 |
33 |
34 | uuid:VLC_DLNA_PLAYER_UPNP_1_0_202305065544
35 |
36 |
--------------------------------------------------------------------------------
/app/src/main/assets/upnp/scpd - 副本.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 1
5 | 0
6 |
7 |
8 |
9 | GetCurrentTransportActions
10 |
11 |
12 | InstanceID
13 | in
14 | A_ARG_TYPE_InstanceID
15 |
16 |
17 | Actions
18 | out
19 | CurrentTransportActions
20 |
21 |
22 |
23 |
24 | GetDeviceCapabilities
25 |
26 |
27 | InstanceID
28 | in
29 | A_ARG_TYPE_InstanceID
30 |
31 |
32 | PlayMedia
33 | out
34 | PossiblePlaybackStorageMedia
35 |
36 |
37 | RecMedia
38 | out
39 | PossibleRecordStorageMedia
40 |
41 |
42 | RecQualityModes
43 | out
44 | PossibleRecordQualityModes
45 |
46 |
47 |
48 |
49 | GetMediaInfo
50 |
51 |
52 | InstanceID
53 | in
54 | A_ARG_TYPE_InstanceID
55 |
56 |
57 | NrTracks
58 | out
59 | NumberOfTracks
60 |
61 |
62 | MediaDuration
63 | out
64 | CurrentMediaDuration
65 |
66 |
67 | CurrentURI
68 | out
69 | AVTransportURI
70 |
71 |
72 | CurrentURIMetaData
73 | out
74 | AVTransportURIMetaData
75 |
76 |
77 | NextURI
78 | out
79 | NextAVTransportURI
80 |
81 |
82 | NextURIMetaData
83 | out
84 | NextAVTransportURIMetaData
85 |
86 |
87 | PlayMedium
88 | out
89 | PlaybackStorageMedium
90 |
91 |
92 | RecordMedium
93 | out
94 | RecordStorageMedium
95 |
96 |
97 | WriteStatus
98 | out
99 | RecordMediumWriteStatus
100 |
101 |
102 |
103 |
104 | GetPositionInfo
105 |
106 |
107 | InstanceID
108 | in
109 | A_ARG_TYPE_InstanceID
110 |
111 |
112 | Track
113 | out
114 | CurrentTrack
115 |
116 |
117 | TrackDuration
118 | out
119 | CurrentTrackDuration
120 |
121 |
122 | TrackMetaData
123 | out
124 | CurrentTrackMetaData
125 |
126 |
127 | TrackURI
128 | out
129 | CurrentTrackURI
130 |
131 |
132 | RelTime
133 | out
134 | RelativeTimePosition
135 |
136 |
137 | AbsTime
138 | out
139 | AbsoluteTimePosition
140 |
141 |
142 | RelCount
143 | out
144 | RelativeCounterPosition
145 |
146 |
147 | AbsCount
148 | out
149 | AbsoluteCounterPosition
150 |
151 |
152 |
153 |
154 | GetTransportInfo
155 |
156 |
157 | InstanceID
158 | in
159 | A_ARG_TYPE_InstanceID
160 |
161 |
162 | CurrentTransportState
163 | out
164 | TransportState
165 |
166 |
167 | CurrentTransportStatus
168 | out
169 | TransportStatus
170 |
171 |
172 | CurrentSpeed
173 | out
174 | TransportPlaySpeed
175 |
176 |
177 |
178 |
179 | GetTransportSettings
180 |
181 |
182 | InstanceID
183 | in
184 | A_ARG_TYPE_InstanceID
185 |
186 |
187 | PlayMode
188 | out
189 | CurrentPlayMode
190 |
191 |
192 | RecQualityMode
193 | out
194 | CurrentRecordQualityMode
195 |
196 |
197 |
198 |
199 | Next
200 |
201 |
202 | InstanceID
203 | in
204 | A_ARG_TYPE_InstanceID
205 |
206 |
207 |
208 |
209 | Pause
210 |
211 |
212 | InstanceID
213 | in
214 | A_ARG_TYPE_InstanceID
215 |
216 |
217 |
218 |
219 | Play
220 |
221 |
222 | InstanceID
223 | in
224 | A_ARG_TYPE_InstanceID
225 |
226 |
227 | Speed
228 | in
229 | TransportPlaySpeed
230 |
231 |
232 |
233 |
234 | Previous
235 |
236 |
237 | InstanceID
238 | in
239 | A_ARG_TYPE_InstanceID
240 |
241 |
242 |
243 |
244 | Seek
245 |
246 |
247 | InstanceID
248 | in
249 | A_ARG_TYPE_InstanceID
250 |
251 |
252 | Unit
253 | in
254 | A_ARG_TYPE_SeekMode
255 |
256 |
257 | Target
258 | in
259 | A_ARG_TYPE_SeekTarget
260 |
261 |
262 |
263 |
264 | SetAVTransportURI
265 |
266 |
267 | InstanceID
268 | in
269 | A_ARG_TYPE_InstanceID
270 |
271 |
272 | CurrentURI
273 | in
274 | AVTransportURI
275 |
276 |
277 | CurrentURIMetaData
278 | in
279 | AVTransportURIMetaData
280 |
281 |
282 |
283 |
284 | SetPlayMode
285 |
286 |
287 | InstanceID
288 | in
289 | A_ARG_TYPE_InstanceID
290 |
291 |
292 | NewPlayMode
293 | in
294 | CurrentPlayMode
295 |
296 |
297 |
298 |
299 | Stop
300 |
301 |
302 | InstanceID
303 | in
304 | A_ARG_TYPE_InstanceID
305 |
306 |
307 |
308 |
309 |
310 |
311 | CurrentPlayMode
312 | string
313 | NORMAL
314 |
315 | NORMAL
316 | REPEAT_ONE
317 | REPEAT_ALL
318 | SHUFFLE
319 | SHUFFLE_NOREPEAT
320 |
321 |
322 |
323 | RecordStorageMedium
324 | string
325 |
326 | NOT_IMPLEMENTED
327 |
328 |
329 |
330 | LastChange
331 | string
332 |
333 |
334 | RelativeTimePosition
335 | string
336 |
337 |
338 | CurrentTrackURI
339 | string
340 |
341 |
342 | CurrentTrackDuration
343 | string
344 |
345 |
346 | CurrentRecordQualityMode
347 | string
348 |
349 | NOT_IMPLEMENTED
350 |
351 |
352 |
353 | CurrentMediaDuration
354 | string
355 |
356 |
357 | AbsoluteCounterPosition
358 | i4
359 |
360 |
361 | RelativeCounterPosition
362 | i4
363 |
364 |
365 | A_ARG_TYPE_InstanceID
366 | ui4
367 |
368 |
369 | AVTransportURI
370 | string
371 |
372 |
373 | TransportState
374 | string
375 |
376 | STOPPED
377 | PAUSED_PLAYBACK
378 | PLAYING
379 | TRANSITIONING
380 | NO_MEDIA_PRESENT
381 |
382 |
383 |
384 | CurrentTrackMetaData
385 | string
386 |
387 |
388 | NextAVTransportURI
389 | string
390 |
391 |
392 | PossibleRecordQualityModes
393 | string
394 |
395 | NOT_IMPLEMENTED
396 |
397 |
398 |
399 | CurrentTrack
400 | ui4
401 |
402 | 0
403 | 65535
404 | 1
405 |
406 |
407 |
408 | AbsoluteTimePosition
409 | string
410 |
411 |
412 | NextAVTransportURIMetaData
413 | string
414 |
415 |
416 | PlaybackStorageMedium
417 | string
418 |
419 | NONE
420 | UNKNOWN
421 | CD-DA
422 | HDD
423 | NETWORK
424 |
425 |
426 |
427 | CurrentTransportActions
428 | string
429 |
430 |
431 | RecordMediumWriteStatus
432 | string
433 |
434 | NOT_IMPLEMENTED
435 |
436 |
437 |
438 | PossiblePlaybackStorageMedia
439 | string
440 |
441 | NONE
442 | UNKNOWN
443 | CD-DA
444 | HDD
445 | NETWORK
446 |
447 |
448 |
449 | AVTransportURIMetaData
450 | string
451 |
452 |
453 | NumberOfTracks
454 | ui4
455 |
456 | 0
457 | 65535
458 |
459 |
460 |
461 | A_ARG_TYPE_SeekMode
462 | string
463 |
464 | REL_TIME
465 | TRACK_NR
466 |
467 |
468 |
469 | A_ARG_TYPE_SeekTarget
470 | string
471 |
472 |
473 | PossibleRecordStorageMedia
474 | string
475 |
476 | NOT_IMPLEMENTED
477 |
478 |
479 |
480 | TransportStatus
481 | string
482 |
483 | OK
484 | ERROR_OCCURRED
485 |
486 |
487 |
488 | TransportPlaySpeed
489 | string
490 |
491 | 1
492 |
493 |
494 |
495 |
--------------------------------------------------------------------------------
/app/src/main/assets/upnp/scpd.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 1
5 | 0
6 |
7 |
8 |
9 | GetCurrentTransportActions
10 |
11 |
12 | InstanceID
13 | in
14 | A_ARG_TYPE_InstanceID
15 |
16 |
17 | Actions
18 | out
19 | CurrentTransportActions
20 |
21 |
22 |
23 |
24 | GetDeviceCapabilities
25 |
26 |
27 | InstanceID
28 | in
29 | A_ARG_TYPE_InstanceID
30 |
31 |
32 | PlayMedia
33 | out
34 | PossiblePlaybackStorageMedia
35 |
36 |
37 | RecMedia
38 | out
39 | PossibleRecordStorageMedia
40 |
41 |
42 | RecQualityModes
43 | out
44 | PossibleRecordQualityModes
45 |
46 |
47 |
48 |
49 | GetMediaInfo
50 |
51 |
52 | InstanceID
53 | in
54 | A_ARG_TYPE_InstanceID
55 |
56 |
57 | NrTracks
58 | out
59 | NumberOfTracks
60 |
61 |
62 | MediaDuration
63 | out
64 | CurrentMediaDuration
65 |
66 |
67 | CurrentURI
68 | out
69 | AVTransportURI
70 |
71 |
72 | CurrentURIMetaData
73 | out
74 | AVTransportURIMetaData
75 |
76 |
77 | NextURI
78 | out
79 | NextAVTransportURI
80 |
81 |
82 | NextURIMetaData
83 | out
84 | NextAVTransportURIMetaData
85 |
86 |
87 | PlayMedium
88 | out
89 | PlaybackStorageMedium
90 |
91 |
92 | RecordMedium
93 | out
94 | RecordStorageMedium
95 |
96 |
97 | WriteStatus
98 | out
99 | RecordMediumWriteStatus
100 |
101 |
102 |
103 |
104 | GetPositionInfo
105 |
106 |
107 | InstanceID
108 | in
109 | A_ARG_TYPE_InstanceID
110 |
111 |
112 | Track
113 | out
114 | CurrentTrack
115 |
116 |
117 | TrackDuration
118 | out
119 | CurrentTrackDuration
120 |
121 |
122 | TrackMetaData
123 | out
124 | CurrentTrackMetaData
125 |
126 |
127 | TrackURI
128 | out
129 | CurrentTrackURI
130 |
131 |
132 | RelTime
133 | out
134 | RelativeTimePosition
135 |
136 |
137 | AbsTime
138 | out
139 | AbsoluteTimePosition
140 |
141 |
142 | RelCount
143 | out
144 | RelativeCounterPosition
145 |
146 |
147 | AbsCount
148 | out
149 | AbsoluteCounterPosition
150 |
151 |
152 |
153 |
154 | GetTransportInfo
155 |
156 |
157 | InstanceID
158 | in
159 | A_ARG_TYPE_InstanceID
160 |
161 |
162 | CurrentTransportState
163 | out
164 | TransportState
165 |
166 |
167 | CurrentTransportStatus
168 | out
169 | TransportStatus
170 |
171 |
172 | CurrentSpeed
173 | out
174 | TransportPlaySpeed
175 |
176 |
177 |
178 |
179 | GetTransportSettings
180 |
181 |
182 | InstanceID
183 | in
184 | A_ARG_TYPE_InstanceID
185 |
186 |
187 | PlayMode
188 | out
189 | CurrentPlayMode
190 |
191 |
192 | RecQualityMode
193 | out
194 | CurrentRecordQualityMode
195 |
196 |
197 |
198 |
199 | Next
200 |
201 |
202 | InstanceID
203 | in
204 | A_ARG_TYPE_InstanceID
205 |
206 |
207 |
208 |
209 | Pause
210 |
211 |
212 | InstanceID
213 | in
214 | A_ARG_TYPE_InstanceID
215 |
216 |
217 |
218 |
219 | Play
220 |
221 |
222 | InstanceID
223 | in
224 | A_ARG_TYPE_InstanceID
225 |
226 |
227 | Speed
228 | in
229 | TransportPlaySpeed
230 |
231 |
232 |
233 |
234 | Previous
235 |
236 |
237 | InstanceID
238 | in
239 | A_ARG_TYPE_InstanceID
240 |
241 |
242 |
243 |
244 | Seek
245 |
246 |
247 | InstanceID
248 | in
249 | A_ARG_TYPE_InstanceID
250 |
251 |
252 | Unit
253 | in
254 | A_ARG_TYPE_SeekMode
255 |
256 |
257 | Target
258 | in
259 | A_ARG_TYPE_SeekTarget
260 |
261 |
262 |
263 |
264 | SetAVTransportURI
265 |
266 |
267 | InstanceID
268 | in
269 | A_ARG_TYPE_InstanceID
270 |
271 |
272 | CurrentURI
273 | in
274 | AVTransportURI
275 |
276 |
277 | CurrentURIMetaData
278 | in
279 | AVTransportURIMetaData
280 |
281 |
282 |
283 |
284 | SetPlayMode
285 |
286 |
287 | InstanceID
288 | in
289 | A_ARG_TYPE_InstanceID
290 |
291 |
292 | NewPlayMode
293 | in
294 | CurrentPlayMode
295 |
296 |
297 |
298 |
299 | Stop
300 |
301 |
302 | InstanceID
303 | in
304 | A_ARG_TYPE_InstanceID
305 |
306 |
307 |
308 |
309 |
310 |
311 | CurrentPlayMode
312 | string
313 | NORMAL
314 |
315 | NORMAL
316 | REPEAT_ONE
317 | REPEAT_ALL
318 | SHUFFLE
319 | SHUFFLE_NOREPEAT
320 |
321 |
322 |
323 | RecordStorageMedium
324 | string
325 |
326 | NOT_IMPLEMENTED
327 |
328 |
329 |
330 | LastChange
331 | string
332 |
333 |
334 | RelativeTimePosition
335 | string
336 |
337 |
338 | CurrentTrackURI
339 | string
340 |
341 |
342 | CurrentTrackDuration
343 | string
344 |
345 |
346 | CurrentRecordQualityMode
347 | string
348 |
349 | NOT_IMPLEMENTED
350 |
351 |
352 |
353 | CurrentMediaDuration
354 | string
355 |
356 |
357 | AbsoluteCounterPosition
358 | i4
359 |
360 |
361 | RelativeCounterPosition
362 | i4
363 |
364 |
365 | A_ARG_TYPE_InstanceID
366 | ui4
367 |
368 |
369 | AVTransportURI
370 | string
371 |
372 |
373 | TransportState
374 | string
375 |
376 | STOPPED
377 | PAUSED_PLAYBACK
378 | PLAYING
379 | TRANSITIONING
380 | NO_MEDIA_PRESENT
381 |
382 |
383 |
384 | CurrentTrackMetaData
385 | string
386 |
387 |
388 | NextAVTransportURI
389 | string
390 |
391 |
392 | PossibleRecordQualityModes
393 | string
394 |
395 | NOT_IMPLEMENTED
396 |
397 |
398 |
399 | CurrentTrack
400 | ui4
401 |
402 | 0
403 | 65535
404 | 1
405 |
406 |
407 |
408 | AbsoluteTimePosition
409 | string
410 |
411 |
412 | NextAVTransportURIMetaData
413 | string
414 |
415 |
416 | PlaybackStorageMedium
417 | string
418 |
419 | NONE
420 | UNKNOWN
421 | CD-DA
422 | HDD
423 | NETWORK
424 |
425 |
426 |
427 | CurrentTransportActions
428 | string
429 |
430 |
431 | RecordMediumWriteStatus
432 | string
433 |
434 | NOT_IMPLEMENTED
435 |
436 |
437 |
438 | PossiblePlaybackStorageMedia
439 | string
440 |
441 | NONE
442 | UNKNOWN
443 | CD-DA
444 | HDD
445 | NETWORK
446 |
447 |
448 |
449 | AVTransportURIMetaData
450 | string
451 |
452 |
453 | NumberOfTracks
454 | ui4
455 |
456 | 0
457 | 65535
458 |
459 |
460 |
461 | A_ARG_TYPE_SeekMode
462 | string
463 |
464 | REL_TIME
465 | TRACK_NR
466 |
467 |
468 |
469 | A_ARG_TYPE_SeekTarget
470 | string
471 |
472 |
473 | PossibleRecordStorageMedia
474 | string
475 |
476 | NOT_IMPLEMENTED
477 |
478 |
479 |
480 | TransportStatus
481 | string
482 |
483 | OK
484 | ERROR_OCCURRED
485 |
486 |
487 |
488 | TransportPlaySpeed
489 | string
490 |
491 | 1
492 |
493 |
494 |
495 |
--------------------------------------------------------------------------------
/app/src/main/ic_launcher-playstore.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sifacaii/VlcDlnaPlayer/9fdb6db26056f20201bb246cea116d258ad16d10/app/src/main/ic_launcher-playstore.png
--------------------------------------------------------------------------------
/app/src/main/java/org/sifacaii/vlcdlnaplayer/Config.java:
--------------------------------------------------------------------------------
1 | package org.sifacaii.vlcdlnaplayer;
2 |
3 | public class Config {
4 | public static final String KEY_HACC = "ISHACC";
5 | public static final String KEY_ForceHACC = "ISForceHACC";
6 | }
7 |
--------------------------------------------------------------------------------
/app/src/main/java/org/sifacaii/vlcdlnaplayer/MainActivity.java:
--------------------------------------------------------------------------------
1 | package org.sifacaii.vlcdlnaplayer;
2 |
3 | import androidx.appcompat.app.AppCompatActivity;
4 |
5 | import android.annotation.SuppressLint;
6 | import android.app.AlertDialog;
7 | import android.content.DialogInterface;
8 | import android.os.Bundle;
9 | import android.util.Log;
10 | import android.view.KeyEvent;
11 | import android.view.View;
12 | import android.widget.CheckBox;
13 | import android.widget.FrameLayout;
14 | import android.widget.TextView;
15 |
16 | import com.orhanobut.hawk.Hawk;
17 |
18 | import org.sifacaii.vlcdlnaplayer.dlna.AVTransport;
19 | import org.sifacaii.vlcdlnaplayer.dlna.SSDP;
20 | import org.sifacaii.vlcdlnaplayer.vlcplayer.Player;
21 | import org.sifacaii.vlcdlnaplayer.vlcplayer.Video;
22 |
23 | import java.util.HashMap;
24 |
25 | public class MainActivity extends AppCompatActivity {
26 |
27 | private String TAG = "主窗口:";
28 |
29 | private CheckBox box_hacc;
30 | private CheckBox box_forcehacc;
31 | private TextView textView_logs;
32 | private FrameLayout play_container;
33 | private Player player;
34 |
35 | @SuppressLint("MissingInflatedId")
36 | @Override
37 | protected void onCreate(Bundle savedInstanceState) {
38 | super.onCreate(savedInstanceState);
39 | setContentView(R.layout.activity_main);
40 |
41 | Hawk.init(getApplicationContext()).build();
42 |
43 | box_hacc = findViewById(R.id.box_hacc);
44 | box_forcehacc = findViewById(R.id.box_forcehacc);
45 | textView_logs = findViewById(R.id.textview_logs);
46 | play_container = findViewById(R.id.play_container);
47 |
48 | box_hacc.setChecked(Hawk.get(Config.KEY_HACC,false));
49 | box_forcehacc.setChecked(Hawk.get(Config.KEY_ForceHACC,false));
50 |
51 | box_hacc.setOnClickListener(new View.OnClickListener() {
52 | @Override
53 | public void onClick(View view) {
54 | Hawk.put(Config.KEY_HACC,box_hacc.isChecked());
55 | }
56 | });
57 |
58 | box_forcehacc.setOnClickListener(new View.OnClickListener() {
59 | @Override
60 | public void onClick(View view) {
61 | Hawk.put(Config.KEY_ForceHACC,box_forcehacc.isChecked());
62 | }
63 | });
64 |
65 | DlnaInit();
66 | }
67 |
68 | protected void DlnaInit() {
69 | SSDP ssdp = new SSDP(this);
70 | AVTransport avTransport = AVTransport.getInstance(this);
71 | avTransport.setPlayerControl(playerControl);
72 | ssdp.start();
73 | }
74 |
75 | private Player.PlayEvent playEvent = new Player.PlayEvent() {
76 | @Override
77 | public void onStart(long tricks) {
78 |
79 | }
80 |
81 | @Override
82 | public void onPause(boolean isPause) {
83 |
84 | }
85 |
86 | @Override
87 | public void onStop() {
88 | playerControl.Stop();
89 | }
90 |
91 | @Override
92 | public void onTimeChanged(long triks) {
93 |
94 | }
95 |
96 | @Override
97 | public void onMessage(String msg) {
98 |
99 | }
100 | };
101 |
102 | protected AVTransport.PlayerControl playerControl = new AVTransport.PlayerControl() {
103 | @Override
104 | public void SetAVTransportURI(String url, String metadata) {
105 | Video v = new Video();
106 | v.Hacc = Hawk.get(Config.KEY_HACC,false);
107 | v.ForceHacc = Hawk.get(Config.KEY_ForceHACC,false);
108 | v.Url = url;
109 |
110 | runOnUiThread(new Runnable() {
111 | @Override
112 | public void run() {
113 | if (player != null) {
114 | player.setMedia(v);
115 | } else {
116 | player = new Player(MainActivity.this);
117 | player.setPlayEvent(playEvent);
118 | FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(-1, -1);
119 | play_container.addView(player, lp);
120 | player.setMedia(v);
121 | }
122 | }
123 | });
124 | }
125 |
126 | @Override
127 | public void Play(String Speed) {
128 | if (player != null) player.start();
129 | }
130 |
131 | @Override
132 | public void Pause() {
133 | if (player != null) player.pause();
134 | }
135 |
136 | @Override
137 | public HashMap GetTransportInfo() {
138 | HashMap map = new HashMap<>();
139 | //if (player == null) return map;
140 | String state = "STOPPED";
141 | float speed = 1.0f;
142 | if(player == null){
143 | state = "STOPPED";
144 | }else{
145 | state = player.isPlaying() ? "PLAYING":"PAUSED_PLAYBACK";
146 | speed = player.getSpeed();
147 | }
148 | map.put("CurrentTransportState", state);
149 | map.put("CurrentTransportStatus", "OK");
150 | map.put("CurrentSpeed", String.valueOf(speed));
151 | return map;
152 | }
153 |
154 | @Override
155 | public void Seek(String Unit, String Target) {
156 | if(player != null) player.seekTo((int)TimeForstring(Target));
157 | }
158 |
159 | @Override
160 | public HashMap GetPositionInfo() {
161 | //if (player != null) return String.valueOf(player.getCurrentPosition());
162 | HashMap map = new HashMap<>();
163 | if (player == null) return map;
164 | map.put("Track", "0");
165 | map.put("TrackDuration", stringForTime(player.getDuration()));
166 | map.put("TrackMetaData", "");
167 | map.put("TrackURI", "");
168 | map.put("RelTime", stringForTime(player.getCurrentPosition()));
169 | map.put("AbsTime", "");
170 | map.put("RelCount", "");
171 | map.put("AbsCount", "");
172 | return map;
173 | }
174 |
175 | @Override
176 | public HashMap GetMediaInfo() {
177 | return null;
178 | }
179 |
180 | @Override
181 | public void Stop() {
182 | runOnUiThread(new Runnable() {
183 | @Override
184 | public void run() {
185 | if (player != null) {
186 | player.stop();
187 | player.release();
188 | play_container.removeView(player);
189 | player = null;
190 | //Log.d(TAG, "run: 停止");
191 | }
192 | }
193 | });
194 | }
195 | };
196 |
197 | private String stringForTime(long timeMs) {
198 | long totalSeconds = timeMs / 1000;
199 | long seconds = totalSeconds % 60;
200 | long minutes = (totalSeconds / 60) % 60;
201 | long hours = totalSeconds / 3600;
202 | return String.format("%02d:%02d:%02d", hours, minutes, seconds);
203 | }
204 |
205 | private long TimeForstring(String time) {
206 | String[] ts = time.split(":");
207 | long hours = Integer.parseInt(ts[0]) * 3600;
208 | long minutes = Integer.parseInt(ts[1]) * 60;
209 | long seconds = Integer.parseInt(ts[2]);
210 | long totalSeconds = (hours + minutes + seconds) * 1000;
211 | return totalSeconds;
212 | }
213 |
214 | protected void appExit() {
215 | AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this);
216 | builder.setMessage("确认退出?");
217 | builder.setTitle("确认退出");
218 | builder.setPositiveButton("退出", new DialogInterface.OnClickListener() {
219 | @Override
220 | public void onClick(DialogInterface dialog, int which) {
221 | dialog.dismiss();
222 | System.exit(0);
223 | }
224 | });
225 | builder.setNegativeButton("取消", new DialogInterface.OnClickListener() {
226 | @Override
227 | public void onClick(DialogInterface dialog, int which) {
228 | dialog.dismiss();
229 | }
230 | });
231 | builder.create().show();
232 | }
233 |
234 | @Override
235 | public void onBackPressed() {
236 | appExit();
237 | //super.onBackPressed();
238 | }
239 |
240 | @Override
241 | public boolean dispatchKeyEvent(KeyEvent event) {
242 | if(player != null){
243 | return player.dispatchKeyEvent(event);
244 | }
245 | return super.dispatchKeyEvent(event);
246 | }
247 | }
--------------------------------------------------------------------------------
/app/src/main/java/org/sifacaii/vlcdlnaplayer/MessageEvent.java:
--------------------------------------------------------------------------------
1 | package org.sifacaii.vlcdlnaplayer;
2 |
3 | import org.greenrobot.eventbus.Subscribe;
4 | import org.greenrobot.eventbus.ThreadMode;
5 |
6 | public class MessageEvent {
7 |
8 | public static enum Event{
9 | Play(1),
10 | Pause(2),
11 | Stop(3),
12 | TimeChanged(4),
13 | Message(5);
14 | Event(int i) {}
15 | };
16 |
17 | private Event event;
18 |
19 | public Event getEvent() {
20 | return event;
21 | }
22 |
23 | public void setEvent(Event event) {
24 | this.event = event;
25 | }
26 |
27 | private String MSG;
28 |
29 | public String getMSG() {
30 | return MSG;
31 | }
32 |
33 | public void setMSG(String MSG) {
34 | this.MSG = MSG;
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/app/src/main/java/org/sifacaii/vlcdlnaplayer/dlna/AVTransport.java:
--------------------------------------------------------------------------------
1 | package org.sifacaii.vlcdlnaplayer.dlna;
2 |
3 | import android.content.Context;
4 | import android.util.Log;
5 | import android.util.Xml;
6 |
7 | import org.xmlpull.v1.XmlPullParser;
8 | import org.xmlpull.v1.XmlPullParserException;
9 |
10 | import java.io.IOException;
11 | import java.io.InputStream;
12 | import java.io.StringReader;
13 | import java.util.HashMap;
14 | import java.util.Map;
15 |
16 | import fi.iki.elonen.NanoHTTPD;
17 |
18 | public class AVTransport extends NanoHTTPD {
19 | private String TAG = "AVTransport服务:";
20 |
21 | public static final String localAddress = Utils.getIpAddress4();
22 | public static final int port = 63636;
23 | private Context mContext;
24 | private PlayerControl playerControl;
25 |
26 | public void setContext(Context context) {
27 | mContext = context;
28 | }
29 |
30 | public static AVTransport getInstance(Context context) {
31 | AVTransport avTransport = new AVTransport(port);
32 | avTransport.setContext(context);
33 | try {
34 | avTransport.start();
35 | } catch (IOException e) {
36 | //Log.e("CONTROL", "getInstance: ", e);
37 | return null;
38 | }
39 | return avTransport;
40 | }
41 |
42 | public AVTransport(int port) {
43 | super(port);
44 | }
45 |
46 | public AVTransport(String hostname, int port) {
47 | super(hostname, port);
48 | }
49 |
50 | @Override
51 | public Response serve(IHTTPSession session) {
52 | String uri = session.getUri();
53 | if (session.getMethod() == Method.GET) {
54 | try {
55 | InputStream is = mContext.getAssets().open("upnp" + uri);
56 | return newFixedLengthResponse(Response.Status.OK, "text/xml", is, is.available());
57 | } catch (IOException e) {
58 | throw new RuntimeException(e);
59 | }
60 | }
61 | if (session.getMethod() == Method.POST) {
62 | String rsp = "";
63 | Map files = new HashMap<>();
64 | Map header = session.getHeaders();
65 | String soapaction = header.get("soapaction");
66 | String xml = "";
67 | try {
68 | session.parseBody(files);
69 | xml = files.get("postData");
70 | } catch (IOException e) {
71 | throw new RuntimeException(e);
72 | } catch (ResponseException e) {
73 | throw new RuntimeException(e);
74 | }
75 | if (uri.equals("/control")) {
76 | rsp = MediaControl(soapaction, xml);
77 | }
78 | //Log.d(TAG, "serve: " + uri + " rsp:" + rsp);
79 | return newFixedLengthResponse(Response.Status.OK, "text/xml", rsp);
80 | }
81 |
82 | return newFixedLengthResponse(Response.Status.NO_CONTENT, "text/xml", "");
83 | }
84 |
85 | private String MediaControl(String soapaction, String xml) {
86 | //Log.d(TAG, "MediaControl_" + soapaction + ": " + xml);
87 | if (playerControl == null) return "";
88 | if (xml == null || xml.length() < 1) return "";
89 |
90 | String rsp = "";
91 |
92 | HashMap x = null;
93 | try {
94 | x = ParseXML(xml);
95 | } catch (XmlPullParserException e) {
96 | throw new RuntimeException(e);
97 | } catch (IOException e) {
98 | throw new RuntimeException(e);
99 | }
100 | if (x == null) return rsp;
101 |
102 | String[] action = soapaction.split("#");
103 | if (action.length < 2) return rsp;
104 | //Log.d(TAG, "MediaControl: action:" + action[1] + " xml:" + x);
105 |
106 | String act = action[1].replace("\"", "");
107 | switch (act) {
108 | case "SetAVTransportURI":
109 | playerControl.SetAVTransportURI(x.get("CurrentURI"), x.get("CurrentURIMetaData"));
110 | break;
111 | case "Play":
112 | playerControl.Play(x.get("Speed"));
113 | break;
114 | case "Pause":
115 | playerControl.Pause();
116 | break;
117 | case "GetTransportInfo":
118 | rsp = getRspXML("GetTransportInfo", playerControl.GetTransportInfo());
119 | break;
120 | case "Seek":
121 | playerControl.Seek(x.get("Unit"), x.get("Target"));
122 | break;
123 | case "GetPositionInfo":
124 | rsp = getRspXML("GetPositionInfo", playerControl.GetPositionInfo());
125 | break;
126 | case "GetMediaInfo":
127 | rsp = getRspXML("GetMediaInfo", playerControl.GetMediaInfo());
128 | break;
129 | case "Stop":
130 | playerControl.Stop();
131 | break;
132 | }
133 | //Log.d(TAG, "MediaControl_rsp: " + rsp);
134 | return rsp;
135 | }
136 |
137 | public HashMap ParseXML(String xml) throws XmlPullParserException, IOException {
138 | HashMap result = new HashMap<>();
139 | XmlPullParser xmlPullParser = Xml.newPullParser();
140 | xmlPullParser.setInput(new StringReader(xml));
141 |
142 | int eventType = xmlPullParser.getEventType();
143 | String tagName = "";
144 | while (eventType != XmlPullParser.END_DOCUMENT) {
145 | switch (eventType) {
146 | case XmlPullParser.START_TAG:
147 | tagName = xmlPullParser.getName();
148 | break;
149 | case XmlPullParser.TEXT:
150 | String value = xmlPullParser.getText();
151 | if (value != null) {
152 | value = value.trim();
153 | if (!value.equals("")) {
154 | result.put(tagName, value);
155 | }
156 | }
157 | break;
158 | case XmlPullParser.END_TAG:
159 | break;
160 | }
161 | eventType = xmlPullParser.next();
162 | }
163 | return result;
164 | }
165 |
166 | private String getRspXML(String action, HashMap map) {
167 | String rsp = "" +
168 | "" +
170 | "" +
171 | "";
172 |
173 | if (map != null) {
174 | for (String key : map.keySet()) {
175 | rsp += "<" + key + ">" + map.get(key) + "" + key + ">";
176 | }
177 | }
178 |
179 | rsp += "" +
180 | "" +
181 | "";
182 |
183 | return rsp;
184 | }
185 |
186 | public void setPlayerControl(PlayerControl playerControl) {
187 | this.playerControl = playerControl;
188 | }
189 |
190 | public interface PlayerControl {
191 | void SetAVTransportURI(String url, String metadata);
192 |
193 | void Play(String Speed);
194 |
195 | void Pause();
196 |
197 | HashMap GetTransportInfo();
198 |
199 | void Seek(String Unit, String Target);
200 |
201 | HashMap GetPositionInfo();
202 |
203 | HashMap GetMediaInfo();
204 |
205 | void Stop();
206 | }
207 | }
208 |
--------------------------------------------------------------------------------
/app/src/main/java/org/sifacaii/vlcdlnaplayer/dlna/NOTIFY.java:
--------------------------------------------------------------------------------
1 | package org.sifacaii.vlcdlnaplayer.dlna;
2 |
3 | public class NOTIFY {
4 |
5 | public String CMD;
6 | public String HOST;
7 | public String ST;
8 | public String MAN;
9 | public String MX;
10 |
11 | public NOTIFY(String msg){
12 | String[] d = msg.split("\n");
13 | if(d.length > 0){
14 | CMD = d[0];
15 | }
16 | for(int i=1;i allNetInterfaces = NetworkInterface.getNetworkInterfaces();
17 | InetAddress ip = null;
18 | while (allNetInterfaces.hasMoreElements()) {
19 | NetworkInterface netInterface = (NetworkInterface) allNetInterfaces.nextElement();
20 | if (netInterface.isLoopback() || netInterface.isVirtual() || !netInterface.isUp()) {
21 | continue;
22 | } else {
23 | Enumeration addresses = netInterface.getInetAddresses();
24 | while (addresses.hasMoreElements()) {
25 | ip = addresses.nextElement();
26 | if (ip != null && ip instanceof Inet4Address) {
27 | return ip.getHostAddress();
28 | }
29 | }
30 | }
31 | }
32 | } catch (Exception e) {
33 | System.err.println("IP地址获取失败" + e.toString());
34 | }
35 | return "";
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/app/src/main/java/org/sifacaii/vlcdlnaplayer/vlcplayer/Controller.java:
--------------------------------------------------------------------------------
1 | package org.sifacaii.vlcdlnaplayer.vlcplayer;
2 |
3 |
4 | import android.content.Context;
5 | import android.content.res.Resources;
6 | import android.os.Build;
7 | import android.util.AttributeSet;
8 | import android.util.Log;
9 | import android.view.KeyEvent;
10 | import android.view.LayoutInflater;
11 | import android.view.View;
12 | import android.widget.FrameLayout;
13 | import android.widget.ImageButton;
14 | import android.widget.LinearLayout;
15 | import android.widget.PopupWindow;
16 | import android.widget.ProgressBar;
17 | import android.widget.TextView;
18 |
19 | import androidx.annotation.Nullable;
20 | import androidx.annotation.RequiresApi;
21 |
22 | import org.sifacaii.vlcdlnaplayer.R;
23 | import org.videolan.libvlc.MediaPlayer;
24 |
25 | import java.util.ArrayList;
26 |
27 | public class Controller extends FrameLayout implements View.OnClickListener {
28 | private String TAG = "播放器控制器";
29 |
30 |
31 | private int defaultTimeout = 6000;
32 | private Player player;
33 |
34 | private Context mContext;
35 |
36 | private LinearLayout mContainerTitle;
37 | private LinearLayout mContainerBottom;
38 | private LinearLayout mContainerSeek;
39 | private TextView mSeekTime;
40 | private ProgressBar mSeekProgress;
41 | private ProgressBar mBufferProgress;
42 |
43 | private ImageButton mPauseButton;
44 | private ImageButton mFfwdButton;
45 | private ImageButton mRewButton;
46 | private ImageButton mNextButton;
47 | private ImageButton mPrevButton;
48 | private TextView mRateButton;
49 | private ImageButton mScaleButton;
50 | private TextView mAudioButton;
51 | private TextView mSubtitleButton;
52 | private ImageButton mPlayListButton;
53 |
54 | private ProgressBar mProgress;
55 | private TextView mEndTime;
56 | private TextView mCurrentTime;
57 | private TextView mTitle;
58 |
59 | private boolean mShowing = false;
60 |
61 | private boolean mPopupMenuShowing = false;
62 | private PopMenuP mRateMenu;
63 | private PopMenuP mScaleMenu;
64 | private PopMenuP mAudioMenu;
65 | private PopMenuP mSubtitleMenu;
66 | private PopMenuP mPlayListMenu;
67 |
68 | public Controller(Context context) {
69 | super(context);
70 | mContext = context;
71 | init();
72 | }
73 |
74 | public Controller(Context context, @Nullable AttributeSet attrs) {
75 | super(context, attrs);
76 | mContext = context;
77 | init();
78 | }
79 |
80 | public Controller(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
81 | super(context, attrs, defStyleAttr);
82 | mContext = context;
83 | init();
84 | }
85 |
86 | @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
87 | public Controller(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
88 | super(context, attrs, defStyleAttr, defStyleRes);
89 | mContext = context;
90 | init();
91 | }
92 |
93 | private void init() {
94 | LayoutInflater.from(mContext).inflate(R.layout.controller, this);
95 |
96 | mContainerTitle = findViewById(R.id.container_title);
97 | mContainerBottom = findViewById(R.id.container_bottom);
98 | mContainerSeek = findViewById(R.id.container_seek);
99 | mSeekTime = findViewById(R.id.seek_time);
100 | mSeekProgress = findViewById(R.id.seek_progress);
101 | mBufferProgress = findViewById(R.id.buffer_Progress);
102 | mPauseButton = findViewById(R.id.pause);
103 | mFfwdButton = findViewById(R.id.ffwd);
104 | mRewButton = findViewById(R.id.rew);
105 | mNextButton = findViewById(R.id.next);
106 | mPrevButton = findViewById(R.id.prev);
107 | mRateButton = findViewById(R.id.rate);
108 | mScaleButton = findViewById(R.id.scale);
109 | mAudioButton = findViewById(R.id.audiotrack);
110 | mSubtitleButton = findViewById(R.id.subtitletrack);
111 | mPlayListButton = findViewById(R.id.playlist);
112 |
113 | mProgress = findViewById(R.id.mediacontroller_progress);
114 | mEndTime = findViewById(R.id.time);
115 | mCurrentTime = findViewById(R.id.time_current);
116 | mTitle = findViewById(R.id.title);
117 |
118 | mPauseButton.setOnClickListener(this);
119 | mFfwdButton.setOnClickListener(this);
120 | mRewButton.setOnClickListener(this);
121 | mNextButton.setOnClickListener(this);
122 | mPrevButton.setOnClickListener(this);
123 | mRateButton.setOnClickListener(this);
124 | mScaleButton.setOnClickListener(this);
125 | mAudioButton.setOnClickListener(this);
126 | mSubtitleButton.setOnClickListener(this);
127 | mPlayListButton.setOnClickListener(this);
128 | }
129 |
130 | public void setMediaPlayer(Player player) {
131 | this.player = player;
132 | initPopupMenu();
133 | }
134 |
135 | private void initPopupMenu() {
136 | // 速率
137 | mRateMenu = new PopMenuP(mContext, mRateButton);
138 | for (int i = 0; i < player.speedRate.length; i++) {
139 | mRateMenu.add(mRateButton.getId(), i, i, String.valueOf(player.speedRate[i]));
140 | }
141 | mRateMenu.setOnItemClickListener(new PopMenuP.OnItemClickListener() {
142 | @Override
143 | public void onClick(PopMenuP.menu m) {
144 | player.setSpeed(Float.valueOf(m.name));
145 | mRateButton.setText(m.name + "x");
146 | }
147 | });
148 | mRateMenu.setOnDismissListener(popupMenuDismissListener);
149 |
150 | // 画面缩放
151 | mScaleMenu = new PopMenuP(mContext, mScaleButton);
152 | for (int i = 0; i < player.scaleTypes.length; i++) {
153 | mScaleMenu.add(mScaleButton.getId(), i, i, player.scaleTypes[i].name());
154 | }
155 | mScaleMenu.setOnDismissListener(popupMenuDismissListener);
156 | mScaleMenu.setOnItemClickListener(new PopMenuP.OnItemClickListener() {
157 | @Override
158 | public void onClick(PopMenuP.menu m) {
159 | player.setScale(player.scaleTypes[m.id]);
160 | }
161 | });
162 | }
163 |
164 | public void initTrackList() {
165 | // 音轨
166 | MediaPlayer.TrackDescription[] audioTracks = player.getAudioSessions();
167 | if (mAudioButton != null && audioTracks != null) {
168 | mAudioMenu = new PopMenuP(mContext, mAudioButton);
169 | for (int i = 0; i < audioTracks.length; i++) {
170 | mAudioMenu.add(mAudioButton.getId(), audioTracks[i].id, i, audioTracks[i].name);
171 | }
172 | if (mAudioMenu.size() > 0) {
173 | mAudioMenu.setOnDismissListener(popupMenuDismissListener);
174 | mAudioMenu.setOnItemClickListener(new PopMenuP.OnItemClickListener() {
175 | @Override
176 | public void onClick(PopMenuP.menu m) {
177 | int audioid = m.id;
178 | if (audioid == 0) audioid = -1;
179 | player.setAudioSessionId(audioid);
180 | }
181 | });
182 | }
183 | }else{
184 | mAudioButton.setVisibility(GONE);
185 | }
186 |
187 | //字幕
188 | MediaPlayer.TrackDescription[] subtitleTracks = player.getSubtitleSessions();
189 | if (mSubtitleButton != null && subtitleTracks != null) {
190 | mSubtitleMenu = new PopMenuP(mContext, mSubtitleButton);
191 | for (int i = 0; i < subtitleTracks.length; i++) {
192 | mSubtitleMenu.add(mSubtitleButton.getId(), subtitleTracks[i].id, i, subtitleTracks[i].name);
193 | }
194 | if (mSubtitleMenu.size() > 0) {
195 | mSubtitleMenu.setOnDismissListener(popupMenuDismissListener);
196 | mSubtitleMenu.setOnItemClickListener(new PopMenuP.OnItemClickListener() {
197 | @Override
198 | public void onClick(PopMenuP.menu m) {
199 | int subid = m.id;
200 | if (subid == 0) subid = -1;
201 | player.setSubtitleSessionId(subid);
202 | }
203 | });
204 | }
205 | }else{
206 | mSubtitleButton.setVisibility(GONE);
207 | }
208 |
209 | // 播放列表
210 | ArrayList