├── .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 | 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) + ""; 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