react-native-queue-player 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +201 -0
- package/QueuePlayer.podspec +114 -0
- package/README.md +194 -0
- package/android/CMakeLists.txt +41 -0
- package/android/build.gradle +257 -0
- package/android/src/androidTest/java/com/margelo/nitro/queueplayer/SignalsmithStretchNativeTest.kt +136 -0
- package/android/src/main/AndroidManifest.xml +173 -0
- package/android/src/main/cpp/CMakeLists.txt +221 -0
- package/android/src/main/cpp/THIRD_PARTY_LICENSES +128 -0
- package/android/src/main/cpp/THIRD_PARTY_VERSIONS.txt +32 -0
- package/android/src/main/cpp/airplay2_audio.c +537 -0
- package/android/src/main/cpp/airplay2_audio.h +66 -0
- package/android/src/main/cpp/airplay2_control.c +680 -0
- package/android/src/main/cpp/airplay2_control.h +101 -0
- package/android/src/main/cpp/airplay2_crypto.c +548 -0
- package/android/src/main/cpp/airplay2_crypto.h +213 -0
- package/android/src/main/cpp/airplay2_dmap.c +81 -0
- package/android/src/main/cpp/airplay2_dmap.h +53 -0
- package/android/src/main/cpp/airplay2_filelog.h +100 -0
- package/android/src/main/cpp/airplay2_jni.cpp +579 -0
- package/android/src/main/cpp/airplay2_ntp.c +203 -0
- package/android/src/main/cpp/airplay2_ntp.h +39 -0
- package/android/src/main/cpp/airplay2_pair.c +653 -0
- package/android/src/main/cpp/airplay2_pair.h +114 -0
- package/android/src/main/cpp/airplay2_plist.c +840 -0
- package/android/src/main/cpp/airplay2_plist.h +184 -0
- package/android/src/main/cpp/airplay2_ptp.c +509 -0
- package/android/src/main/cpp/airplay2_ptp.h +40 -0
- package/android/src/main/cpp/airplay2_rtsp.c +693 -0
- package/android/src/main/cpp/airplay2_rtsp.h +90 -0
- package/android/src/main/cpp/airplay2_session.c +196 -0
- package/android/src/main/cpp/airplay2_session.h +197 -0
- package/android/src/main/cpp/airplay2_tlv.c +135 -0
- package/android/src/main/cpp/airplay2_tlv.h +87 -0
- package/android/src/main/cpp/airplay_jni.cpp +589 -0
- package/android/src/main/cpp/build-openssl-android.sh +94 -0
- package/android/src/main/cpp/cpp-adapter.cpp +11 -0
- package/android/src/main/cpp/prebuilt/libsodium/arm64-v8a/lib/libsodium.a +0 -0
- package/android/src/main/cpp/prebuilt/libsodium/include/sodium/core.h +28 -0
- package/android/src/main/cpp/prebuilt/libsodium/include/sodium/crypto_aead_aegis128l.h +92 -0
- package/android/src/main/cpp/prebuilt/libsodium/include/sodium/crypto_aead_aegis256.h +92 -0
- package/android/src/main/cpp/prebuilt/libsodium/include/sodium/crypto_aead_aes256gcm.h +179 -0
- package/android/src/main/cpp/prebuilt/libsodium/include/sodium/crypto_aead_chacha20poly1305.h +180 -0
- package/android/src/main/cpp/prebuilt/libsodium/include/sodium/crypto_aead_xchacha20poly1305.h +100 -0
- package/android/src/main/cpp/prebuilt/libsodium/include/sodium/crypto_auth.h +46 -0
- package/android/src/main/cpp/prebuilt/libsodium/include/sodium/crypto_auth_hmacsha256.h +70 -0
- package/android/src/main/cpp/prebuilt/libsodium/include/sodium/crypto_auth_hmacsha512.h +68 -0
- package/android/src/main/cpp/prebuilt/libsodium/include/sodium/crypto_auth_hmacsha512256.h +65 -0
- package/android/src/main/cpp/prebuilt/libsodium/include/sodium/crypto_box.h +177 -0
- package/android/src/main/cpp/prebuilt/libsodium/include/sodium/crypto_box_curve25519xchacha20poly1305.h +164 -0
- package/android/src/main/cpp/prebuilt/libsodium/include/sodium/crypto_box_curve25519xsalsa20poly1305.h +112 -0
- package/android/src/main/cpp/prebuilt/libsodium/include/sodium/crypto_core_ed25519.h +104 -0
- package/android/src/main/cpp/prebuilt/libsodium/include/sodium/crypto_core_hchacha20.h +36 -0
- package/android/src/main/cpp/prebuilt/libsodium/include/sodium/crypto_core_hsalsa20.h +36 -0
- package/android/src/main/cpp/prebuilt/libsodium/include/sodium/crypto_core_ristretto255.h +100 -0
- package/android/src/main/cpp/prebuilt/libsodium/include/sodium/crypto_core_salsa20.h +36 -0
- package/android/src/main/cpp/prebuilt/libsodium/include/sodium/crypto_core_salsa2012.h +36 -0
- package/android/src/main/cpp/prebuilt/libsodium/include/sodium/crypto_core_salsa208.h +40 -0
- package/android/src/main/cpp/prebuilt/libsodium/include/sodium/crypto_generichash.h +84 -0
- package/android/src/main/cpp/prebuilt/libsodium/include/sodium/crypto_generichash_blake2b.h +122 -0
- package/android/src/main/cpp/prebuilt/libsodium/include/sodium/crypto_hash.h +40 -0
- package/android/src/main/cpp/prebuilt/libsodium/include/sodium/crypto_hash_sha256.h +60 -0
- package/android/src/main/cpp/prebuilt/libsodium/include/sodium/crypto_hash_sha512.h +60 -0
- package/android/src/main/cpp/prebuilt/libsodium/include/sodium/crypto_kdf.h +53 -0
- package/android/src/main/cpp/prebuilt/libsodium/include/sodium/crypto_kdf_blake2b.h +44 -0
- package/android/src/main/cpp/prebuilt/libsodium/include/sodium/crypto_kdf_hkdf_sha256.h +74 -0
- package/android/src/main/cpp/prebuilt/libsodium/include/sodium/crypto_kdf_hkdf_sha512.h +75 -0
- package/android/src/main/cpp/prebuilt/libsodium/include/sodium/crypto_kx.h +66 -0
- package/android/src/main/cpp/prebuilt/libsodium/include/sodium/crypto_onetimeauth.h +65 -0
- package/android/src/main/cpp/prebuilt/libsodium/include/sodium/crypto_onetimeauth_poly1305.h +72 -0
- package/android/src/main/cpp/prebuilt/libsodium/include/sodium/crypto_pwhash.h +147 -0
- package/android/src/main/cpp/prebuilt/libsodium/include/sodium/crypto_pwhash_argon2i.h +122 -0
- package/android/src/main/cpp/prebuilt/libsodium/include/sodium/crypto_pwhash_argon2id.h +122 -0
- package/android/src/main/cpp/prebuilt/libsodium/include/sodium/crypto_pwhash_scryptsalsa208sha256.h +120 -0
- package/android/src/main/cpp/prebuilt/libsodium/include/sodium/crypto_scalarmult.h +46 -0
- package/android/src/main/cpp/prebuilt/libsodium/include/sodium/crypto_scalarmult_curve25519.h +42 -0
- package/android/src/main/cpp/prebuilt/libsodium/include/sodium/crypto_scalarmult_ed25519.h +51 -0
- package/android/src/main/cpp/prebuilt/libsodium/include/sodium/crypto_scalarmult_ristretto255.h +43 -0
- package/android/src/main/cpp/prebuilt/libsodium/include/sodium/crypto_secretbox.h +93 -0
- package/android/src/main/cpp/prebuilt/libsodium/include/sodium/crypto_secretbox_xchacha20poly1305.h +70 -0
- package/android/src/main/cpp/prebuilt/libsodium/include/sodium/crypto_secretbox_xsalsa20poly1305.h +69 -0
- package/android/src/main/cpp/prebuilt/libsodium/include/sodium/crypto_secretstream_xchacha20poly1305.h +108 -0
- package/android/src/main/cpp/prebuilt/libsodium/include/sodium/crypto_shorthash.h +41 -0
- package/android/src/main/cpp/prebuilt/libsodium/include/sodium/crypto_shorthash_siphash24.h +50 -0
- package/android/src/main/cpp/prebuilt/libsodium/include/sodium/crypto_sign.h +107 -0
- package/android/src/main/cpp/prebuilt/libsodium/include/sodium/crypto_sign_ed25519.h +124 -0
- package/android/src/main/cpp/prebuilt/libsodium/include/sodium/crypto_sign_edwards25519sha512batch.h +55 -0
- package/android/src/main/cpp/prebuilt/libsodium/include/sodium/crypto_stream.h +59 -0
- package/android/src/main/cpp/prebuilt/libsodium/include/sodium/crypto_stream_chacha20.h +106 -0
- package/android/src/main/cpp/prebuilt/libsodium/include/sodium/crypto_stream_salsa20.h +61 -0
- package/android/src/main/cpp/prebuilt/libsodium/include/sodium/crypto_stream_salsa2012.h +53 -0
- package/android/src/main/cpp/prebuilt/libsodium/include/sodium/crypto_stream_salsa208.h +56 -0
- package/android/src/main/cpp/prebuilt/libsodium/include/sodium/crypto_stream_xchacha20.h +61 -0
- package/android/src/main/cpp/prebuilt/libsodium/include/sodium/crypto_stream_xsalsa20.h +61 -0
- package/android/src/main/cpp/prebuilt/libsodium/include/sodium/crypto_verify_16.h +23 -0
- package/android/src/main/cpp/prebuilt/libsodium/include/sodium/crypto_verify_32.h +23 -0
- package/android/src/main/cpp/prebuilt/libsodium/include/sodium/crypto_verify_64.h +23 -0
- package/android/src/main/cpp/prebuilt/libsodium/include/sodium/export.h +57 -0
- package/android/src/main/cpp/prebuilt/libsodium/include/sodium/randombytes.h +72 -0
- package/android/src/main/cpp/prebuilt/libsodium/include/sodium/randombytes_internal_random.h +22 -0
- package/android/src/main/cpp/prebuilt/libsodium/include/sodium/randombytes_sysrandom.h +19 -0
- package/android/src/main/cpp/prebuilt/libsodium/include/sodium/runtime.h +55 -0
- package/android/src/main/cpp/prebuilt/libsodium/include/sodium/utils.h +179 -0
- package/android/src/main/cpp/prebuilt/libsodium/include/sodium/version.h +33 -0
- package/android/src/main/cpp/prebuilt/libsodium/include/sodium.h +75 -0
- package/android/src/main/cpp/prebuilt/libsodium/x86_64/lib/libsodium.a +0 -0
- package/android/src/main/cpp/prebuilt/openssl/arm64-v8a/lib/libcrypto.a +0 -0
- package/android/src/main/cpp/prebuilt/openssl/arm64-v8a/lib/libssl.a +0 -0
- package/android/src/main/cpp/prebuilt/openssl/x86_64/lib/libcrypto.a +0 -0
- package/android/src/main/cpp/prebuilt/openssl/x86_64/lib/libssl.a +0 -0
- package/android/src/main/cpp/stretch_jni.cpp +171 -0
- package/android/src/main/cpp/third_party/alac/addons/alac_wrapper.cpp +198 -0
- package/android/src/main/cpp/third_party/alac/addons/alac_wrapper.h +46 -0
- package/android/src/main/cpp/third_party/alac/codec/ALACAudioTypes.h +197 -0
- package/android/src/main/cpp/third_party/alac/codec/ALACBitUtilities.c +260 -0
- package/android/src/main/cpp/third_party/alac/codec/ALACBitUtilities.h +104 -0
- package/android/src/main/cpp/third_party/alac/codec/ALACDecoder.cpp +730 -0
- package/android/src/main/cpp/third_party/alac/codec/ALACDecoder.h +65 -0
- package/android/src/main/cpp/third_party/alac/codec/ALACEncoder.cpp +1425 -0
- package/android/src/main/cpp/third_party/alac/codec/ALACEncoder.h +92 -0
- package/android/src/main/cpp/third_party/alac/codec/EndianPortable.c +181 -0
- package/android/src/main/cpp/third_party/alac/codec/EndianPortable.h +59 -0
- package/android/src/main/cpp/third_party/alac/codec/ag_dec.c +362 -0
- package/android/src/main/cpp/third_party/alac/codec/ag_enc.c +370 -0
- package/android/src/main/cpp/third_party/alac/codec/aglib.h +81 -0
- package/android/src/main/cpp/third_party/alac/codec/dp_dec.c +381 -0
- package/android/src/main/cpp/third_party/alac/codec/dp_enc.c +386 -0
- package/android/src/main/cpp/third_party/alac/codec/dplib.h +61 -0
- package/android/src/main/cpp/third_party/alac/codec/matrix_dec.c +390 -0
- package/android/src/main/cpp/third_party/alac/codec/matrix_enc.c +342 -0
- package/android/src/main/cpp/third_party/alac/codec/matrixlib.h +80 -0
- package/android/src/main/cpp/third_party/crosstools/cross_log.c +95 -0
- package/android/src/main/cpp/third_party/crosstools/cross_log.h +25 -0
- package/android/src/main/cpp/third_party/crosstools/cross_net.c +1139 -0
- package/android/src/main/cpp/third_party/crosstools/cross_net.h +51 -0
- package/android/src/main/cpp/third_party/crosstools/cross_ssl.c +371 -0
- package/android/src/main/cpp/third_party/crosstools/cross_ssl.h +15 -0
- package/android/src/main/cpp/third_party/crosstools/cross_thread.c +134 -0
- package/android/src/main/cpp/third_party/crosstools/cross_thread.h +20 -0
- package/android/src/main/cpp/third_party/crosstools/cross_util.c +753 -0
- package/android/src/main/cpp/third_party/crosstools/cross_util.h +125 -0
- package/android/src/main/cpp/third_party/crosstools/platform.c +227 -0
- package/android/src/main/cpp/third_party/crosstools/platform.h +127 -0
- package/android/src/main/cpp/third_party/curve25519/include/curve25519_dh.h +53 -0
- package/android/src/main/cpp/third_party/curve25519/include/ed25519_signature.h +98 -0
- package/android/src/main/cpp/third_party/curve25519/include/external_calls.h +36 -0
- package/android/src/main/cpp/third_party/curve25519/source/BaseTypes.h +125 -0
- package/android/src/main/cpp/third_party/curve25519/source/base_folding8.h +1288 -0
- package/android/src/main/cpp/third_party/curve25519/source/curve25519_dh.c +208 -0
- package/android/src/main/cpp/third_party/curve25519/source/curve25519_mehdi.c +410 -0
- package/android/src/main/cpp/third_party/curve25519/source/curve25519_mehdi.h +175 -0
- package/android/src/main/cpp/third_party/curve25519/source/curve25519_order.c +155 -0
- package/android/src/main/cpp/third_party/curve25519/source/curve25519_utils.c +153 -0
- package/android/src/main/cpp/third_party/curve25519/source/custom_blind.c +27 -0
- package/android/src/main/cpp/third_party/curve25519/source/custom_blind.h +11 -0
- package/android/src/main/cpp/third_party/curve25519/source/ed25519_sign.c +419 -0
- package/android/src/main/cpp/third_party/curve25519/source/ed25519_signature.h +98 -0
- package/android/src/main/cpp/third_party/curve25519/source/ed25519_verify.c +313 -0
- package/android/src/main/cpp/third_party/curve25519/source/sha512.c +294 -0
- package/android/src/main/cpp/third_party/curve25519/source/sha512.h +93 -0
- package/android/src/main/cpp/third_party/dmap-parser/dmap_parser.c +545 -0
- package/android/src/main/cpp/third_party/dmap-parser/dmap_parser.h +90 -0
- package/android/src/main/cpp/third_party/libraop/aes.c +863 -0
- package/android/src/main/cpp/third_party/libraop/aes.h +18 -0
- package/android/src/main/cpp/third_party/libraop/aes_ctr.c +89 -0
- package/android/src/main/cpp/third_party/libraop/aes_ctr.h +59 -0
- package/android/src/main/cpp/third_party/libraop/alac.c +1135 -0
- package/android/src/main/cpp/third_party/libraop/alac.h +55 -0
- package/android/src/main/cpp/third_party/libraop/raop_client.c +1500 -0
- package/android/src/main/cpp/third_party/libraop/raop_client.h +178 -0
- package/android/src/main/cpp/third_party/libraop/rtsp_client.c +743 -0
- package/android/src/main/cpp/third_party/libraop/rtsp_client.h +50 -0
- package/android/src/main/cpp/third_party/openssl/include/openssl/__DECC_INCLUDE_EPILOGUE.H +22 -0
- package/android/src/main/cpp/third_party/openssl/include/openssl/__DECC_INCLUDE_PROLOGUE.H +26 -0
- package/android/src/main/cpp/third_party/openssl/include/openssl/aes.h +111 -0
- package/android/src/main/cpp/third_party/openssl/include/openssl/asn1.h +1134 -0
- package/android/src/main/cpp/third_party/openssl/include/openssl/asn1.h.in +967 -0
- package/android/src/main/cpp/third_party/openssl/include/openssl/asn1err.h +142 -0
- package/android/src/main/cpp/third_party/openssl/include/openssl/asn1t.h +946 -0
- package/android/src/main/cpp/third_party/openssl/include/openssl/asn1t.h.in +923 -0
- package/android/src/main/cpp/third_party/openssl/include/openssl/async.h +104 -0
- package/android/src/main/cpp/third_party/openssl/include/openssl/asyncerr.h +29 -0
- package/android/src/main/cpp/third_party/openssl/include/openssl/bio.h +1022 -0
- package/android/src/main/cpp/third_party/openssl/include/openssl/bio.h.in +999 -0
- package/android/src/main/cpp/third_party/openssl/include/openssl/bioerr.h +72 -0
- package/android/src/main/cpp/third_party/openssl/include/openssl/blowfish.h +78 -0
- package/android/src/main/cpp/third_party/openssl/include/openssl/bn.h +590 -0
- package/android/src/main/cpp/third_party/openssl/include/openssl/bnerr.h +47 -0
- package/android/src/main/cpp/third_party/openssl/include/openssl/buffer.h +62 -0
- package/android/src/main/cpp/third_party/openssl/include/openssl/buffererr.h +25 -0
- package/android/src/main/cpp/third_party/openssl/include/openssl/byteorder.h +339 -0
- package/android/src/main/cpp/third_party/openssl/include/openssl/camellia.h +117 -0
- package/android/src/main/cpp/third_party/openssl/include/openssl/cast.h +71 -0
- package/android/src/main/cpp/third_party/openssl/include/openssl/cmac.h +52 -0
- package/android/src/main/cpp/third_party/openssl/include/openssl/cmp.h +727 -0
- package/android/src/main/cpp/third_party/openssl/include/openssl/cmp.h.in +584 -0
- package/android/src/main/cpp/third_party/openssl/include/openssl/cmp_util.h +56 -0
- package/android/src/main/cpp/third_party/openssl/include/openssl/cmperr.h +134 -0
- package/android/src/main/cpp/third_party/openssl/include/openssl/cms.h +511 -0
- package/android/src/main/cpp/third_party/openssl/include/openssl/cms.h.in +413 -0
- package/android/src/main/cpp/third_party/openssl/include/openssl/cmserr.h +126 -0
- package/android/src/main/cpp/third_party/openssl/include/openssl/comp.h +98 -0
- package/android/src/main/cpp/third_party/openssl/include/openssl/comp.h.in +76 -0
- package/android/src/main/cpp/third_party/openssl/include/openssl/comperr.h +38 -0
- package/android/src/main/cpp/third_party/openssl/include/openssl/conf.h +214 -0
- package/android/src/main/cpp/third_party/openssl/include/openssl/conf.h.in +177 -0
- package/android/src/main/cpp/third_party/openssl/include/openssl/conf_api.h +46 -0
- package/android/src/main/cpp/third_party/openssl/include/openssl/conferr.h +52 -0
- package/android/src/main/cpp/third_party/openssl/include/openssl/configuration.h +212 -0
- package/android/src/main/cpp/third_party/openssl/include/openssl/configuration.h.in +75 -0
- package/android/src/main/cpp/third_party/openssl/include/openssl/conftypes.h +44 -0
- package/android/src/main/cpp/third_party/openssl/include/openssl/core.h +236 -0
- package/android/src/main/cpp/third_party/openssl/include/openssl/core_dispatch.h +1141 -0
- package/android/src/main/cpp/third_party/openssl/include/openssl/core_names.h +575 -0
- package/android/src/main/cpp/third_party/openssl/include/openssl/core_names.h.in +126 -0
- package/android/src/main/cpp/third_party/openssl/include/openssl/core_object.h +41 -0
- package/android/src/main/cpp/third_party/openssl/include/openssl/crmf.h +278 -0
- package/android/src/main/cpp/third_party/openssl/include/openssl/crmf.h.in +207 -0
- package/android/src/main/cpp/third_party/openssl/include/openssl/crmferr.h +57 -0
- package/android/src/main/cpp/third_party/openssl/include/openssl/crypto.h +583 -0
- package/android/src/main/cpp/third_party/openssl/include/openssl/crypto.h.in +560 -0
- package/android/src/main/cpp/third_party/openssl/include/openssl/cryptoerr.h +56 -0
- package/android/src/main/cpp/third_party/openssl/include/openssl/cryptoerr_legacy.h +1466 -0
- package/android/src/main/cpp/third_party/openssl/include/openssl/ct.h +573 -0
- package/android/src/main/cpp/third_party/openssl/include/openssl/ct.h.in +525 -0
- package/android/src/main/cpp/third_party/openssl/include/openssl/cterr.h +45 -0
- package/android/src/main/cpp/third_party/openssl/include/openssl/decoder.h +133 -0
- package/android/src/main/cpp/third_party/openssl/include/openssl/decodererr.h +28 -0
- package/android/src/main/cpp/third_party/openssl/include/openssl/des.h +211 -0
- package/android/src/main/cpp/third_party/openssl/include/openssl/dh.h +339 -0
- package/android/src/main/cpp/third_party/openssl/include/openssl/dherr.h +59 -0
- package/android/src/main/cpp/third_party/openssl/include/openssl/dsa.h +280 -0
- package/android/src/main/cpp/third_party/openssl/include/openssl/dsaerr.h +44 -0
- package/android/src/main/cpp/third_party/openssl/include/openssl/dtls1.h +57 -0
- package/android/src/main/cpp/third_party/openssl/include/openssl/e_os2.h +310 -0
- package/android/src/main/cpp/third_party/openssl/include/openssl/e_ostime.h +38 -0
- package/android/src/main/cpp/third_party/openssl/include/openssl/ebcdic.h +39 -0
- package/android/src/main/cpp/third_party/openssl/include/openssl/ec.h +1588 -0
- package/android/src/main/cpp/third_party/openssl/include/openssl/ecdh.h +10 -0
- package/android/src/main/cpp/third_party/openssl/include/openssl/ecdsa.h +10 -0
- package/android/src/main/cpp/third_party/openssl/include/openssl/ecerr.h +104 -0
- package/android/src/main/cpp/third_party/openssl/include/openssl/encoder.h +124 -0
- package/android/src/main/cpp/third_party/openssl/include/openssl/encodererr.h +28 -0
- package/android/src/main/cpp/third_party/openssl/include/openssl/engine.h +833 -0
- package/android/src/main/cpp/third_party/openssl/include/openssl/engineerr.h +63 -0
- package/android/src/main/cpp/third_party/openssl/include/openssl/err.h +512 -0
- package/android/src/main/cpp/third_party/openssl/include/openssl/err.h.in +501 -0
- package/android/src/main/cpp/third_party/openssl/include/openssl/ess.h +128 -0
- package/android/src/main/cpp/third_party/openssl/include/openssl/ess.h.in +81 -0
- package/android/src/main/cpp/third_party/openssl/include/openssl/esserr.h +32 -0
- package/android/src/main/cpp/third_party/openssl/include/openssl/evp.h +2310 -0
- package/android/src/main/cpp/third_party/openssl/include/openssl/evperr.h +142 -0
- package/android/src/main/cpp/third_party/openssl/include/openssl/fips_names.h +50 -0
- package/android/src/main/cpp/third_party/openssl/include/openssl/fipskey.h +41 -0
- package/android/src/main/cpp/third_party/openssl/include/openssl/fipskey.h.in +40 -0
- package/android/src/main/cpp/third_party/openssl/include/openssl/hmac.h +62 -0
- package/android/src/main/cpp/third_party/openssl/include/openssl/hpke.h +169 -0
- package/android/src/main/cpp/third_party/openssl/include/openssl/http.h +119 -0
- package/android/src/main/cpp/third_party/openssl/include/openssl/httperr.h +56 -0
- package/android/src/main/cpp/third_party/openssl/include/openssl/idea.h +82 -0
- package/android/src/main/cpp/third_party/openssl/include/openssl/indicator.h +31 -0
- package/android/src/main/cpp/third_party/openssl/include/openssl/kdf.h +138 -0
- package/android/src/main/cpp/third_party/openssl/include/openssl/kdferr.h +16 -0
- package/android/src/main/cpp/third_party/openssl/include/openssl/lhash.h +398 -0
- package/android/src/main/cpp/third_party/openssl/include/openssl/lhash.h.in +373 -0
- package/android/src/main/cpp/third_party/openssl/include/openssl/macros.h +349 -0
- package/android/src/main/cpp/third_party/openssl/include/openssl/md2.h +56 -0
- package/android/src/main/cpp/third_party/openssl/include/openssl/md4.h +63 -0
- package/android/src/main/cpp/third_party/openssl/include/openssl/md5.h +62 -0
- package/android/src/main/cpp/third_party/openssl/include/openssl/mdc2.h +55 -0
- package/android/src/main/cpp/third_party/openssl/include/openssl/ml_kem.h +31 -0
- package/android/src/main/cpp/third_party/openssl/include/openssl/modes.h +219 -0
- package/android/src/main/cpp/third_party/openssl/include/openssl/obj_mac.h +6636 -0
- package/android/src/main/cpp/third_party/openssl/include/openssl/objects.h +184 -0
- package/android/src/main/cpp/third_party/openssl/include/openssl/objectserr.h +28 -0
- package/android/src/main/cpp/third_party/openssl/include/openssl/ocsp.h +483 -0
- package/android/src/main/cpp/third_party/openssl/include/openssl/ocsp.h.in +387 -0
- package/android/src/main/cpp/third_party/openssl/include/openssl/ocsperr.h +53 -0
- package/android/src/main/cpp/third_party/openssl/include/openssl/opensslconf.h +17 -0
- package/android/src/main/cpp/third_party/openssl/include/openssl/opensslv.h +114 -0
- package/android/src/main/cpp/third_party/openssl/include/openssl/opensslv.h.in +113 -0
- package/android/src/main/cpp/third_party/openssl/include/openssl/ossl_typ.h +16 -0
- package/android/src/main/cpp/third_party/openssl/include/openssl/param_build.h +63 -0
- package/android/src/main/cpp/third_party/openssl/include/openssl/params.h +163 -0
- package/android/src/main/cpp/third_party/openssl/include/openssl/pem.h +547 -0
- package/android/src/main/cpp/third_party/openssl/include/openssl/pem2.h +19 -0
- package/android/src/main/cpp/third_party/openssl/include/openssl/pemerr.h +59 -0
- package/android/src/main/cpp/third_party/openssl/include/openssl/pkcs12.h +366 -0
- package/android/src/main/cpp/third_party/openssl/include/openssl/pkcs12.h.in +343 -0
- package/android/src/main/cpp/third_party/openssl/include/openssl/pkcs12err.h +46 -0
- package/android/src/main/cpp/third_party/openssl/include/openssl/pkcs7.h +430 -0
- package/android/src/main/cpp/third_party/openssl/include/openssl/pkcs7.h.in +359 -0
- package/android/src/main/cpp/third_party/openssl/include/openssl/pkcs7err.h +63 -0
- package/android/src/main/cpp/third_party/openssl/include/openssl/prov_ssl.h +38 -0
- package/android/src/main/cpp/third_party/openssl/include/openssl/proverr.h +169 -0
- package/android/src/main/cpp/third_party/openssl/include/openssl/provider.h +94 -0
- package/android/src/main/cpp/third_party/openssl/include/openssl/quic.h +75 -0
- package/android/src/main/cpp/third_party/openssl/include/openssl/rand.h +131 -0
- package/android/src/main/cpp/third_party/openssl/include/openssl/randerr.h +70 -0
- package/android/src/main/cpp/third_party/openssl/include/openssl/rc2.h +68 -0
- package/android/src/main/cpp/third_party/openssl/include/openssl/rc4.h +47 -0
- package/android/src/main/cpp/third_party/openssl/include/openssl/rc5.h +79 -0
- package/android/src/main/cpp/third_party/openssl/include/openssl/ripemd.h +59 -0
- package/android/src/main/cpp/third_party/openssl/include/openssl/rsa.h +615 -0
- package/android/src/main/cpp/third_party/openssl/include/openssl/rsaerr.h +107 -0
- package/android/src/main/cpp/third_party/openssl/include/openssl/safestack.h +297 -0
- package/android/src/main/cpp/third_party/openssl/include/openssl/safestack.h.in +227 -0
- package/android/src/main/cpp/third_party/openssl/include/openssl/seed.h +113 -0
- package/android/src/main/cpp/third_party/openssl/include/openssl/self_test.h +112 -0
- package/android/src/main/cpp/third_party/openssl/include/openssl/sha.h +139 -0
- package/android/src/main/cpp/third_party/openssl/include/openssl/srp.h +285 -0
- package/android/src/main/cpp/third_party/openssl/include/openssl/srp.h.in +214 -0
- package/android/src/main/cpp/third_party/openssl/include/openssl/srtp.h +68 -0
- package/android/src/main/cpp/third_party/openssl/include/openssl/ssl.h +2933 -0
- package/android/src/main/cpp/third_party/openssl/include/openssl/ssl.h.in +2886 -0
- package/android/src/main/cpp/third_party/openssl/include/openssl/ssl2.h +30 -0
- package/android/src/main/cpp/third_party/openssl/include/openssl/ssl3.h +358 -0
- package/android/src/main/cpp/third_party/openssl/include/openssl/sslerr.h +382 -0
- package/android/src/main/cpp/third_party/openssl/include/openssl/sslerr_legacy.h +467 -0
- package/android/src/main/cpp/third_party/openssl/include/openssl/stack.h +90 -0
- package/android/src/main/cpp/third_party/openssl/include/openssl/store.h +377 -0
- package/android/src/main/cpp/third_party/openssl/include/openssl/storeerr.h +49 -0
- package/android/src/main/cpp/third_party/openssl/include/openssl/symhacks.h +39 -0
- package/android/src/main/cpp/third_party/openssl/include/openssl/thread.h +31 -0
- package/android/src/main/cpp/third_party/openssl/include/openssl/tls1.h +1220 -0
- package/android/src/main/cpp/third_party/openssl/include/openssl/trace.h +321 -0
- package/android/src/main/cpp/third_party/openssl/include/openssl/ts.h +522 -0
- package/android/src/main/cpp/third_party/openssl/include/openssl/tserr.h +67 -0
- package/android/src/main/cpp/third_party/openssl/include/openssl/txt_db.h +63 -0
- package/android/src/main/cpp/third_party/openssl/include/openssl/types.h +248 -0
- package/android/src/main/cpp/third_party/openssl/include/openssl/ui.h +407 -0
- package/android/src/main/cpp/third_party/openssl/include/openssl/ui.h.in +384 -0
- package/android/src/main/cpp/third_party/openssl/include/openssl/uierr.h +38 -0
- package/android/src/main/cpp/third_party/openssl/include/openssl/whrlpool.h +62 -0
- package/android/src/main/cpp/third_party/openssl/include/openssl/x509.h +1303 -0
- package/android/src/main/cpp/third_party/openssl/include/openssl/x509.h.in +1109 -0
- package/android/src/main/cpp/third_party/openssl/include/openssl/x509_acert.h +294 -0
- package/android/src/main/cpp/third_party/openssl/include/openssl/x509_acert.h.in +199 -0
- package/android/src/main/cpp/third_party/openssl/include/openssl/x509_vfy.h +903 -0
- package/android/src/main/cpp/third_party/openssl/include/openssl/x509_vfy.h.in +806 -0
- package/android/src/main/cpp/third_party/openssl/include/openssl/x509err.h +70 -0
- package/android/src/main/cpp/third_party/openssl/include/openssl/x509v3.h +1968 -0
- package/android/src/main/cpp/third_party/openssl/include/openssl/x509v3.h.in +1367 -0
- package/android/src/main/cpp/third_party/openssl/include/openssl/x509v3err.h +97 -0
- package/android/src/main/cpp/third_party/pair_ap/pair-internal.h +390 -0
- package/android/src/main/cpp/third_party/pair_ap/pair-tlv.c +221 -0
- package/android/src/main/cpp/third_party/pair_ap/pair-tlv.h +77 -0
- package/android/src/main/cpp/third_party/pair_ap/pair.c +806 -0
- package/android/src/main/cpp/third_party/pair_ap/pair.h +266 -0
- package/android/src/main/cpp/third_party/pair_ap/pair_fruit_stub.c +36 -0
- package/android/src/main/cpp/third_party/pair_ap/pair_homekit.c +3190 -0
- package/android/src/main/cpp/third_party/signalsmith/LICENSE-signalsmith-linear.txt +21 -0
- package/android/src/main/cpp/third_party/signalsmith/LICENSE-signalsmith-stretch.txt +21 -0
- package/android/src/main/cpp/third_party/signalsmith/THIRD_PARTY_VERSIONS.txt +9 -0
- package/android/src/main/cpp/third_party/signalsmith/include/signalsmith-linear/fft.h +1380 -0
- package/android/src/main/cpp/third_party/signalsmith/include/signalsmith-linear/linear.h +1075 -0
- package/android/src/main/cpp/third_party/signalsmith/include/signalsmith-linear/stft.h +643 -0
- package/android/src/main/cpp/third_party/signalsmith/include/signalsmith-stretch.h +1060 -0
- package/android/src/main/java/com/margelo/nitro/queueplayer/AirPlayEngine.kt +314 -0
- package/android/src/main/java/com/margelo/nitro/queueplayer/ArtworkContentProvider.kt +124 -0
- package/android/src/main/java/com/margelo/nitro/queueplayer/AudioFocusErrorMapping.kt +89 -0
- package/android/src/main/java/com/margelo/nitro/queueplayer/AudioPipelineRenderersFactory.kt +60 -0
- package/android/src/main/java/com/margelo/nitro/queueplayer/BrowseCallbackRegistry.kt +136 -0
- package/android/src/main/java/com/margelo/nitro/queueplayer/BrowseDataProvider.kt +128 -0
- package/android/src/main/java/com/margelo/nitro/queueplayer/BufferStateTranslator.kt +35 -0
- package/android/src/main/java/com/margelo/nitro/queueplayer/CacheMimeTypes.kt +79 -0
- package/android/src/main/java/com/margelo/nitro/queueplayer/CastManager.kt +629 -0
- package/android/src/main/java/com/margelo/nitro/queueplayer/CrossfadeEngine.kt +1350 -0
- package/android/src/main/java/com/margelo/nitro/queueplayer/CrossfadeForwardingPlayer.kt +66 -0
- package/android/src/main/java/com/margelo/nitro/queueplayer/CustomPresetStore.kt +100 -0
- package/android/src/main/java/com/margelo/nitro/queueplayer/Equalizer.kt +348 -0
- package/android/src/main/java/com/margelo/nitro/queueplayer/EqualizerEngine.kt +222 -0
- package/android/src/main/java/com/margelo/nitro/queueplayer/EqualizerLegacyEngine.kt +213 -0
- package/android/src/main/java/com/margelo/nitro/queueplayer/EqualizerPresets.kt +45 -0
- package/android/src/main/java/com/margelo/nitro/queueplayer/FFTProcessorTee.kt +173 -0
- package/android/src/main/java/com/margelo/nitro/queueplayer/FifoCacheEvictor.kt +96 -0
- package/android/src/main/java/com/margelo/nitro/queueplayer/GaplessEngine.kt +587 -0
- package/android/src/main/java/com/margelo/nitro/queueplayer/HeadlessJsMediaService.kt +220 -0
- package/android/src/main/java/com/margelo/nitro/queueplayer/IEqualizerEngine.kt +97 -0
- package/android/src/main/java/com/margelo/nitro/queueplayer/ListenerRegistry.kt +59 -0
- package/android/src/main/java/com/margelo/nitro/queueplayer/LookaheadCache.kt +343 -0
- package/android/src/main/java/com/margelo/nitro/queueplayer/LookaheadCacheWriter.kt +533 -0
- package/android/src/main/java/com/margelo/nitro/queueplayer/MediaButtonDispatch.kt +88 -0
- package/android/src/main/java/com/margelo/nitro/queueplayer/MediaItemBuilder.kt +281 -0
- package/android/src/main/java/com/margelo/nitro/queueplayer/MediaItemMapper.kt +76 -0
- package/android/src/main/java/com/margelo/nitro/queueplayer/MilestoneTracker.kt +66 -0
- package/android/src/main/java/com/margelo/nitro/queueplayer/MimeCapturingDataSource.kt +64 -0
- package/android/src/main/java/com/margelo/nitro/queueplayer/MultiplexingAudioBufferSink.kt +92 -0
- package/android/src/main/java/com/margelo/nitro/queueplayer/NotificationProvider.kt +37 -0
- package/android/src/main/java/com/margelo/nitro/queueplayer/NowPlayingFormatExtractor.kt +166 -0
- package/android/src/main/java/com/margelo/nitro/queueplayer/PendingBrowseRequests.kt +70 -0
- package/android/src/main/java/com/margelo/nitro/queueplayer/PendingIntentBuffer.kt +82 -0
- package/android/src/main/java/com/margelo/nitro/queueplayer/PitchAwareAudioProcessorChain.kt +69 -0
- package/android/src/main/java/com/margelo/nitro/queueplayer/PitchCorrection.kt +27 -0
- package/android/src/main/java/com/margelo/nitro/queueplayer/PlaceholderArtwork.kt +65 -0
- package/android/src/main/java/com/margelo/nitro/queueplayer/PlaceholderFallbackBitmapLoader.kt +75 -0
- package/android/src/main/java/com/margelo/nitro/queueplayer/PlaybackEngine.kt +336 -0
- package/android/src/main/java/com/margelo/nitro/queueplayer/PlaybackErrorMapping.kt +175 -0
- package/android/src/main/java/com/margelo/nitro/queueplayer/PlaybackModeStateMachine.kt +127 -0
- package/android/src/main/java/com/margelo/nitro/queueplayer/PlaybackService.kt +1716 -0
- package/android/src/main/java/com/margelo/nitro/queueplayer/PlaybackServiceCallback.kt +755 -0
- package/android/src/main/java/com/margelo/nitro/queueplayer/ProgressEmissionGate.kt +70 -0
- package/android/src/main/java/com/margelo/nitro/queueplayer/QueueMutationArithmetic.kt +131 -0
- package/android/src/main/java/com/margelo/nitro/queueplayer/QueuePlayerPackage.kt +22 -0
- package/android/src/main/java/com/margelo/nitro/queueplayer/QueueSkipArithmetic.kt +113 -0
- package/android/src/main/java/com/margelo/nitro/queueplayer/RadixTwoFFT.kt +176 -0
- package/android/src/main/java/com/margelo/nitro/queueplayer/RemoteCommandDeduper.kt +81 -0
- package/android/src/main/java/com/margelo/nitro/queueplayer/ReplayGainAudioProcessor.kt +84 -0
- package/android/src/main/java/com/margelo/nitro/queueplayer/ReplayGainConfig.kt +34 -0
- package/android/src/main/java/com/margelo/nitro/queueplayer/ReplayGainData.kt +44 -0
- package/android/src/main/java/com/margelo/nitro/queueplayer/ReplayGainExtractor.kt +209 -0
- package/android/src/main/java/com/margelo/nitro/queueplayer/ReplayGainGain.kt +34 -0
- package/android/src/main/java/com/margelo/nitro/queueplayer/SearchResultCache.kt +72 -0
- package/android/src/main/java/com/margelo/nitro/queueplayer/SessionCommandForwardingPlayer.kt +167 -0
- package/android/src/main/java/com/margelo/nitro/queueplayer/SignalsmithStretchAudioProcessor.kt +225 -0
- package/android/src/main/java/com/margelo/nitro/queueplayer/SignalsmithStretchNative.kt +93 -0
- package/android/src/main/java/com/margelo/nitro/queueplayer/SleepTimerCore.kt +125 -0
- package/android/src/main/java/com/margelo/nitro/queueplayer/TrackPlayer.kt +4242 -0
- package/android/src/main/java/com/margelo/nitro/queueplayer/TransportStateTranslator.kt +70 -0
- package/android/src/main/java/com/margelo/nitro/queueplayer/UrlSchemes.kt +9 -0
- package/android/src/main/java/com/margelo/nitro/queueplayer/VisualizationGate.kt +35 -0
- package/android/src/main/java/com/margelo/nitro/queueplayer/Visualizer.kt +240 -0
- package/android/src/main/java/com/margelo/nitro/queueplayer/VisualizerTeeBuilder.kt +63 -0
- package/android/src/main/java/com/margelo/nitro/queueplayer/VoiceSearchExtrasParser.kt +103 -0
- package/android/src/main/java/com/margelo/nitro/queueplayer/cast/AudioRouteState.kt +202 -0
- package/android/src/main/java/com/margelo/nitro/queueplayer/cast/AuthRequiredException.kt +14 -0
- package/android/src/main/java/com/margelo/nitro/queueplayer/cast/CastBackend.kt +41 -0
- package/android/src/main/java/com/margelo/nitro/queueplayer/cast/CastBackends.kt +56 -0
- package/android/src/main/java/com/margelo/nitro/queueplayer/cast/CastEventBridge.kt +122 -0
- package/android/src/main/java/com/margelo/nitro/queueplayer/cast/CastMediaItem.kt +20 -0
- package/android/src/main/java/com/margelo/nitro/queueplayer/cast/CastSession.kt +193 -0
- package/android/src/main/java/com/margelo/nitro/queueplayer/cast/CastSinkRouter.kt +95 -0
- package/android/src/main/java/com/margelo/nitro/queueplayer/cast/CastTransportRouter.kt +151 -0
- package/android/src/main/java/com/margelo/nitro/queueplayer/cast/DiscoveryState.kt +53 -0
- package/android/src/main/java/com/margelo/nitro/queueplayer/cast/MetadataAwareSession.kt +17 -0
- package/android/src/main/java/com/margelo/nitro/queueplayer/cast/PlaybackStateRouter.kt +65 -0
- package/android/src/main/java/com/margelo/nitro/queueplayer/cast/Receiver.kt +35 -0
- package/android/src/main/java/com/margelo/nitro/queueplayer/cast/airplay/AirPlay2JNI.kt +129 -0
- package/android/src/main/java/com/margelo/nitro/queueplayer/cast/airplay/AirPlay2MetadataSync.kt +189 -0
- package/android/src/main/java/com/margelo/nitro/queueplayer/cast/airplay/AirPlay2Session.kt +123 -0
- package/android/src/main/java/com/margelo/nitro/queueplayer/cast/airplay/AirPlay2Sink.kt +205 -0
- package/android/src/main/java/com/margelo/nitro/queueplayer/cast/airplay/AirPlayBackend.kt +585 -0
- package/android/src/main/java/com/margelo/nitro/queueplayer/cast/airplay/AirPlayBackendInitializer.kt +43 -0
- package/android/src/main/java/com/margelo/nitro/queueplayer/cast/airplay/AirPlayDiscovery.kt +511 -0
- package/android/src/main/java/com/margelo/nitro/queueplayer/cast/airplay/AirPlayDiscoveryLifecycleObserver.kt +57 -0
- package/android/src/main/java/com/margelo/nitro/queueplayer/cast/airplay/AirPlayMetadataSync.kt +199 -0
- package/android/src/main/java/com/margelo/nitro/queueplayer/cast/airplay/AirPlayReceiverMapper.kt +42 -0
- package/android/src/main/java/com/margelo/nitro/queueplayer/cast/airplay/AirPlayRenderersFactory.kt +43 -0
- package/android/src/main/java/com/margelo/nitro/queueplayer/cast/airplay/AirPlaySession.kt +101 -0
- package/android/src/main/java/com/margelo/nitro/queueplayer/cast/airplay/AirPlaySink.kt +265 -0
- package/android/src/main/java/com/margelo/nitro/queueplayer/cast/airplay/AirPlayTxtParser.kt +203 -0
- package/android/src/main/java/com/margelo/nitro/queueplayer/cast/airplay/AirplayJNI.kt +106 -0
- package/android/src/main/java/com/margelo/nitro/queueplayer/cast/airplay/MulticastLockHolder.kt +75 -0
- package/android/src/main/java/com/margelo/nitro/queueplayer/cast/chromecast/CastMediaItemConverter.kt +67 -0
- package/android/src/main/java/com/margelo/nitro/queueplayer/cast/chromecast/CastVolumeForwardingPlayer.kt +156 -0
- package/android/src/main/java/com/margelo/nitro/queueplayer/cast/chromecast/ChromecastBackend.kt +171 -0
- package/android/src/main/java/com/margelo/nitro/queueplayer/cast/chromecast/ChromecastOptionsProvider.kt +51 -0
- package/android/src/main/java/com/margelo/nitro/queueplayer/cast/chromecast/ChromecastReceiverMapper.kt +44 -0
- package/android/src/main/java/com/margelo/nitro/queueplayer/cast/chromecast/ChromecastSession.kt +544 -0
- package/android/src/main/java/com/margelo/nitro/queueplayer/cast/chromecast/MediaInfoBuilder.kt +92 -0
- package/android/src/main/java/com/margelo/nitro/queueplayer/cast/chromecast/RemoteMediaCallbacks.kt +113 -0
- package/android/src/main/java/com/margelo/nitro/queueplayer/cast/http/LocalMediaServer.kt +160 -0
- package/android/src/main/java/com/margelo/nitro/queueplayer/cast/http/LocalMediaServerLifecycle.kt +15 -0
- package/android/src/main/java/com/margelo/nitro/queueplayer/cast/http/MediaServerHandle.kt +44 -0
- package/android/src/main/java/com/margelo/nitro/queueplayer/cast/http/MediaTokenRegistry.kt +37 -0
- package/android/src/main/java/com/margelo/nitro/queueplayer/cast/http/MimeTypes.kt +27 -0
- package/android/src/main/java/com/margelo/nitro/queueplayer/cast/net/LocalAddressMonitor.kt +86 -0
- package/android/src/main/res/raw/rnqp_placeholder.jpg +0 -0
- package/android/src/main/res/values/cast_strings.xml +15 -0
- package/android/src/main/res/values/strings.xml +24 -0
- package/android/src/main/res/xml/automotive_app_desc.xml +15 -0
- package/android/src/test/java/androidx/media3/session/MediaSessionControllerRequestTestSeam.kt +27 -0
- package/android/src/test/java/com/margelo/nitro/queueplayer/ArtworkContentProviderTest.kt +124 -0
- package/android/src/test/java/com/margelo/nitro/queueplayer/AudioFocusErrorMappingTest.kt +122 -0
- package/android/src/test/java/com/margelo/nitro/queueplayer/AudioPipelineRenderersFactoryTest.kt +57 -0
- package/android/src/test/java/com/margelo/nitro/queueplayer/AvrcpMetadataTest.kt +264 -0
- package/android/src/test/java/com/margelo/nitro/queueplayer/BrowseCallbackRegistryTest.kt +104 -0
- package/android/src/test/java/com/margelo/nitro/queueplayer/BrowseDataProviderTest.kt +276 -0
- package/android/src/test/java/com/margelo/nitro/queueplayer/BufferStateTranslatorTest.kt +58 -0
- package/android/src/test/java/com/margelo/nitro/queueplayer/CacheMimeTypesTest.kt +116 -0
- package/android/src/test/java/com/margelo/nitro/queueplayer/CrossfadeEngineLifecycleTest.kt +214 -0
- package/android/src/test/java/com/margelo/nitro/queueplayer/CrossfadeEngineReplayGainTest.kt +180 -0
- package/android/src/test/java/com/margelo/nitro/queueplayer/CustomPresetStoreTest.kt +128 -0
- package/android/src/test/java/com/margelo/nitro/queueplayer/EqualizerEngineTest.kt +223 -0
- package/android/src/test/java/com/margelo/nitro/queueplayer/EqualizerKtTest.kt +274 -0
- package/android/src/test/java/com/margelo/nitro/queueplayer/EqualizerLegacyEngineTest.kt +214 -0
- package/android/src/test/java/com/margelo/nitro/queueplayer/EqualizerPresetsTest.kt +89 -0
- package/android/src/test/java/com/margelo/nitro/queueplayer/FFTProcessorTeeTest.kt +185 -0
- package/android/src/test/java/com/margelo/nitro/queueplayer/FifoCacheEvictorTest.kt +112 -0
- package/android/src/test/java/com/margelo/nitro/queueplayer/ForwardingPlayerTestAccess.kt +21 -0
- package/android/src/test/java/com/margelo/nitro/queueplayer/GaplessEngineLifecycleTest.kt +65 -0
- package/android/src/test/java/com/margelo/nitro/queueplayer/GaplessEngineReplayGainTest.kt +154 -0
- package/android/src/test/java/com/margelo/nitro/queueplayer/HeadlessJsMediaServiceTest.kt +106 -0
- package/android/src/test/java/com/margelo/nitro/queueplayer/LookaheadCacheTest.kt +183 -0
- package/android/src/test/java/com/margelo/nitro/queueplayer/LookaheadCacheWriterTest.kt +649 -0
- package/android/src/test/java/com/margelo/nitro/queueplayer/MediaButtonDispatchTest.kt +192 -0
- package/android/src/test/java/com/margelo/nitro/queueplayer/MediaItemBuilderTest.kt +389 -0
- package/android/src/test/java/com/margelo/nitro/queueplayer/MediaItemMapperTest.kt +145 -0
- package/android/src/test/java/com/margelo/nitro/queueplayer/MilestoneTrackerTest.kt +81 -0
- package/android/src/test/java/com/margelo/nitro/queueplayer/MultiplexingAudioBufferSinkTest.kt +112 -0
- package/android/src/test/java/com/margelo/nitro/queueplayer/NotificationProviderTest.kt +86 -0
- package/android/src/test/java/com/margelo/nitro/queueplayer/NowPlayingFormatExtractorTest.kt +106 -0
- package/android/src/test/java/com/margelo/nitro/queueplayer/PendingBrowseRequestsTest.kt +82 -0
- package/android/src/test/java/com/margelo/nitro/queueplayer/PendingIntentBufferTest.kt +192 -0
- package/android/src/test/java/com/margelo/nitro/queueplayer/PitchAwareAudioProcessorChainTest.kt +147 -0
- package/android/src/test/java/com/margelo/nitro/queueplayer/PitchCorrectionTest.kt +55 -0
- package/android/src/test/java/com/margelo/nitro/queueplayer/PlaceholderFallbackBitmapLoaderTest.kt +187 -0
- package/android/src/test/java/com/margelo/nitro/queueplayer/PlaybackEngineInterfaceTest.kt +134 -0
- package/android/src/test/java/com/margelo/nitro/queueplayer/PlaybackModeStateMachineTest.kt +157 -0
- package/android/src/test/java/com/margelo/nitro/queueplayer/PlaybackServiceArtworkLoaderTest.kt +95 -0
- package/android/src/test/java/com/margelo/nitro/queueplayer/PlaybackServiceCallbackBrowseTest.kt +480 -0
- package/android/src/test/java/com/margelo/nitro/queueplayer/PlaybackServiceCallbackSearchTest.kt +171 -0
- package/android/src/test/java/com/margelo/nitro/queueplayer/PlaybackServiceCallbackTest.kt +773 -0
- package/android/src/test/java/com/margelo/nitro/queueplayer/PlaybackServiceCastBindTest.kt +100 -0
- package/android/src/test/java/com/margelo/nitro/queueplayer/PlaybackServiceLifecycleTest.kt +315 -0
- package/android/src/test/java/com/margelo/nitro/queueplayer/PlaybackServiceSessionActivityTest.kt +121 -0
- package/android/src/test/java/com/margelo/nitro/queueplayer/PlayerCommandsForTest.kt +246 -0
- package/android/src/test/java/com/margelo/nitro/queueplayer/ProgressEmissionGateTest.kt +113 -0
- package/android/src/test/java/com/margelo/nitro/queueplayer/QueueMutationArithmeticTest.kt +252 -0
- package/android/src/test/java/com/margelo/nitro/queueplayer/QueueSkipArithmeticTest.kt +343 -0
- package/android/src/test/java/com/margelo/nitro/queueplayer/RadixTwoFFTTest.kt +109 -0
- package/android/src/test/java/com/margelo/nitro/queueplayer/RemoteCommandDeduperTest.kt +106 -0
- package/android/src/test/java/com/margelo/nitro/queueplayer/ReplayGainAudioProcessorTest.kt +141 -0
- package/android/src/test/java/com/margelo/nitro/queueplayer/ReplayGainConfigTest.kt +103 -0
- package/android/src/test/java/com/margelo/nitro/queueplayer/ReplayGainExtractorTest.kt +469 -0
- package/android/src/test/java/com/margelo/nitro/queueplayer/ReplayGainGainTest.kt +129 -0
- package/android/src/test/java/com/margelo/nitro/queueplayer/RobolectricServiceBindHelper.kt +83 -0
- package/android/src/test/java/com/margelo/nitro/queueplayer/SearchResultCacheTest.kt +128 -0
- package/android/src/test/java/com/margelo/nitro/queueplayer/ServiceReadyReasonTest.kt +42 -0
- package/android/src/test/java/com/margelo/nitro/queueplayer/SessionCommandForwardingPlayerTest.kt +521 -0
- package/android/src/test/java/com/margelo/nitro/queueplayer/SleepTimerCoreTest.kt +125 -0
- package/android/src/test/java/com/margelo/nitro/queueplayer/SmokeTest.kt +27 -0
- package/android/src/test/java/com/margelo/nitro/queueplayer/TrackPlayerAirPlayMetadataWiringTest.kt +305 -0
- package/android/src/test/java/com/margelo/nitro/queueplayer/TrackPlayerAudioFocusTest.kt +179 -0
- package/android/src/test/java/com/margelo/nitro/queueplayer/TrackPlayerEventsTest.kt +497 -0
- package/android/src/test/java/com/margelo/nitro/queueplayer/TrackPlayerLifecycleTest.kt +209 -0
- package/android/src/test/java/com/margelo/nitro/queueplayer/TrackPlayerLookaheadConfigTest.kt +417 -0
- package/android/src/test/java/com/margelo/nitro/queueplayer/TrackPlayerMutationTest.kt +473 -0
- package/android/src/test/java/com/margelo/nitro/queueplayer/TrackPlayerPitchCorrectionTest.kt +61 -0
- package/android/src/test/java/com/margelo/nitro/queueplayer/TrackPlayerPlaybackModeTest.kt +28 -0
- package/android/src/test/java/com/margelo/nitro/queueplayer/TrackPlayerProgressThrottleTest.kt +89 -0
- package/android/src/test/java/com/margelo/nitro/queueplayer/TrackPlayerQueueTest.kt +290 -0
- package/android/src/test/java/com/margelo/nitro/queueplayer/TrackPlayerReadersTest.kt +151 -0
- package/android/src/test/java/com/margelo/nitro/queueplayer/TrackPlayerReplayGainTest.kt +69 -0
- package/android/src/test/java/com/margelo/nitro/queueplayer/TrackPlayerSkipCapabilityTest.kt +324 -0
- package/android/src/test/java/com/margelo/nitro/queueplayer/TrackPlayerSkipTest.kt +287 -0
- package/android/src/test/java/com/margelo/nitro/queueplayer/TrackPlayerSourceClassifierTest.kt +82 -0
- package/android/src/test/java/com/margelo/nitro/queueplayer/TrackPlayerTransportTest.kt +222 -0
- package/android/src/test/java/com/margelo/nitro/queueplayer/TransportStateTranslatorTest.kt +287 -0
- package/android/src/test/java/com/margelo/nitro/queueplayer/VisualizationGateTest.kt +96 -0
- package/android/src/test/java/com/margelo/nitro/queueplayer/VisualizerKtTest.kt +202 -0
- package/android/src/test/java/com/margelo/nitro/queueplayer/VoiceSearchExtrasParserTest.kt +211 -0
- package/android/src/test/java/com/margelo/nitro/queueplayer/cast/CastSinkRouterTest.kt +120 -0
- package/android/src/test/java/com/margelo/nitro/queueplayer/cast/FakeRemotePlayer.kt +62 -0
- package/android/src/test/java/com/margelo/nitro/queueplayer/cast/PlaybackStateRouterTest.kt +43 -0
- package/android/src/test/java/com/margelo/nitro/queueplayer/cast/airplay/AirPlayDiscoveryFilterTest.kt +86 -0
- package/android/src/test/java/com/margelo/nitro/queueplayer/cast/airplay/AirPlayTxtParserClassificationTest.kt +34 -0
- package/android/src/test/java/com/margelo/nitro/queueplayer/cast/chromecast/CastMediaItemConverterTest.kt +78 -0
- package/android/src/test/java/com/margelo/nitro/queueplayer/cast/chromecast/CastVolumeForwardingPlayerTest.kt +176 -0
- package/app.plugin.js +466 -0
- package/ios/AVPlayerItemQueueItemId.swift +61 -0
- package/ios/AVQueueBuilder.swift +200 -0
- package/ios/Array+SafeSubscript.swift +6 -0
- package/ios/ArtworkLoader.swift +136 -0
- package/ios/ArtworkResolver.swift +87 -0
- package/ios/AudioCategoryMapping.swift +48 -0
- package/ios/AudioSession.swift +220 -0
- package/ios/AudioSessionError.swift +44 -0
- package/ios/AudioTapProvider.swift +647 -0
- package/ios/BiquadCoefficients.swift +80 -0
- package/ios/CarPlayBridge.swift +178 -0
- package/ios/CarPlayBrowseBuilder.swift +368 -0
- package/ios/CarPlayBrowseDataProvider.swift +162 -0
- package/ios/CarPlayCoordinator.swift +42 -0
- package/ios/CarPlaySceneDelegate.swift +199 -0
- package/ios/Cast/AirPlayRouteState.swift +281 -0
- package/ios/Cast/Chromecast/ChromecastDiscoveryAdapter.swift +27 -0
- package/ios/Cast/Chromecast/ChromecastReceiverMapper.swift +48 -0
- package/ios/Cast/Chromecast/ChromecastSession.swift +740 -0
- package/ios/Cast/Chromecast/ChromecastSessionAdapter.swift +114 -0
- package/ios/Cast/Chromecast/ChromecastSupport.swift +63 -0
- package/ios/Cast/Chromecast/MediaInfoBuilder.swift +104 -0
- package/ios/Cast/Chromecast/RemoteMediaObserver.swift +147 -0
- package/ios/Cast/Core/CastEventBridge.swift +210 -0
- package/ios/Cast/Core/CastMediaItem.swift +41 -0
- package/ios/Cast/Core/CastNowPlayingController.swift +310 -0
- package/ios/Cast/Core/CastReceiverState.swift +22 -0
- package/ios/Cast/Core/CastSession.swift +147 -0
- package/ios/Cast/Core/CastTransportRouter.swift +162 -0
- package/ios/Cast/Core/ChromecastBackend.swift +259 -0
- package/ios/Cast/Core/LocalAddressMonitor.swift +110 -0
- package/ios/Cast/Core/LocalMediaServer.swift +270 -0
- package/ios/Cast/Core/LocalNetworkPermissionProbe.swift +265 -0
- package/ios/Cast/Core/MediaHTTPConnection.swift +349 -0
- package/ios/Cast/Core/MediaServerHandle.swift +49 -0
- package/ios/Cast/Core/MediaTokenRegistry.swift +51 -0
- package/ios/Cast/Core/MimeTypes.swift +26 -0
- package/ios/Cast/Core/PlaybackStateRouter.swift +123 -0
- package/ios/CastManager.swift +493 -0
- package/ios/CrossfadeEngine.swift +1343 -0
- package/ios/CustomPresetStore.swift +78 -0
- package/ios/EQProcessor.swift +321 -0
- package/ios/EQTap.swift +241 -0
- package/ios/Equalizer.swift +321 -0
- package/ios/EqualizerPresets.swift +48 -0
- package/ios/FFTProcessor.swift +237 -0
- package/ios/GaplessEngine.swift +385 -0
- package/ios/InputGuards.swift +63 -0
- package/ios/InterruptionEventMapping.swift +51 -0
- package/ios/ListenerRegistry.swift +73 -0
- package/ios/LookaheadCache.swift +833 -0
- package/ios/LookaheadCacheEviction.swift +83 -0
- package/ios/LookaheadCachePrefetcher.swift +503 -0
- package/ios/MetadataReader.swift +555 -0
- package/ios/MilestoneTracker.swift +63 -0
- package/ios/NowPlayingFormatExtractor.swift +362 -0
- package/ios/NowPlayingInfo.swift +190 -0
- package/ios/OutputRouteMonitor.swift +125 -0
- package/ios/PendingBrowseRequests.swift +62 -0
- package/ios/PitchAlgorithm.swift +24 -0
- package/ios/PlaceholderArtwork.swift +46 -0
- package/ios/PlaybackEngine.swift +319 -0
- package/ios/PlaybackErrorMapping.swift +195 -0
- package/ios/PlaybackModeStateMachine.swift +159 -0
- package/ios/PlayerStateDerivation.swift +45 -0
- package/ios/ProgressEmissionGate.swift +54 -0
- package/ios/QueueMutationArithmetic.swift +129 -0
- package/ios/QueueSkipArithmetic.swift +109 -0
- package/ios/QueueState.swift +49 -0
- package/ios/RemoteCommands.swift +229 -0
- package/ios/ReplayGainData.swift +58 -0
- package/ios/ReplayGainExtractor.swift +82 -0
- package/ios/ReplayGainProcessor.swift +46 -0
- package/ios/Resources/RNQP-placeholder.jpg +0 -0
- package/ios/Resources/silence.m4a +0 -0
- package/ios/Siri/PendingIntentBuffer.swift +100 -0
- package/ios/Siri/PlayMediaIntentHandler.swift +283 -0
- package/ios/Siri/QueuePlayerMediaIntents.swift +45 -0
- package/ios/Siri/VoiceDonation.swift +50 -0
- package/ios/SleepTimerCore.swift +128 -0
- package/ios/StreamingBitrateProbe.swift +318 -0
- package/ios/Tests/AVQueueBuilderTests.swift +430 -0
- package/ios/Tests/ArtworkResolverTests.swift +540 -0
- package/ios/Tests/AudioSessionTests.swift +222 -0
- package/ios/Tests/AudioTapProviderReplayGainTests.swift +136 -0
- package/ios/Tests/AudioTapProviderVisualizationFlagTests.swift +104 -0
- package/ios/Tests/BiquadCoefficientsTests.swift +84 -0
- package/ios/Tests/CarPlayBridgeTests.swift +91 -0
- package/ios/Tests/CarPlayBrowseBuilderTests.swift +221 -0
- package/ios/Tests/CarPlayBrowseDataProviderTests.swift +218 -0
- package/ios/Tests/CastNowPlayingControllerTests.swift +204 -0
- package/ios/Tests/CrossfadeEngineStubTests.swift +43 -0
- package/ios/Tests/EQProcessorTests.swift +302 -0
- package/ios/Tests/EQTapTests.swift +49 -0
- package/ios/Tests/EqualizerAudioMixProviderTests.swift +110 -0
- package/ios/Tests/EqualizerHybridTests.swift +303 -0
- package/ios/Tests/EqualizerPresetsTests.swift +69 -0
- package/ios/Tests/FFTProcessorTests.swift +303 -0
- package/ios/Tests/Fixtures/silence-aac-128.m4a +0 -0
- package/ios/Tests/Fixtures/silence-cbr-128.mp3 +0 -0
- package/ios/Tests/Fixtures/silence-vbr.mp3 +0 -0
- package/ios/Tests/GaplessEngineLifecycleTests.swift +160 -0
- package/ios/Tests/InputGuardsTests.swift +75 -0
- package/ios/Tests/InterruptionEventMappingTests.swift +91 -0
- package/ios/Tests/LookaheadCacheEvictionTests.swift +217 -0
- package/ios/Tests/LookaheadCachePrefetcherTests.swift +630 -0
- package/ios/Tests/LookaheadCacheRuntimeConfigTests.swift +247 -0
- package/ios/Tests/LookaheadCacheTests.swift +717 -0
- package/ios/Tests/MediaHTTPConnectionTests.swift +163 -0
- package/ios/Tests/MetadataReaderTests.swift +605 -0
- package/ios/Tests/MilestoneTrackerTests.swift +76 -0
- package/ios/Tests/NowPlayingFormatExtractorTests.swift +158 -0
- package/ios/Tests/NowPlayingInfoTests.swift +292 -0
- package/ios/Tests/PendingBrowseRequestsTests.swift +69 -0
- package/ios/Tests/PendingIntentBufferTests.swift +164 -0
- package/ios/Tests/PlaybackEngineProtocolTests.swift +92 -0
- package/ios/Tests/PlaybackErrorMappingTests.swift +179 -0
- package/ios/Tests/PlaybackModeStateMachineTests.swift +40 -0
- package/ios/Tests/PlaybackStateRouterTests.swift +178 -0
- package/ios/Tests/PlayerStateDerivationTests.swift +69 -0
- package/ios/Tests/ProgressEmissionGateTests.swift +113 -0
- package/ios/Tests/QueueMutationArithmeticTests.swift +245 -0
- package/ios/Tests/RemoteCommandsTests.swift +147 -0
- package/ios/Tests/ReplayGainExtractorTests.swift +197 -0
- package/ios/Tests/ReplayGainProcessorTests.swift +90 -0
- package/ios/Tests/SkipCapabilityTests.swift +98 -0
- package/ios/Tests/SkipIndexTests.swift +259 -0
- package/ios/Tests/SleepTimerCoreTests.swift +98 -0
- package/ios/Tests/StreamingBitrateProbeTests.swift +29 -0
- package/ios/Tests/TrackSourceClassifierTests.swift +50 -0
- package/ios/Tests/VoiceDonationTests.swift +26 -0
- package/ios/TrackPlayer.swift +4766 -0
- package/ios/Visualizer.swift +274 -0
- package/ios/WindowApplicationSceneDelegate.swift +59 -0
- package/ios/tests-harness/Podfile +56 -0
- package/ios/tests-harness/TestHost/App.swift +12 -0
- package/ios/tests-harness/TestHost/Info.plist +37 -0
- package/ios/tests-harness/TestHost.xcodeproj/project.pbxproj +472 -0
- package/ios/tests-harness/TestHost.xcodeproj/xcshareddata/xcschemes/TestHost.xcscheme +68 -0
- package/ios/tests-harness/package.json +10 -0
- package/ios/tests-harness/scripts/seed-xcodeproj.rb +118 -0
- package/lib/module/CastManager.nitro.js +4 -0
- package/lib/module/CastManager.nitro.js.map +1 -0
- package/lib/module/Equalizer.nitro.js +4 -0
- package/lib/module/Equalizer.nitro.js.map +1 -0
- package/lib/module/TrackPlayer.nitro.js +4 -0
- package/lib/module/TrackPlayer.nitro.js.map +1 -0
- package/lib/module/Visualizer.nitro.js +4 -0
- package/lib/module/Visualizer.nitro.js.map +1 -0
- package/lib/module/cast/index.js +156 -0
- package/lib/module/cast/index.js.map +1 -0
- package/lib/module/clients.js +43 -0
- package/lib/module/clients.js.map +1 -0
- package/lib/module/equalizer.js +85 -0
- package/lib/module/equalizer.js.map +1 -0
- package/lib/module/hooks/index.js +16 -0
- package/lib/module/hooks/index.js.map +1 -0
- package/lib/module/hooks/useActiveTrack.js +50 -0
- package/lib/module/hooks/useActiveTrack.js.map +1 -0
- package/lib/module/hooks/useAudioRoute.js +60 -0
- package/lib/module/hooks/useAudioRoute.js.map +1 -0
- package/lib/module/hooks/useBufferState.js +34 -0
- package/lib/module/hooks/useBufferState.js.map +1 -0
- package/lib/module/hooks/useCanSkip.js +67 -0
- package/lib/module/hooks/useCanSkip.js.map +1 -0
- package/lib/module/hooks/useCast.js +107 -0
- package/lib/module/hooks/useCast.js.map +1 -0
- package/lib/module/hooks/useEqualizer.js +91 -0
- package/lib/module/hooks/useEqualizer.js.map +1 -0
- package/lib/module/hooks/useFullyBuffered.js +33 -0
- package/lib/module/hooks/useFullyBuffered.js.map +1 -0
- package/lib/module/hooks/useLookaheadCache.js +49 -0
- package/lib/module/hooks/useLookaheadCache.js.map +1 -0
- package/lib/module/hooks/useNowPlayingFormat.js +42 -0
- package/lib/module/hooks/useNowPlayingFormat.js.map +1 -0
- package/lib/module/hooks/usePlaybackState.js +36 -0
- package/lib/module/hooks/usePlaybackState.js.map +1 -0
- package/lib/module/hooks/useProgress.js +31 -0
- package/lib/module/hooks/useProgress.js.map +1 -0
- package/lib/module/hooks/useSleepTimer.js +65 -0
- package/lib/module/hooks/useSleepTimer.js.map +1 -0
- package/lib/module/hooks/useVisualizerState.js +99 -0
- package/lib/module/hooks/useVisualizerState.js.map +1 -0
- package/lib/module/index.js +37 -0
- package/lib/module/index.js.map +1 -0
- package/lib/module/package.json +1 -0
- package/lib/module/playbackService.js +174 -0
- package/lib/module/playbackService.js.map +1 -0
- package/lib/module/types.js +592 -0
- package/lib/module/types.js.map +1 -0
- package/lib/typescript/CastManager.nitro.d.ts +154 -0
- package/lib/typescript/CastManager.nitro.d.ts.map +1 -0
- package/lib/typescript/Equalizer.nitro.d.ts +30 -0
- package/lib/typescript/Equalizer.nitro.d.ts.map +1 -0
- package/lib/typescript/TrackPlayer.nitro.d.ts +584 -0
- package/lib/typescript/TrackPlayer.nitro.d.ts.map +1 -0
- package/lib/typescript/Visualizer.nitro.d.ts +36 -0
- package/lib/typescript/Visualizer.nitro.d.ts.map +1 -0
- package/lib/typescript/cast/index.d.ts +65 -0
- package/lib/typescript/cast/index.d.ts.map +1 -0
- package/lib/typescript/clients.d.ts +9 -0
- package/lib/typescript/clients.d.ts.map +1 -0
- package/lib/typescript/equalizer.d.ts +45 -0
- package/lib/typescript/equalizer.d.ts.map +1 -0
- package/lib/typescript/hooks/index.d.ts +22 -0
- package/lib/typescript/hooks/index.d.ts.map +1 -0
- package/lib/typescript/hooks/useActiveTrack.d.ts +21 -0
- package/lib/typescript/hooks/useActiveTrack.d.ts.map +1 -0
- package/lib/typescript/hooks/useAudioRoute.d.ts +13 -0
- package/lib/typescript/hooks/useAudioRoute.d.ts.map +1 -0
- package/lib/typescript/hooks/useBufferState.d.ts +18 -0
- package/lib/typescript/hooks/useBufferState.d.ts.map +1 -0
- package/lib/typescript/hooks/useCanSkip.d.ts +43 -0
- package/lib/typescript/hooks/useCanSkip.d.ts.map +1 -0
- package/lib/typescript/hooks/useCast.d.ts +32 -0
- package/lib/typescript/hooks/useCast.d.ts.map +1 -0
- package/lib/typescript/hooks/useEqualizer.d.ts +20 -0
- package/lib/typescript/hooks/useEqualizer.d.ts.map +1 -0
- package/lib/typescript/hooks/useFullyBuffered.d.ts +15 -0
- package/lib/typescript/hooks/useFullyBuffered.d.ts.map +1 -0
- package/lib/typescript/hooks/useLookaheadCache.d.ts +13 -0
- package/lib/typescript/hooks/useLookaheadCache.d.ts.map +1 -0
- package/lib/typescript/hooks/useNowPlayingFormat.d.ts +18 -0
- package/lib/typescript/hooks/useNowPlayingFormat.d.ts.map +1 -0
- package/lib/typescript/hooks/usePlaybackState.d.ts +9 -0
- package/lib/typescript/hooks/usePlaybackState.d.ts.map +1 -0
- package/lib/typescript/hooks/useProgress.d.ts +15 -0
- package/lib/typescript/hooks/useProgress.d.ts.map +1 -0
- package/lib/typescript/hooks/useSleepTimer.d.ts +26 -0
- package/lib/typescript/hooks/useSleepTimer.d.ts.map +1 -0
- package/lib/typescript/hooks/useVisualizerState.d.ts +40 -0
- package/lib/typescript/hooks/useVisualizerState.d.ts.map +1 -0
- package/lib/typescript/index.d.ts +14 -0
- package/lib/typescript/index.d.ts.map +1 -0
- package/lib/typescript/package.json +1 -0
- package/lib/typescript/playbackService.d.ts +140 -0
- package/lib/typescript/playbackService.d.ts.map +1 -0
- package/lib/typescript/types.d.ts +1215 -0
- package/lib/typescript/types.d.ts.map +1 -0
- package/nitro.json +53 -0
- package/nitrogen/generated/android/c++/JAudioCategoryOptions.hpp +65 -0
- package/nitrogen/generated/android/c++/JAudioCodec.hpp +73 -0
- package/nitrogen/generated/android/c++/JAudioContainer.hpp +76 -0
- package/nitrogen/generated/android/c++/JAudioContentType.hpp +58 -0
- package/nitrogen/generated/android/c++/JAudioRoute.hpp +75 -0
- package/nitrogen/generated/android/c++/JAudioRouteKind.hpp +91 -0
- package/nitrogen/generated/android/c++/JBrowseItem.hpp +78 -0
- package/nitrogen/generated/android/c++/JBrowseSection.hpp +97 -0
- package/nitrogen/generated/android/c++/JBrowseSnapshot.hpp +87 -0
- package/nitrogen/generated/android/c++/JBufferState.hpp +64 -0
- package/nitrogen/generated/android/c++/JCacheStatus.hpp +92 -0
- package/nitrogen/generated/android/c++/JCastConfigureOptions.hpp +62 -0
- package/nitrogen/generated/android/c++/JCastConnectOptions.hpp +57 -0
- package/nitrogen/generated/android/c++/JCastDeviceKind.hpp +70 -0
- package/nitrogen/generated/android/c++/JCastDiscoveryState.hpp +57 -0
- package/nitrogen/generated/android/c++/JCastLocalNetworkPermission.hpp +61 -0
- package/nitrogen/generated/android/c++/JCastLocalNetworkPermissionEvent.hpp +58 -0
- package/nitrogen/generated/android/c++/JCastProtocol.hpp +67 -0
- package/nitrogen/generated/android/c++/JCastReceiver.hpp +89 -0
- package/nitrogen/generated/android/c++/JCastReceiverCapabilities.hpp +66 -0
- package/nitrogen/generated/android/c++/JCastRoute.hpp +85 -0
- package/nitrogen/generated/android/c++/JCastSessionDiedEvent.hpp +63 -0
- package/nitrogen/generated/android/c++/JCastSessionEndReason.hpp +76 -0
- package/nitrogen/generated/android/c++/JCastStreamingModel.hpp +58 -0
- package/nitrogen/generated/android/c++/JEqualizerBand.hpp +61 -0
- package/nitrogen/generated/android/c++/JEqualizerPreset.hpp +76 -0
- package/nitrogen/generated/android/c++/JEvictionPolicy.hpp +58 -0
- package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_std__shared_ptr_Promise_std__vector_BrowseItem______std__string.hpp +146 -0
- package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_std__shared_ptr_Promise_std__vector_TrackItem______MediaSearchRequest.hpp +161 -0
- package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_std__shared_ptr_Promise_std__vector_TrackItem______std__string.hpp +147 -0
- package/nitrogen/generated/android/c++/JFunc_void.hpp +75 -0
- package/nitrogen/generated/android/c++/JFunc_void_AudioRoute.hpp +80 -0
- package/nitrogen/generated/android/c++/JFunc_void_BufferState.hpp +77 -0
- package/nitrogen/generated/android/c++/JFunc_void_CacheStatus.hpp +79 -0
- package/nitrogen/generated/android/c++/JFunc_void_CastDiscoveryState.hpp +77 -0
- package/nitrogen/generated/android/c++/JFunc_void_CastLocalNetworkPermissionEvent.hpp +79 -0
- package/nitrogen/generated/android/c++/JFunc_void_CastRoute.hpp +86 -0
- package/nitrogen/generated/android/c++/JFunc_void_CastSessionDiedEvent.hpp +80 -0
- package/nitrogen/generated/android/c++/JFunc_void_PlaybackError.hpp +80 -0
- package/nitrogen/generated/android/c++/JFunc_void_PlayerProgress.hpp +77 -0
- package/nitrogen/generated/android/c++/JFunc_void_PlayerState_StateChangeReason.hpp +79 -0
- package/nitrogen/generated/android/c++/JFunc_void_ServiceReadyReason.hpp +77 -0
- package/nitrogen/generated/android/c++/JFunc_void_SkipCapability.hpp +77 -0
- package/nitrogen/generated/android/c++/JFunc_void_SleepTimerState.hpp +78 -0
- package/nitrogen/generated/android/c++/JFunc_void_VisualizerErrorReason.hpp +77 -0
- package/nitrogen/generated/android/c++/JFunc_void_VisualizerFrame.hpp +80 -0
- package/nitrogen/generated/android/c++/JFunc_void_bool.hpp +75 -0
- package/nitrogen/generated/android/c++/JFunc_void_double_double.hpp +75 -0
- package/nitrogen/generated/android/c++/JFunc_void_std__optional_TrackItem__double_TrackChangeReason_std__optional_double_.hpp +82 -0
- package/nitrogen/generated/android/c++/JFunc_void_std__optional_std__variant_nitro__NullType__NowPlayingFormat__.hpp +93 -0
- package/nitrogen/generated/android/c++/JFunc_void_std__vector_CastReceiver_.hpp +105 -0
- package/nitrogen/generated/android/c++/JFunc_void_std__vector_EqualizerBand_.hpp +96 -0
- package/nitrogen/generated/android/c++/JHybridCastManagerSpec.cpp +388 -0
- package/nitrogen/generated/android/c++/JHybridCastManagerSpec.hpp +84 -0
- package/nitrogen/generated/android/c++/JHybridEqualizerSpec.cpp +233 -0
- package/nitrogen/generated/android/c++/JHybridEqualizerSpec.hpp +74 -0
- package/nitrogen/generated/android/c++/JHybridTrackPlayerSpec.cpp +1052 -0
- package/nitrogen/generated/android/c++/JHybridTrackPlayerSpec.hpp +130 -0
- package/nitrogen/generated/android/c++/JHybridVisualizerSpec.cpp +84 -0
- package/nitrogen/generated/android/c++/JHybridVisualizerSpec.hpp +64 -0
- package/nitrogen/generated/android/c++/JLookaheadCacheConfig.hpp +61 -0
- package/nitrogen/generated/android/c++/JMediaSearchOrigin.hpp +58 -0
- package/nitrogen/generated/android/c++/JMediaSearchQueueLocation.hpp +61 -0
- package/nitrogen/generated/android/c++/JMediaSearchReference.hpp +58 -0
- package/nitrogen/generated/android/c++/JMediaSearchReleaseDateRange.hpp +61 -0
- package/nitrogen/generated/android/c++/JMediaSearchRepeatMode.hpp +61 -0
- package/nitrogen/generated/android/c++/JMediaSearchRequest.hpp +126 -0
- package/nitrogen/generated/android/c++/JMediaSearchType.hpp +67 -0
- package/nitrogen/generated/android/c++/JNowPlayingFormat.hpp +123 -0
- package/nitrogen/generated/android/c++/JNowPlayingFormatAndroidExtras.hpp +71 -0
- package/nitrogen/generated/android/c++/JNowPlayingFormatIosExtras.hpp +61 -0
- package/nitrogen/generated/android/c++/JPitchCorrectionMode.hpp +61 -0
- package/nitrogen/generated/android/c++/JPlaybackError.hpp +87 -0
- package/nitrogen/generated/android/c++/JPlaybackErrorCode.hpp +88 -0
- package/nitrogen/generated/android/c++/JPlaybackMode.hpp +63 -0
- package/nitrogen/generated/android/c++/JPlaybackModeKind.hpp +58 -0
- package/nitrogen/generated/android/c++/JPlayerConfig.hpp +126 -0
- package/nitrogen/generated/android/c++/JPlayerProgress.hpp +65 -0
- package/nitrogen/generated/android/c++/JPlayerState.hpp +73 -0
- package/nitrogen/generated/android/c++/JRemoteControlOptions.hpp +66 -0
- package/nitrogen/generated/android/c++/JRemoteSkipMode.hpp +58 -0
- package/nitrogen/generated/android/c++/JRepeatMode.hpp +61 -0
- package/nitrogen/generated/android/c++/JReplayGainMode.hpp +61 -0
- package/nitrogen/generated/android/c++/JSectionIcon.hpp +91 -0
- package/nitrogen/generated/android/c++/JServiceReadyReason.hpp +58 -0
- package/nitrogen/generated/android/c++/JShuffleResult.hpp +88 -0
- package/nitrogen/generated/android/c++/JSkipCapability.hpp +61 -0
- package/nitrogen/generated/android/c++/JSleepTimerState.hpp +69 -0
- package/nitrogen/generated/android/c++/JStateChangeReason.hpp +73 -0
- package/nitrogen/generated/android/c++/JTrackChangeReason.hpp +73 -0
- package/nitrogen/generated/android/c++/JTrackItem.hpp +100 -0
- package/nitrogen/generated/android/c++/JTrackSource.hpp +61 -0
- package/nitrogen/generated/android/c++/JVariant_NullType_Double.cpp +26 -0
- package/nitrogen/generated/android/c++/JVariant_NullType_Double.hpp +69 -0
- package/nitrogen/generated/android/c++/JVariant_NullType_NowPlayingFormat.cpp +26 -0
- package/nitrogen/generated/android/c++/JVariant_NullType_NowPlayingFormat.hpp +83 -0
- package/nitrogen/generated/android/c++/JVariant_NullType_String.cpp +26 -0
- package/nitrogen/generated/android/c++/JVariant_NullType_String.hpp +70 -0
- package/nitrogen/generated/android/c++/JVisualizerConfig.hpp +65 -0
- package/nitrogen/generated/android/c++/JVisualizerErrorReason.hpp +64 -0
- package/nitrogen/generated/android/c++/JVisualizerFrame.hpp +67 -0
- package/nitrogen/generated/android/c++/JVoiceVocabulary.hpp +98 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/AudioCategoryOptions.kt +61 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/AudioCodec.kt +28 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/AudioContainer.kt +29 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/AudioContentType.kt +23 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/AudioRoute.kt +71 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/AudioRouteKind.kt +34 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/BrowseItem.kt +76 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/BrowseSection.kt +71 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/BrowseSnapshot.kt +56 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/BufferState.kt +25 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/CacheStatus.kt +71 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/CastConfigureOptions.kt +56 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/CastConnectOptions.kt +51 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/CastDeviceKind.kt +27 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/CastDiscoveryState.kt +51 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/CastLocalNetworkPermission.kt +24 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/CastLocalNetworkPermissionEvent.kt +51 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/CastProtocol.kt +26 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/CastReceiver.kt +81 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/CastReceiverCapabilities.kt +61 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/CastRoute.kt +76 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/CastSessionDiedEvent.kt +56 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/CastSessionEndReason.kt +29 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/CastStreamingModel.kt +23 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/EqualizerBand.kt +56 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/EqualizerPreset.kt +61 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/EvictionPolicy.kt +23 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/Func_std__shared_ptr_Promise_std__shared_ptr_Promise_std__vector_BrowseItem______std__string.kt +80 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/Func_std__shared_ptr_Promise_std__shared_ptr_Promise_std__vector_TrackItem______MediaSearchRequest.kt +80 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/Func_std__shared_ptr_Promise_std__shared_ptr_Promise_std__vector_TrackItem______std__string.kt +80 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/Func_void.kt +80 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/Func_void_AudioRoute.kt +80 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/Func_void_BufferState.kt +80 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/Func_void_CacheStatus.kt +80 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/Func_void_CastDiscoveryState.kt +80 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/Func_void_CastLocalNetworkPermissionEvent.kt +80 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/Func_void_CastRoute.kt +80 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/Func_void_CastSessionDiedEvent.kt +80 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/Func_void_PlaybackError.kt +80 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/Func_void_PlayerProgress.kt +80 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/Func_void_PlayerState_StateChangeReason.kt +80 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/Func_void_ServiceReadyReason.kt +80 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/Func_void_SkipCapability.kt +80 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/Func_void_SleepTimerState.kt +80 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/Func_void_VisualizerErrorReason.kt +80 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/Func_void_VisualizerFrame.kt +80 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/Func_void_bool.kt +80 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/Func_void_double_double.kt +80 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/Func_void_std__optional_TrackItem__double_TrackChangeReason_std__optional_double_.kt +80 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/Func_void_std__optional_std__variant_nitro__NullType__NowPlayingFormat__.kt +80 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/Func_void_std__vector_CastReceiver_.kt +80 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/Func_void_std__vector_EqualizerBand_.kt +80 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/HybridCastManagerSpec.kt +169 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/HybridEqualizerSpec.kt +109 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/HybridTrackPlayerSpec.kt +419 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/HybridVisualizerSpec.kt +63 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/LookaheadCacheConfig.kt +56 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/MediaSearchOrigin.kt +23 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/MediaSearchQueueLocation.kt +24 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/MediaSearchReference.kt +23 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/MediaSearchReleaseDateRange.kt +56 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/MediaSearchRepeatMode.kt +24 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/MediaSearchRequest.kt +121 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/MediaSearchType.kt +26 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/NowPlayingFormat.kt +116 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/NowPlayingFormatAndroidExtras.kt +61 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/NowPlayingFormatIosExtras.kt +51 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/PitchCorrectionMode.kt +24 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/PlaybackError.kt +86 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/PlaybackErrorCode.kt +33 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/PlaybackMode.kt +56 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/PlaybackModeKind.kt +23 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/PlayerConfig.kt +111 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/PlayerProgress.kt +61 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/PlayerState.kt +28 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/RemoteControlOptions.kt +61 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/RemoteSkipMode.kt +23 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/RepeatMode.kt +24 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/ReplayGainMode.kt +24 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/SectionIcon.kt +34 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/ServiceReadyReason.kt +23 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/ShuffleResult.kt +61 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/SkipCapability.kt +56 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/SleepTimerState.kt +66 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/StateChangeReason.kt +28 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/TrackChangeReason.kt +28 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/TrackItem.kt +86 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/TrackSource.kt +24 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/Variant_NullType_Double.kt +62 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/Variant_NullType_NowPlayingFormat.kt +62 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/Variant_NullType_String.kt +62 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/VisualizerConfig.kt +61 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/VisualizerErrorReason.kt +25 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/VisualizerFrame.kt +61 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/VoiceVocabulary.kt +56 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/queueplayerOnLoad.kt +35 -0
- package/nitrogen/generated/android/queueplayer+autolinking.cmake +90 -0
- package/nitrogen/generated/android/queueplayer+autolinking.gradle +27 -0
- package/nitrogen/generated/android/queueplayerOnLoad.cpp +152 -0
- package/nitrogen/generated/android/queueplayerOnLoad.hpp +34 -0
- package/nitrogen/generated/ios/QueuePlayer+autolinking.rb +62 -0
- package/nitrogen/generated/ios/QueuePlayer-Swift-Cxx-Bridge.cpp +343 -0
- package/nitrogen/generated/ios/QueuePlayer-Swift-Cxx-Bridge.hpp +1831 -0
- package/nitrogen/generated/ios/QueuePlayer-Swift-Cxx-Umbrella.hpp +256 -0
- package/nitrogen/generated/ios/QueuePlayerAutolinking.mm +57 -0
- package/nitrogen/generated/ios/QueuePlayerAutolinking.swift +62 -0
- package/nitrogen/generated/ios/c++/HybridCastManagerSpecSwift.cpp +11 -0
- package/nitrogen/generated/ios/c++/HybridCastManagerSpecSwift.hpp +298 -0
- package/nitrogen/generated/ios/c++/HybridEqualizerSpecSwift.cpp +11 -0
- package/nitrogen/generated/ios/c++/HybridEqualizerSpecSwift.hpp +178 -0
- package/nitrogen/generated/ios/c++/HybridTrackPlayerSpecSwift.cpp +11 -0
- package/nitrogen/generated/ios/c++/HybridTrackPlayerSpecSwift.hpp +749 -0
- package/nitrogen/generated/ios/c++/HybridVisualizerSpecSwift.cpp +11 -0
- package/nitrogen/generated/ios/c++/HybridVisualizerSpecSwift.hpp +103 -0
- package/nitrogen/generated/ios/swift/AudioCategoryOptions.swift +78 -0
- package/nitrogen/generated/ios/swift/AudioCodec.swift +60 -0
- package/nitrogen/generated/ios/swift/AudioContainer.swift +64 -0
- package/nitrogen/generated/ios/swift/AudioContentType.swift +40 -0
- package/nitrogen/generated/ios/swift/AudioRoute.swift +49 -0
- package/nitrogen/generated/ios/swift/AudioRouteKind.swift +84 -0
- package/nitrogen/generated/ios/swift/BrowseItem.swift +93 -0
- package/nitrogen/generated/ios/swift/BrowseSection.swift +74 -0
- package/nitrogen/generated/ios/swift/BrowseSnapshot.swift +40 -0
- package/nitrogen/generated/ios/swift/BufferState.swift +48 -0
- package/nitrogen/generated/ios/swift/CacheStatus.swift +55 -0
- package/nitrogen/generated/ios/swift/CastConfigureOptions.swift +47 -0
- package/nitrogen/generated/ios/swift/CastConnectOptions.swift +29 -0
- package/nitrogen/generated/ios/swift/CastDeviceKind.swift +56 -0
- package/nitrogen/generated/ios/swift/CastDiscoveryState.swift +29 -0
- package/nitrogen/generated/ios/swift/CastLocalNetworkPermission.swift +44 -0
- package/nitrogen/generated/ios/swift/CastLocalNetworkPermissionEvent.swift +29 -0
- package/nitrogen/generated/ios/swift/CastProtocol.swift +52 -0
- package/nitrogen/generated/ios/swift/CastReceiver.swift +59 -0
- package/nitrogen/generated/ios/swift/CastReceiverCapabilities.swift +39 -0
- package/nitrogen/generated/ios/swift/CastRoute.swift +54 -0
- package/nitrogen/generated/ios/swift/CastSessionDiedEvent.swift +34 -0
- package/nitrogen/generated/ios/swift/CastSessionEndReason.swift +64 -0
- package/nitrogen/generated/ios/swift/CastStreamingModel.swift +40 -0
- package/nitrogen/generated/ios/swift/EqualizerBand.swift +34 -0
- package/nitrogen/generated/ios/swift/EqualizerPreset.swift +45 -0
- package/nitrogen/generated/ios/swift/EvictionPolicy.swift +40 -0
- package/nitrogen/generated/ios/swift/Func_std__shared_ptr_Promise_std__shared_ptr_Promise_std__vector_BrowseItem______std__string.swift +67 -0
- package/nitrogen/generated/ios/swift/Func_std__shared_ptr_Promise_std__shared_ptr_Promise_std__vector_TrackItem______MediaSearchRequest.swift +67 -0
- package/nitrogen/generated/ios/swift/Func_std__shared_ptr_Promise_std__shared_ptr_Promise_std__vector_TrackItem______std__string.swift +67 -0
- package/nitrogen/generated/ios/swift/Func_void.swift +46 -0
- package/nitrogen/generated/ios/swift/Func_void_AudioRoute.swift +46 -0
- package/nitrogen/generated/ios/swift/Func_void_BufferState.swift +46 -0
- package/nitrogen/generated/ios/swift/Func_void_CacheStatus.swift +46 -0
- package/nitrogen/generated/ios/swift/Func_void_CastDiscoveryState.swift +46 -0
- package/nitrogen/generated/ios/swift/Func_void_CastLocalNetworkPermission.swift +46 -0
- package/nitrogen/generated/ios/swift/Func_void_CastLocalNetworkPermissionEvent.swift +46 -0
- package/nitrogen/generated/ios/swift/Func_void_CastRoute.swift +46 -0
- package/nitrogen/generated/ios/swift/Func_void_CastSessionDiedEvent.swift +46 -0
- package/nitrogen/generated/ios/swift/Func_void_PlaybackError.swift +46 -0
- package/nitrogen/generated/ios/swift/Func_void_PlayerProgress.swift +46 -0
- package/nitrogen/generated/ios/swift/Func_void_PlayerState_StateChangeReason.swift +46 -0
- package/nitrogen/generated/ios/swift/Func_void_ServiceReadyReason.swift +46 -0
- package/nitrogen/generated/ios/swift/Func_void_ShuffleResult.swift +46 -0
- package/nitrogen/generated/ios/swift/Func_void_SkipCapability.swift +46 -0
- package/nitrogen/generated/ios/swift/Func_void_SleepTimerState.swift +46 -0
- package/nitrogen/generated/ios/swift/Func_void_VisualizerErrorReason.swift +46 -0
- package/nitrogen/generated/ios/swift/Func_void_VisualizerFrame.swift +46 -0
- package/nitrogen/generated/ios/swift/Func_void_bool.swift +46 -0
- package/nitrogen/generated/ios/swift/Func_void_double_double.swift +46 -0
- package/nitrogen/generated/ios/swift/Func_void_std__exception_ptr.swift +46 -0
- package/nitrogen/generated/ios/swift/Func_void_std__optional_TrackItem__double_TrackChangeReason_std__optional_double_.swift +53 -0
- package/nitrogen/generated/ios/swift/Func_void_std__optional_std__variant_nitro__NullType__NowPlayingFormat__.swift +65 -0
- package/nitrogen/generated/ios/swift/Func_void_std__shared_ptr_Promise_std__vector_BrowseItem___.swift +66 -0
- package/nitrogen/generated/ios/swift/Func_void_std__shared_ptr_Promise_std__vector_TrackItem___.swift +66 -0
- package/nitrogen/generated/ios/swift/Func_void_std__vector_BrowseItem_.swift +46 -0
- package/nitrogen/generated/ios/swift/Func_void_std__vector_CastReceiver_.swift +46 -0
- package/nitrogen/generated/ios/swift/Func_void_std__vector_EqualizerBand_.swift +46 -0
- package/nitrogen/generated/ios/swift/Func_void_std__vector_TrackItem_.swift +46 -0
- package/nitrogen/generated/ios/swift/HybridCastManagerSpec.swift +76 -0
- package/nitrogen/generated/ios/swift/HybridCastManagerSpec_cxx.swift +519 -0
- package/nitrogen/generated/ios/swift/HybridEqualizerSpec.swift +66 -0
- package/nitrogen/generated/ios/swift/HybridEqualizerSpec_cxx.swift +353 -0
- package/nitrogen/generated/ios/swift/HybridTrackPlayerSpec.swift +122 -0
- package/nitrogen/generated/ios/swift/HybridTrackPlayerSpec_cxx.swift +1430 -0
- package/nitrogen/generated/ios/swift/HybridVisualizerSpec.swift +56 -0
- package/nitrogen/generated/ios/swift/HybridVisualizerSpec_cxx.swift +163 -0
- package/nitrogen/generated/ios/swift/LookaheadCacheConfig.swift +34 -0
- package/nitrogen/generated/ios/swift/MediaSearchOrigin.swift +40 -0
- package/nitrogen/generated/ios/swift/MediaSearchQueueLocation.swift +44 -0
- package/nitrogen/generated/ios/swift/MediaSearchReference.swift +40 -0
- package/nitrogen/generated/ios/swift/MediaSearchReleaseDateRange.swift +60 -0
- package/nitrogen/generated/ios/swift/MediaSearchRepeatMode.swift +44 -0
- package/nitrogen/generated/ios/swift/MediaSearchRequest.swift +239 -0
- package/nitrogen/generated/ios/swift/MediaSearchType.swift +52 -0
- package/nitrogen/generated/ios/swift/NowPlayingFormat.swift +394 -0
- package/nitrogen/generated/ios/swift/NowPlayingFormatAndroidExtras.swift +135 -0
- package/nitrogen/generated/ios/swift/NowPlayingFormatIosExtras.swift +61 -0
- package/nitrogen/generated/ios/swift/PitchCorrectionMode.swift +44 -0
- package/nitrogen/generated/ios/swift/PlaybackError.swift +64 -0
- package/nitrogen/generated/ios/swift/PlaybackErrorCode.swift +80 -0
- package/nitrogen/generated/ios/swift/PlaybackMode.swift +47 -0
- package/nitrogen/generated/ios/swift/PlaybackModeKind.swift +40 -0
- package/nitrogen/generated/ios/swift/PlayerConfig.swift +251 -0
- package/nitrogen/generated/ios/swift/PlayerProgress.swift +39 -0
- package/nitrogen/generated/ios/swift/PlayerState.swift +60 -0
- package/nitrogen/generated/ios/swift/RemoteControlOptions.swift +39 -0
- package/nitrogen/generated/ios/swift/RemoteSkipMode.swift +40 -0
- package/nitrogen/generated/ios/swift/RepeatMode.swift +44 -0
- package/nitrogen/generated/ios/swift/ReplayGainMode.swift +44 -0
- package/nitrogen/generated/ios/swift/SectionIcon.swift +84 -0
- package/nitrogen/generated/ios/swift/ServiceReadyReason.swift +40 -0
- package/nitrogen/generated/ios/swift/ShuffleResult.swift +51 -0
- package/nitrogen/generated/ios/swift/SkipCapability.swift +34 -0
- package/nitrogen/generated/ios/swift/SleepTimerState.swift +70 -0
- package/nitrogen/generated/ios/swift/StateChangeReason.swift +60 -0
- package/nitrogen/generated/ios/swift/TrackChangeReason.swift +60 -0
- package/nitrogen/generated/ios/swift/TrackItem.swift +169 -0
- package/nitrogen/generated/ios/swift/TrackSource.swift +44 -0
- package/nitrogen/generated/ios/swift/Variant_NullType_Double.swift +30 -0
- package/nitrogen/generated/ios/swift/Variant_NullType_NowPlayingFormat.swift +30 -0
- package/nitrogen/generated/ios/swift/Variant_NullType_String.swift +30 -0
- package/nitrogen/generated/ios/swift/VisualizerConfig.swift +52 -0
- package/nitrogen/generated/ios/swift/VisualizerErrorReason.swift +48 -0
- package/nitrogen/generated/ios/swift/VisualizerFrame.swift +52 -0
- package/nitrogen/generated/ios/swift/VoiceVocabulary.swift +46 -0
- package/nitrogen/generated/shared/c++/AudioCategoryOptions.hpp +91 -0
- package/nitrogen/generated/shared/c++/AudioCodec.hpp +96 -0
- package/nitrogen/generated/shared/c++/AudioContainer.hpp +100 -0
- package/nitrogen/generated/shared/c++/AudioContentType.hpp +76 -0
- package/nitrogen/generated/shared/c++/AudioRoute.hpp +101 -0
- package/nitrogen/generated/shared/c++/AudioRouteKind.hpp +120 -0
- package/nitrogen/generated/shared/c++/BrowseItem.hpp +104 -0
- package/nitrogen/generated/shared/c++/BrowseSection.hpp +106 -0
- package/nitrogen/generated/shared/c++/BrowseSnapshot.hpp +90 -0
- package/nitrogen/generated/shared/c++/BufferState.hpp +84 -0
- package/nitrogen/generated/shared/c++/CacheStatus.hpp +100 -0
- package/nitrogen/generated/shared/c++/CastConfigureOptions.hpp +88 -0
- package/nitrogen/generated/shared/c++/CastConnectOptions.hpp +83 -0
- package/nitrogen/generated/shared/c++/CastDeviceKind.hpp +92 -0
- package/nitrogen/generated/shared/c++/CastDiscoveryState.hpp +83 -0
- package/nitrogen/generated/shared/c++/CastLocalNetworkPermission.hpp +80 -0
- package/nitrogen/generated/shared/c++/CastLocalNetworkPermissionEvent.hpp +84 -0
- package/nitrogen/generated/shared/c++/CastProtocol.hpp +88 -0
- package/nitrogen/generated/shared/c++/CastReceiver.hpp +115 -0
- package/nitrogen/generated/shared/c++/CastReceiverCapabilities.hpp +92 -0
- package/nitrogen/generated/shared/c++/CastRoute.hpp +111 -0
- package/nitrogen/generated/shared/c++/CastSessionDiedEvent.hpp +89 -0
- package/nitrogen/generated/shared/c++/CastSessionEndReason.hpp +100 -0
- package/nitrogen/generated/shared/c++/CastStreamingModel.hpp +76 -0
- package/nitrogen/generated/shared/c++/EqualizerBand.hpp +87 -0
- package/nitrogen/generated/shared/c++/EqualizerPreset.hpp +92 -0
- package/nitrogen/generated/shared/c++/EvictionPolicy.hpp +76 -0
- package/nitrogen/generated/shared/c++/HybridCastManagerSpec.cpp +42 -0
- package/nitrogen/generated/shared/c++/HybridCastManagerSpec.hpp +115 -0
- package/nitrogen/generated/shared/c++/HybridEqualizerSpec.cpp +32 -0
- package/nitrogen/generated/shared/c++/HybridEqualizerSpec.hpp +81 -0
- package/nitrogen/generated/shared/c++/HybridTrackPlayerSpec.cpp +88 -0
- package/nitrogen/generated/shared/c++/HybridTrackPlayerSpec.hpp +209 -0
- package/nitrogen/generated/shared/c++/HybridVisualizerSpec.cpp +22 -0
- package/nitrogen/generated/shared/c++/HybridVisualizerSpec.hpp +71 -0
- package/nitrogen/generated/shared/c++/LookaheadCacheConfig.hpp +87 -0
- package/nitrogen/generated/shared/c++/MediaSearchOrigin.hpp +76 -0
- package/nitrogen/generated/shared/c++/MediaSearchQueueLocation.hpp +80 -0
- package/nitrogen/generated/shared/c++/MediaSearchReference.hpp +76 -0
- package/nitrogen/generated/shared/c++/MediaSearchReleaseDateRange.hpp +87 -0
- package/nitrogen/generated/shared/c++/MediaSearchRepeatMode.hpp +80 -0
- package/nitrogen/generated/shared/c++/MediaSearchRequest.hpp +157 -0
- package/nitrogen/generated/shared/c++/MediaSearchType.hpp +88 -0
- package/nitrogen/generated/shared/c++/NowPlayingFormat.hpp +149 -0
- package/nitrogen/generated/shared/c++/NowPlayingFormatAndroidExtras.hpp +94 -0
- package/nitrogen/generated/shared/c++/NowPlayingFormatIosExtras.hpp +85 -0
- package/nitrogen/generated/shared/c++/PitchCorrectionMode.hpp +80 -0
- package/nitrogen/generated/shared/c++/PlaybackError.hpp +113 -0
- package/nitrogen/generated/shared/c++/PlaybackErrorCode.hpp +116 -0
- package/nitrogen/generated/shared/c++/PlaybackMode.hpp +89 -0
- package/nitrogen/generated/shared/c++/PlaybackModeKind.hpp +76 -0
- package/nitrogen/generated/shared/c++/PlayerConfig.hpp +141 -0
- package/nitrogen/generated/shared/c++/PlayerProgress.hpp +91 -0
- package/nitrogen/generated/shared/c++/PlayerState.hpp +96 -0
- package/nitrogen/generated/shared/c++/RemoteControlOptions.hpp +92 -0
- package/nitrogen/generated/shared/c++/RemoteSkipMode.hpp +76 -0
- package/nitrogen/generated/shared/c++/RepeatMode.hpp +80 -0
- package/nitrogen/generated/shared/c++/ReplayGainMode.hpp +80 -0
- package/nitrogen/generated/shared/c++/SectionIcon.hpp +120 -0
- package/nitrogen/generated/shared/c++/ServiceReadyReason.hpp +76 -0
- package/nitrogen/generated/shared/c++/ShuffleResult.hpp +94 -0
- package/nitrogen/generated/shared/c++/SkipCapability.hpp +87 -0
- package/nitrogen/generated/shared/c++/SleepTimerState.hpp +95 -0
- package/nitrogen/generated/shared/c++/StateChangeReason.hpp +96 -0
- package/nitrogen/generated/shared/c++/TrackChangeReason.hpp +96 -0
- package/nitrogen/generated/shared/c++/TrackItem.hpp +113 -0
- package/nitrogen/generated/shared/c++/TrackSource.hpp +80 -0
- package/nitrogen/generated/shared/c++/VisualizerConfig.hpp +91 -0
- package/nitrogen/generated/shared/c++/VisualizerErrorReason.hpp +84 -0
- package/nitrogen/generated/shared/c++/VisualizerFrame.hpp +92 -0
- package/nitrogen/generated/shared/c++/VoiceVocabulary.hpp +88 -0
- package/package.json +167 -0
- package/src/CastManager.nitro.ts +206 -0
- package/src/Equalizer.nitro.ts +37 -0
- package/src/TrackPlayer.nitro.ts +667 -0
- package/src/Visualizer.nitro.ts +48 -0
- package/src/cast/index.ts +224 -0
- package/src/clients.ts +50 -0
- package/src/equalizer.ts +86 -0
- package/src/hooks/index.ts +33 -0
- package/src/hooks/useActiveTrack.ts +55 -0
- package/src/hooks/useAudioRoute.ts +69 -0
- package/src/hooks/useBufferState.ts +35 -0
- package/src/hooks/useCanSkip.ts +92 -0
- package/src/hooks/useCast.ts +121 -0
- package/src/hooks/useEqualizer.ts +111 -0
- package/src/hooks/useFullyBuffered.ts +32 -0
- package/src/hooks/useLookaheadCache.ts +54 -0
- package/src/hooks/useNowPlayingFormat.ts +43 -0
- package/src/hooks/usePlaybackState.ts +34 -0
- package/src/hooks/useProgress.ts +36 -0
- package/src/hooks/useSleepTimer.ts +63 -0
- package/src/hooks/useVisualizerState.ts +121 -0
- package/src/index.ts +125 -0
- package/src/playbackService.ts +304 -0
- package/src/types.ts +1387 -0
|
@@ -0,0 +1,1350 @@
|
|
|
1
|
+
package com.margelo.nitro.queueplayer
|
|
2
|
+
|
|
3
|
+
import android.content.Context
|
|
4
|
+
import android.media.AudioFocusRequest
|
|
5
|
+
import android.media.AudioManager
|
|
6
|
+
import android.os.Build
|
|
7
|
+
import androidx.annotation.MainThread
|
|
8
|
+
import androidx.annotation.RequiresApi
|
|
9
|
+
import androidx.media3.common.AudioAttributes
|
|
10
|
+
import androidx.media3.common.C
|
|
11
|
+
import androidx.media3.common.MediaItem
|
|
12
|
+
import androidx.media3.common.Metadata
|
|
13
|
+
import androidx.media3.common.PlaybackException
|
|
14
|
+
import androidx.media3.common.Player
|
|
15
|
+
import androidx.media3.common.Tracks
|
|
16
|
+
import androidx.media3.common.audio.AudioProcessor
|
|
17
|
+
import androidx.media3.common.util.UnstableApi
|
|
18
|
+
import androidx.media3.exoplayer.DefaultLoadControl
|
|
19
|
+
import androidx.media3.exoplayer.ExoPlayer
|
|
20
|
+
import androidx.media3.exoplayer.LoadControl
|
|
21
|
+
import androidx.media3.exoplayer.RenderersFactory
|
|
22
|
+
import androidx.media3.exoplayer.analytics.AnalyticsListener
|
|
23
|
+
import androidx.media3.exoplayer.audio.TeeAudioProcessor
|
|
24
|
+
import androidx.media3.exoplayer.source.MediaSource
|
|
25
|
+
import kotlinx.coroutines.CoroutineScope
|
|
26
|
+
import kotlinx.coroutines.Dispatchers
|
|
27
|
+
import kotlinx.coroutines.Job
|
|
28
|
+
import kotlinx.coroutines.SupervisorJob
|
|
29
|
+
import kotlinx.coroutines.cancel
|
|
30
|
+
import kotlinx.coroutines.delay
|
|
31
|
+
import kotlinx.coroutines.isActive
|
|
32
|
+
import kotlinx.coroutines.launch
|
|
33
|
+
import java.util.concurrent.CopyOnWriteArrayList
|
|
34
|
+
import kotlin.math.PI
|
|
35
|
+
import kotlin.math.cos
|
|
36
|
+
import kotlin.math.min
|
|
37
|
+
import kotlin.math.sin
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Dual-`ExoPlayer` engine implementing equal-power crossfade at
|
|
41
|
+
* track boundaries.
|
|
42
|
+
*
|
|
43
|
+
* Two ExoPlayer instances rotate roles: the **leading** player is
|
|
44
|
+
* the one currently audible to the listener; the **standby**
|
|
45
|
+
* player prerolls the next queue item ahead of the fade window
|
|
46
|
+
* and ramps up from silence as the leading ramps down. At
|
|
47
|
+
* fade-complete the roles swap.
|
|
48
|
+
*
|
|
49
|
+
* Build constraints (per platform research):
|
|
50
|
+
* - Per-player [MediaSource.Factory]: sharing a factory across
|
|
51
|
+
* two `ExoPlayer` instances is documented as an anti-pattern
|
|
52
|
+
* (see `androidx.media` issue #9099 + ExoPlayer #3438) — DRM
|
|
53
|
+
* provider state and `MediaSource.Factory` configuration
|
|
54
|
+
* couple to a single playback Looper. Each player must own its
|
|
55
|
+
* own factory; the underlying `SimpleCache` is shared via the
|
|
56
|
+
* factory's `CacheDataSource.Factory` so cache reads benefit
|
|
57
|
+
* from prior downloads.
|
|
58
|
+
* - Per-player [LoadControl]: standby uses a smaller buffer
|
|
59
|
+
* window so it doesn't compete with the active player for
|
|
60
|
+
* memory. Defaults align with the leading player.
|
|
61
|
+
* - `handleAudioFocus = false` on BOTH legs: two simultaneously-
|
|
62
|
+
* active Media3 focus requests fight each other (each
|
|
63
|
+
* `AudioFocusManager$AudioFocusListener` is a distinct
|
|
64
|
+
* listener instance from AudioFlinger's perspective even
|
|
65
|
+
* within the same UID — a second request fires
|
|
66
|
+
* `AUDIOFOCUS_LOSS` to the first). The engine instead owns
|
|
67
|
+
* one [AudioManager.OnAudioFocusChangeListener] that
|
|
68
|
+
* coordinates pause/duck across both players.
|
|
69
|
+
*
|
|
70
|
+
* Fade timing:
|
|
71
|
+
* - Position-poll on the leading player at 200ms tick fires
|
|
72
|
+
* when `currentPosition >= duration - crossfadeDurationMs - 100ms`.
|
|
73
|
+
* - The execute coroutine runs on `Dispatchers.Main` at 20ms tick
|
|
74
|
+
* computing equal-power gains (`cos(t·π/2)` outgoing,
|
|
75
|
+
* `sin(t·π/2)` incoming) — `Choreographer.FrameCallback` is
|
|
76
|
+
* unsuitable for audio-only playback (VSYNC pauses when no
|
|
77
|
+
* Surface is rendering, which would freeze the ramp).
|
|
78
|
+
*
|
|
79
|
+
* Edge cases handled:
|
|
80
|
+
* - Manual skip / pause / seek mid-fade: cancel the execute job,
|
|
81
|
+
* snap leading to volume 1.0, release standby's item.
|
|
82
|
+
* - Active player ends before fade completes (duration metadata
|
|
83
|
+
* wrong / network truncation): `STATE_ENDED` listener cancels
|
|
84
|
+
* the poll, snaps standby to volume 1.0, swaps roles
|
|
85
|
+
* immediately so playback continues.
|
|
86
|
+
* - Auto-clamp `crossfadeDurationMs` to `min(configured,
|
|
87
|
+
* leadingDuration/2, standbyDuration/2)` at fade-arm time so
|
|
88
|
+
* short tracks never start a fade that can't complete.
|
|
89
|
+
*/
|
|
90
|
+
@UnstableApi
|
|
91
|
+
@RequiresApi(Build.VERSION_CODES.O) // AudioFocusRequest is API 26+
|
|
92
|
+
internal class CrossfadeEngine(
|
|
93
|
+
private val context: Context,
|
|
94
|
+
/**
|
|
95
|
+
* Provider invoked once per leg at construction so each
|
|
96
|
+
* `ExoPlayer` instance receives its own `MediaSource.Factory`
|
|
97
|
+
* (sharing factories across players is a documented Media3
|
|
98
|
+
* anti-pattern — see `androidx.media #9099`). The provider may
|
|
99
|
+
* close over a shared `SimpleCache` so cache hits are reused
|
|
100
|
+
* across legs, which is the safe + recommended composition.
|
|
101
|
+
*/
|
|
102
|
+
private val mediaSourceFactoryProvider: (() -> MediaSource.Factory)? = null,
|
|
103
|
+
private val audioContentType: AudioContentType? = null,
|
|
104
|
+
private val crossfadeDurationMs: Long,
|
|
105
|
+
) : PlaybackEngine {
|
|
106
|
+
|
|
107
|
+
init {
|
|
108
|
+
require(crossfadeDurationMs >= 0L) { "crossfadeDurationMs must be non-negative" }
|
|
109
|
+
// Defence-in-depth: a remote PcmStream sink (AirPlay 1/2) can only
|
|
110
|
+
// accept input from ONE ExoPlayer at a time. CrossfadeEngine drives
|
|
111
|
+
// two players, so AirPlay streams through the dedicated single-player
|
|
112
|
+
// AirPlayEngine instead while a push-PCM route is active — the engine
|
|
113
|
+
// selector never builds a CrossfadeEngine then. This `check` catches a
|
|
114
|
+
// regression in that gating loud.
|
|
115
|
+
check(!com.margelo.nitro.queueplayer.cast.CastSinkRouter.isRemoteActive()) {
|
|
116
|
+
"CrossfadeEngine instantiated while a cast push-PCM session is active; " +
|
|
117
|
+
"the dedicated AirPlayEngine handles push-PCM routes."
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
override var delegate: PlaybackEngineDelegate? = null
|
|
122
|
+
|
|
123
|
+
// --- Visualizer audio chain (declared before players so buildPlayer
|
|
124
|
+
// can reference them; the engine owns the always-on Tee and exposes
|
|
125
|
+
// attach via the bufferSinks accumulator) ---
|
|
126
|
+
|
|
127
|
+
private val bufferSinks = CopyOnWriteArrayList<TeeAudioProcessor.AudioBufferSink>()
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* One [VisualizerTeeBuilder] per ExoPlayer leg — sharing a single
|
|
131
|
+
* Tee across two AudioSinks would interleave its `flush` +
|
|
132
|
+
* `handleBuffer` state. Both legs feed the same shared
|
|
133
|
+
* [bufferSinks] accumulator so a visualizer subscriber sees PCM
|
|
134
|
+
* from whichever leg is currently audible. Null when
|
|
135
|
+
* `VisualizationGate.isEnabled()` is false at construction time.
|
|
136
|
+
* See [GaplessEngine.visualizerTeeBuilder] for the orthogonality
|
|
137
|
+
* rationale with EQ.
|
|
138
|
+
*/
|
|
139
|
+
private val visualizerTeeBuilderA: VisualizerTeeBuilder? =
|
|
140
|
+
if (VisualizationGate.isEnabled()) VisualizerTeeBuilder(bufferSinks) else null
|
|
141
|
+
private val visualizerTeeBuilderB: VisualizerTeeBuilder? =
|
|
142
|
+
if (VisualizationGate.isEnabled()) VisualizerTeeBuilder(bufferSinks) else null
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* One [ReplayGainAudioProcessor] per leg — each leg's
|
|
146
|
+
* [Player.Listener.onMetadata] pushes ONLY to its own processor so
|
|
147
|
+
* mid-fade tag arrival on the standby leg cannot stomp the leading
|
|
148
|
+
* leg's gain. The crossfade volume ramp (player.volume) is a
|
|
149
|
+
* separate layer; RG composes multiplicatively underneath.
|
|
150
|
+
*/
|
|
151
|
+
private val replayGainProcessorA = ReplayGainAudioProcessor()
|
|
152
|
+
private val replayGainProcessorB = ReplayGainAudioProcessor()
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Per-leg cached [ReplayGainData]. Mirror the iOS per-item
|
|
156
|
+
* `ItemRGState` cache: each leg owns its own data + processor +
|
|
157
|
+
* extraction lifecycle.
|
|
158
|
+
*
|
|
159
|
+
* ReplayGain recompute paths (per-leg listener parse → cache +
|
|
160
|
+
* push, JS-driven mode flip → both legs' cache snapshot + push)
|
|
161
|
+
* all run on the application main thread (Media3 dispatches
|
|
162
|
+
* `Player.Listener` callbacks on the application looper;
|
|
163
|
+
* [TrackPlayer.setReplayGainMode] hops to main before invoking
|
|
164
|
+
* [recomputeReplayGainFromConfig]). The @Volatile fields +
|
|
165
|
+
* @Volatile gain on each [ReplayGainAudioProcessor] are sufficient
|
|
166
|
+
* because there's only one writer per leg's cache + processor at
|
|
167
|
+
* any time.
|
|
168
|
+
*/
|
|
169
|
+
@Volatile
|
|
170
|
+
private var cachedReplayGainDataA: ReplayGainData? = null
|
|
171
|
+
@Volatile
|
|
172
|
+
private var cachedReplayGainDataB: ReplayGainData? = null
|
|
173
|
+
|
|
174
|
+
// One mode-gated pitch-correction chain PER LEG — each holds its own
|
|
175
|
+
// (stateful) music time-stretcher, mirroring the per-leg RG + viz Tee rule.
|
|
176
|
+
private val pitchChainA: PitchAwareAudioProcessorChain = run {
|
|
177
|
+
// Order: RG runs FIRST (pre-viz) so the visualizer + downstream
|
|
178
|
+
// pipeline see post-RG samples — matches "what the user hears".
|
|
179
|
+
val procs = mutableListOf<AudioProcessor>(replayGainProcessorA)
|
|
180
|
+
visualizerTeeBuilderA?.audioProcessor?.let { procs += it }
|
|
181
|
+
PitchAwareAudioProcessorChain(procs.toTypedArray())
|
|
182
|
+
}
|
|
183
|
+
private val pitchChainB: PitchAwareAudioProcessorChain = run {
|
|
184
|
+
val procs = mutableListOf<AudioProcessor>(replayGainProcessorB)
|
|
185
|
+
visualizerTeeBuilderB?.audioProcessor?.let { procs += it }
|
|
186
|
+
PitchAwareAudioProcessorChain(procs.toTypedArray())
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
private val renderersFactoryA: RenderersFactory =
|
|
190
|
+
AudioPipelineRenderersFactory(context, pitchChainA)
|
|
191
|
+
private val renderersFactoryB: RenderersFactory =
|
|
192
|
+
AudioPipelineRenderersFactory(context, pitchChainB)
|
|
193
|
+
|
|
194
|
+
// --- Players ---
|
|
195
|
+
|
|
196
|
+
internal val playerA: ExoPlayer = buildPlayer(isStandby = false, renderersFactory = renderersFactoryA)
|
|
197
|
+
internal val playerB: ExoPlayer = buildPlayer(isStandby = true, renderersFactory = renderersFactoryB)
|
|
198
|
+
|
|
199
|
+
/** `true` when [playerA] is the leading (currently audible) leg. */
|
|
200
|
+
@Volatile
|
|
201
|
+
internal var currentRoleIsA: Boolean = true
|
|
202
|
+
private set
|
|
203
|
+
|
|
204
|
+
/** Leading leg — the player currently producing audible audio. */
|
|
205
|
+
internal val leadingPlayer: ExoPlayer
|
|
206
|
+
get() = if (currentRoleIsA) playerA else playerB
|
|
207
|
+
|
|
208
|
+
/** Standby leg — prerolls the next queue item; volume 0 outside fade window. */
|
|
209
|
+
internal val standbyPlayer: ExoPlayer
|
|
210
|
+
get() = if (currentRoleIsA) playerB else playerA
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* Service-level wiring binds `MediaSession` + `audioFocusListener`
|
|
214
|
+
* to the leading leg. Mid-fade role swap doesn't update this
|
|
215
|
+
* field; `CrossfadeForwardingPlayer` is the layer that hides
|
|
216
|
+
* the rotation from `MediaSession`.
|
|
217
|
+
*/
|
|
218
|
+
override val mediaSessionPlayer: ExoPlayer get() = leadingPlayer
|
|
219
|
+
|
|
220
|
+
// --- Listener registries ---
|
|
221
|
+
|
|
222
|
+
private val sessionIdListeners = CopyOnWriteArrayList<(IntArray) -> Unit>()
|
|
223
|
+
|
|
224
|
+
private var sessionIdA: Int = C.AUDIO_SESSION_ID_UNSET
|
|
225
|
+
private var sessionIdB: Int = C.AUDIO_SESSION_ID_UNSET
|
|
226
|
+
|
|
227
|
+
// --- Fade state ---
|
|
228
|
+
|
|
229
|
+
private val engineScope = CoroutineScope(Dispatchers.Main + SupervisorJob())
|
|
230
|
+
|
|
231
|
+
/** Active fade job; null outside fade window. */
|
|
232
|
+
private var fadeJob: Job? = null
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* Position-poll Job — watches the leading leg's `currentPosition`
|
|
236
|
+
* every 200ms while playback is active and arms the fade when the
|
|
237
|
+
* window approaches. Cancelled on pause / stop / seek / skip /
|
|
238
|
+
* release. Restarted when [play] resumes.
|
|
239
|
+
*/
|
|
240
|
+
private var armingJob: Job? = null
|
|
241
|
+
|
|
242
|
+
/** True after [armCrossfade] has prerolled standby for the upcoming fade. */
|
|
243
|
+
private var standbyArmed: Boolean = false
|
|
244
|
+
|
|
245
|
+
/**
|
|
246
|
+
* User-set volume for the leading leg, preserved across
|
|
247
|
+
* fade-cancel paths so manual `setVolume(v)` survives a
|
|
248
|
+
* subsequent skip / pause / seek that resets the fade.
|
|
249
|
+
* Standby leg is always at 0.0 outside the fade window.
|
|
250
|
+
*/
|
|
251
|
+
private var userVolume: Float = 1f
|
|
252
|
+
|
|
253
|
+
/** Pitch-correction mode, applied to both legs' PlaybackParameters. */
|
|
254
|
+
private var pitchCorrectionMode: PitchCorrectionMode = PitchCorrectionMode.VOICE
|
|
255
|
+
|
|
256
|
+
/** Lib-level audio focus owner (replaces per-player handleAudioFocus = true). */
|
|
257
|
+
private val audioManager: AudioManager =
|
|
258
|
+
context.getSystemService(Context.AUDIO_SERVICE) as AudioManager
|
|
259
|
+
|
|
260
|
+
/**
|
|
261
|
+
* Tracks whether the engine was actively playing at the moment
|
|
262
|
+
* of an `AUDIOFOCUS_LOSS_TRANSIENT` so the matching `_GAIN`
|
|
263
|
+
* resumes only when the user hadn't already paused us.
|
|
264
|
+
* `false` when the user paused before focus was lost — in that
|
|
265
|
+
* case the gain is a no-op.
|
|
266
|
+
*/
|
|
267
|
+
private var pausedByFocusLoss: Boolean = false
|
|
268
|
+
|
|
269
|
+
/**
|
|
270
|
+
* `true` while focus duck is active. The fade tick reads
|
|
271
|
+
* [effectiveUserVolume] so an in-flight ramp respects the duck;
|
|
272
|
+
* outside the fade window, the duck path sets player volumes
|
|
273
|
+
* directly. Cleared on `AUDIOFOCUS_GAIN`.
|
|
274
|
+
*/
|
|
275
|
+
private var duckActive: Boolean = false
|
|
276
|
+
|
|
277
|
+
/** Pre-duck multiplier baked into the running fade, so the
|
|
278
|
+
* fade tick + the standalone-duck path agree on the target
|
|
279
|
+
* volume. */
|
|
280
|
+
private fun effectiveUserVolume(): Float =
|
|
281
|
+
if (duckActive) userVolume * DUCK_MULTIPLIER else userVolume
|
|
282
|
+
|
|
283
|
+
/**
|
|
284
|
+
* Threading: AudioManager dispatches the focus callback on the
|
|
285
|
+
* Looper attached to the main thread by default (no Handler
|
|
286
|
+
* passed to the builder), so this lands on the same dispatcher
|
|
287
|
+
* the engine expects for every other call.
|
|
288
|
+
*/
|
|
289
|
+
private val focusListener = AudioManager.OnAudioFocusChangeListener { focusChange ->
|
|
290
|
+
when (focusChange) {
|
|
291
|
+
AudioManager.AUDIOFOCUS_LOSS -> {
|
|
292
|
+
pausedByFocusLoss = false
|
|
293
|
+
duckActive = false
|
|
294
|
+
playerA.pause()
|
|
295
|
+
playerB.pause()
|
|
296
|
+
}
|
|
297
|
+
AudioManager.AUDIOFOCUS_LOSS_TRANSIENT -> {
|
|
298
|
+
pausedByFocusLoss = leadingPlayer.isPlaying
|
|
299
|
+
duckActive = false
|
|
300
|
+
playerA.pause()
|
|
301
|
+
playerB.pause()
|
|
302
|
+
}
|
|
303
|
+
AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK -> {
|
|
304
|
+
// Notification ping over media etc. — Media3's
|
|
305
|
+
// AudioFocusManager would lower the volume rather than
|
|
306
|
+
// pause; mirror that since we own the policy now.
|
|
307
|
+
// Toggling [duckActive] makes the fade tick honour the
|
|
308
|
+
// duck via [effectiveUserVolume]; for the standalone case
|
|
309
|
+
// (no fade in flight) the player volumes are set directly
|
|
310
|
+
// so the duck takes effect immediately.
|
|
311
|
+
duckActive = true
|
|
312
|
+
if (fadeJob?.isActive != true) {
|
|
313
|
+
leadingPlayer.volume = effectiveUserVolume()
|
|
314
|
+
standbyPlayer.volume = 0f
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
AudioManager.AUDIOFOCUS_GAIN -> {
|
|
318
|
+
if (duckActive) {
|
|
319
|
+
duckActive = false
|
|
320
|
+
if (fadeJob?.isActive != true) {
|
|
321
|
+
leadingPlayer.volume = effectiveUserVolume()
|
|
322
|
+
standbyPlayer.volume = 0f
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
if (pausedByFocusLoss) {
|
|
326
|
+
pausedByFocusLoss = false
|
|
327
|
+
// Resume via the engine's own [play] so the boundary-arming
|
|
328
|
+
// poll restarts; calling `leadingPlayer.play()` directly
|
|
329
|
+
// would resume audio but leave the next fade boundary
|
|
330
|
+
// un-armed. Audio focus is already granted on this branch
|
|
331
|
+
// so the inner requestAudioFocus is a no-op.
|
|
332
|
+
play()
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
private val focusRequest: AudioFocusRequest = AudioFocusRequest
|
|
338
|
+
.Builder(AudioManager.AUDIOFOCUS_GAIN)
|
|
339
|
+
.setAudioAttributes(buildPlatformAudioAttributes())
|
|
340
|
+
.setOnAudioFocusChangeListener(focusListener)
|
|
341
|
+
.setAcceptsDelayedFocusGain(false)
|
|
342
|
+
.build()
|
|
343
|
+
|
|
344
|
+
private var released: Boolean = false
|
|
345
|
+
|
|
346
|
+
// --- Player listeners ---
|
|
347
|
+
|
|
348
|
+
private val leadingPlayerListener = object : Player.Listener {
|
|
349
|
+
override fun onPlaybackStateChanged(playbackState: Int) {
|
|
350
|
+
if (released) return
|
|
351
|
+
delegate?.onStateMaybeChanged(this@CrossfadeEngine, playWhenReadyReason = null)
|
|
352
|
+
if (playbackState == Player.STATE_ENDED) {
|
|
353
|
+
// Leading player ended naturally — if a fade was in flight
|
|
354
|
+
// it didn't complete in time. Cancel the ramp, snap
|
|
355
|
+
// standby to full volume, swap roles immediately so
|
|
356
|
+
// playback continues without a silent gap.
|
|
357
|
+
if (fadeJob?.isActive == true) {
|
|
358
|
+
cancelFadeAndPromoteStandby()
|
|
359
|
+
}
|
|
360
|
+
delegate?.onPlaybackEnded(this@CrossfadeEngine)
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
override fun onPlayWhenReadyChanged(playWhenReady: Boolean, reason: Int) {
|
|
365
|
+
if (released) return
|
|
366
|
+
delegate?.onStateMaybeChanged(this@CrossfadeEngine, playWhenReadyReason = reason)
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
override fun onIsPlayingChanged(isPlaying: Boolean) {
|
|
370
|
+
if (released) return
|
|
371
|
+
delegate?.onIsPlayingChanged(this@CrossfadeEngine, isPlaying)
|
|
372
|
+
delegate?.onStateMaybeChanged(this@CrossfadeEngine, playWhenReadyReason = null)
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
override fun onMediaItemTransition(mediaItem: MediaItem?, reason: Int) {
|
|
376
|
+
if (released) return
|
|
377
|
+
delegate?.onTrackTransition(this@CrossfadeEngine, reason)
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
override fun onTracksChanged(tracks: Tracks) {
|
|
381
|
+
if (released) return
|
|
382
|
+
delegate?.onActiveItemFormatChanged(this@CrossfadeEngine)
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
override fun onPositionDiscontinuity(
|
|
386
|
+
oldPosition: Player.PositionInfo,
|
|
387
|
+
newPosition: Player.PositionInfo,
|
|
388
|
+
reason: Int,
|
|
389
|
+
) {
|
|
390
|
+
if (released) return
|
|
391
|
+
if (reason == Player.DISCONTINUITY_REASON_AUTO_TRANSITION) {
|
|
392
|
+
delegate?.onAutoTransitionDiscontinuity(this@CrossfadeEngine)
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
override fun onPlayerError(error: PlaybackException) {
|
|
397
|
+
if (released) return
|
|
398
|
+
delegate?.onError(this@CrossfadeEngine, error)
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
/**
|
|
403
|
+
* Standby leg listener — only forwards format-change signals so
|
|
404
|
+
* a cached prerolled item's audio session ID is observable
|
|
405
|
+
* before fade-start. Track-transition / playback-state events
|
|
406
|
+
* from the standby leg are filtered out: they aren't observable
|
|
407
|
+
* to the listener until the standby promotes to leading.
|
|
408
|
+
*/
|
|
409
|
+
private val standbyPlayerListener = object : Player.Listener {
|
|
410
|
+
override fun onTracksChanged(tracks: Tracks) {
|
|
411
|
+
// No delegate fire — the active item is the leading leg's,
|
|
412
|
+
// not the standby's. Standby format becomes relevant only
|
|
413
|
+
// post-promotion, at which point the leading-listener fires.
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
override fun onPlayerError(error: PlaybackException) {
|
|
417
|
+
// Standby errors during preroll abort the upcoming fade.
|
|
418
|
+
// Surface as a normal engine error so the consumer can
|
|
419
|
+
// decide whether to skip / retry; the leading leg keeps
|
|
420
|
+
// playing.
|
|
421
|
+
if (released) return
|
|
422
|
+
cancelFade()
|
|
423
|
+
delegate?.onError(this@CrossfadeEngine, error)
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
private val analyticsListenerA = makeSessionIdListener(isA = true)
|
|
428
|
+
private val analyticsListenerB = makeSessionIdListener(isA = false)
|
|
429
|
+
|
|
430
|
+
private fun makeSessionIdListener(isA: Boolean) = object : AnalyticsListener {
|
|
431
|
+
override fun onAudioSessionIdChanged(
|
|
432
|
+
eventTime: AnalyticsListener.EventTime,
|
|
433
|
+
audioSessionId: Int,
|
|
434
|
+
) {
|
|
435
|
+
if (isA) sessionIdA = audioSessionId else sessionIdB = audioSessionId
|
|
436
|
+
val ids = currentAudioSessionIds()
|
|
437
|
+
sessionIdListeners.forEach { it(ids) }
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
/**
|
|
442
|
+
* Per-leg ReplayGain listener bound to the leg identity (NOT the
|
|
443
|
+
* role). [swapRoles] migrates [leadingPlayerListener] /
|
|
444
|
+
* [standbyPlayerListener] across legs because they are role-shaped
|
|
445
|
+
* (forward-to-delegate vs filter-everything-but-error); the RG
|
|
446
|
+
* listener is leg-shaped and stays attached to its own leg through
|
|
447
|
+
* every role swap. Each leg's `onMetadata` pushes ONLY to its own
|
|
448
|
+
* processor / cache.
|
|
449
|
+
*/
|
|
450
|
+
private val replayGainListenerA = makeReplayGainListener(isA = true)
|
|
451
|
+
private val replayGainListenerB = makeReplayGainListener(isA = false)
|
|
452
|
+
|
|
453
|
+
private fun makeReplayGainListener(isA: Boolean) = object : Player.Listener {
|
|
454
|
+
override fun onMediaItemTransition(mediaItem: MediaItem?, reason: Int) {
|
|
455
|
+
if (isA) {
|
|
456
|
+
cachedReplayGainDataA = null
|
|
457
|
+
replayGainProcessorA.setLinearGain(1.0f)
|
|
458
|
+
} else {
|
|
459
|
+
cachedReplayGainDataB = null
|
|
460
|
+
replayGainProcessorB.setLinearGain(1.0f)
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
override fun onTracksChanged(tracks: Tracks) {
|
|
465
|
+
// Container-level metadata (`Format.metadata` populated by
|
|
466
|
+
// Mp3Extractor / FlacExtractor / Mp4Extractor / OggExtractor)
|
|
467
|
+
// surfaces here, not via `onMetadata` — the latter only fires
|
|
468
|
+
// for in-stream metadata samples (HLS in-stream events, ICY
|
|
469
|
+
// shoutcast) routed through `MetadataRenderer`. `onTracksChanged`
|
|
470
|
+
// fires both at initial track-selection AND on auto-advance.
|
|
471
|
+
val (format, mimeType) = pickSelectedAudioFormat(tracks) ?: return
|
|
472
|
+
val parsed = ReplayGainExtractor.extract(format.metadata, mimeType)
|
|
473
|
+
handleReplayGainParse(isA = isA, parsed = parsed)
|
|
474
|
+
// Re-signal the format change AFTER the RG parse. The same leg's
|
|
475
|
+
// leadingPlayerListener.onTracksChanged already fired
|
|
476
|
+
// onActiveItemFormatChanged (it is registered first), but with an
|
|
477
|
+
// empty RG cache — so the now-playing snapshot would carry null
|
|
478
|
+
// replayGain*Db. Re-fire so it picks up the freshly-parsed tags.
|
|
479
|
+
// Mirrors GaplessEngine.onTracksChanged, which parses THEN signals
|
|
480
|
+
// the format change within a single listener.
|
|
481
|
+
delegate?.onActiveItemFormatChanged(this@CrossfadeEngine)
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
override fun onMetadata(metadata: Metadata) {
|
|
485
|
+
// Defensive path for in-stream metadata samples (HLS in-stream
|
|
486
|
+
// RG tags, ICY shoutcast). The container-level path runs via
|
|
487
|
+
// `onTracksChanged` above.
|
|
488
|
+
val player = if (isA) playerA else playerB
|
|
489
|
+
val mimeType = currentSampleMimeType(player)
|
|
490
|
+
val parsed = ReplayGainExtractor.extract(metadata, mimeType)
|
|
491
|
+
handleReplayGainParse(isA = isA, parsed = parsed)
|
|
492
|
+
delegate?.onActiveItemFormatChanged(this@CrossfadeEngine)
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
init {
|
|
497
|
+
playerA.addListener(leadingPlayerListener)
|
|
498
|
+
playerA.addListener(replayGainListenerA)
|
|
499
|
+
playerA.addAnalyticsListener(analyticsListenerA)
|
|
500
|
+
playerB.addListener(standbyPlayerListener)
|
|
501
|
+
playerB.addListener(replayGainListenerB)
|
|
502
|
+
playerB.addAnalyticsListener(analyticsListenerB)
|
|
503
|
+
// Standby starts at volume 0 — it should not contribute to
|
|
504
|
+
// the audible mix until the fade arms.
|
|
505
|
+
playerB.volume = 0f
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
// --- PlaybackEngine: lifecycle ---
|
|
509
|
+
|
|
510
|
+
override suspend fun prepare(
|
|
511
|
+
mediaItems: List<MediaItem>,
|
|
512
|
+
startIndex: Int,
|
|
513
|
+
startPositionMs: Long,
|
|
514
|
+
) {
|
|
515
|
+
leadingPlayer.setMediaItems(mediaItems, startIndex, startPositionMs)
|
|
516
|
+
leadingPlayer.prepare()
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
override fun release() {
|
|
520
|
+
if (released) return
|
|
521
|
+
released = true
|
|
522
|
+
// Release runs as part of an engine swap or destroy; the new
|
|
523
|
+
// engine has already taken over the canonical active-track
|
|
524
|
+
// state, so a mid-fade revert emit here would surface a
|
|
525
|
+
// spurious onTrackChange to the OUTGOING leg of a fade that
|
|
526
|
+
// the consumer no longer cares about.
|
|
527
|
+
cancelFade(emitRevert = false)
|
|
528
|
+
stopArmingPoll()
|
|
529
|
+
audioManager.abandonAudioFocusRequest(focusRequest)
|
|
530
|
+
// [leadingPlayerListener] / [standbyPlayerListener] are
|
|
531
|
+
// role-shaped: [swapRoles] migrates them across legs on every
|
|
532
|
+
// fade. Read them off the current role at release-time so the
|
|
533
|
+
// remove call lands on the leg that actually owns each listener
|
|
534
|
+
// — `Player.removeListener` is a no-op against an unattached
|
|
535
|
+
// listener so a stale call wouldn't leak, but the role-shaped
|
|
536
|
+
// form is what the listeners actually wire to.
|
|
537
|
+
leadingPlayer.removeListener(leadingPlayerListener)
|
|
538
|
+
standbyPlayer.removeListener(standbyPlayerListener)
|
|
539
|
+
// RG listeners + analytics listeners are leg-shaped — they stay
|
|
540
|
+
// attached to their construction-time leg through every swap.
|
|
541
|
+
playerA.removeListener(replayGainListenerA)
|
|
542
|
+
playerA.removeAnalyticsListener(analyticsListenerA)
|
|
543
|
+
playerB.removeListener(replayGainListenerB)
|
|
544
|
+
playerB.removeAnalyticsListener(analyticsListenerB)
|
|
545
|
+
sessionIdListeners.clear()
|
|
546
|
+
bufferSinks.clear()
|
|
547
|
+
delegate = null
|
|
548
|
+
engineScope.cancel()
|
|
549
|
+
playerA.release()
|
|
550
|
+
playerB.release()
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
// --- PlaybackEngine: queue mutation ---
|
|
554
|
+
//
|
|
555
|
+
// Single-item-per-leg model. The engine's queue is the lib-side
|
|
556
|
+
// canonical model owned by `TrackPlayer`; the engine receives
|
|
557
|
+
// pre-built `MediaItem` snapshots and either installs them on
|
|
558
|
+
// the leading leg directly (gapless-equivalent path while no
|
|
559
|
+
// fade is in flight) or queues them for the standby preroll.
|
|
560
|
+
|
|
561
|
+
override fun setMediaItems(
|
|
562
|
+
items: List<MediaItem>,
|
|
563
|
+
startIndex: Int,
|
|
564
|
+
startPositionMs: Long,
|
|
565
|
+
) {
|
|
566
|
+
// emitRevert=false: the caller (setQueue / engine swap) sets the
|
|
567
|
+
// canonical active index itself, so a mid-fade revert here would
|
|
568
|
+
// clobber it with the pre-mutation leading index + emit a spurious
|
|
569
|
+
// crossfade-cancelled track-change.
|
|
570
|
+
cancelFade(emitRevert = false)
|
|
571
|
+
// Reset arming state — the previous queue's preroll-armed
|
|
572
|
+
// standby is stale once the queue is replaced. Without this,
|
|
573
|
+
// [standbyArmed] could remain true against an empty / unrelated
|
|
574
|
+
// queue and [maybeArmCrossfade] would early-return forever.
|
|
575
|
+
standbyArmed = false
|
|
576
|
+
leadingPlayer.setMediaItems(items, startIndex, startPositionMs)
|
|
577
|
+
leadingPlayer.prepare()
|
|
578
|
+
standbyPlayer.stop()
|
|
579
|
+
standbyPlayer.clearMediaItems()
|
|
580
|
+
standbyPlayer.volume = 0f
|
|
581
|
+
// Empty queue — no boundaries to arm. Stop the poll until
|
|
582
|
+
// play() restarts it.
|
|
583
|
+
if (items.isEmpty()) {
|
|
584
|
+
stopArmingPoll()
|
|
585
|
+
}
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
override fun addMediaItems(items: List<MediaItem>, insertBefore: Int) {
|
|
589
|
+
// Queue mutation invalidates the standby leg's pre-loaded
|
|
590
|
+
// future. Cancel any in-flight fade before mutating the
|
|
591
|
+
// leading queue so the standby's stale mediaItems don't race
|
|
592
|
+
// a swapRoles against the post-mutation index space.
|
|
593
|
+
// emitRevert=false: the TrackPlayer mutation body recomputes the
|
|
594
|
+
// canonical index, so a mid-fade revert here would clobber it.
|
|
595
|
+
cancelFade(emitRevert = false)
|
|
596
|
+
leadingPlayer.addMediaItems(insertBefore, items)
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
override fun removeMediaItems(indices: IntArray) {
|
|
600
|
+
cancelFade(emitRevert = false)
|
|
601
|
+
val sorted = indices.toMutableList().apply { sortDescending() }
|
|
602
|
+
for (i in sorted) leadingPlayer.removeMediaItem(i)
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
override fun moveMediaItem(fromIndex: Int, toIndex: Int) {
|
|
606
|
+
cancelFade(emitRevert = false)
|
|
607
|
+
leadingPlayer.moveMediaItem(fromIndex, toIndex)
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
// --- PlaybackEngine: transport ---
|
|
611
|
+
|
|
612
|
+
override fun play() {
|
|
613
|
+
// Request audio focus so the focusListener gets duck / loss
|
|
614
|
+
// callbacks for as long as we're playing, but do NOT gate the
|
|
615
|
+
// actual play on the grant result. AudioManager can briefly
|
|
616
|
+
// return DENIED right after a sibling engine release abandons
|
|
617
|
+
// focus (e.g. during a mid-play setPlaybackMode → swap →
|
|
618
|
+
// play sequence) — gating would silently leave the user
|
|
619
|
+
// paused. Media3's AudioFocusManager (used by GaplessEngine)
|
|
620
|
+
// takes the same approach: focus is best-effort, playback
|
|
621
|
+
// proceeds.
|
|
622
|
+
audioManager.requestAudioFocus(focusRequest)
|
|
623
|
+
leadingPlayer.play()
|
|
624
|
+
startArmingPoll()
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
override fun pause() {
|
|
628
|
+
cancelFade()
|
|
629
|
+
stopArmingPoll()
|
|
630
|
+
leadingPlayer.pause()
|
|
631
|
+
standbyPlayer.pause()
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
override fun stop() {
|
|
635
|
+
cancelFade()
|
|
636
|
+
stopArmingPoll()
|
|
637
|
+
leadingPlayer.stop()
|
|
638
|
+
standbyPlayer.stop()
|
|
639
|
+
audioManager.abandonAudioFocusRequest(focusRequest)
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
override fun seekTo(index: Int, positionMs: Long) {
|
|
643
|
+
// emitRevert=false: every JS-facing seek + skip routes through here
|
|
644
|
+
// with an explicit target index already written to currentTrackIndex
|
|
645
|
+
// by the caller, so a mid-fade revert would clobber it back to the
|
|
646
|
+
// outgoing leg + emit a spurious crossfade-cancelled track-change.
|
|
647
|
+
cancelFade(emitRevert = false)
|
|
648
|
+
leadingPlayer.seekTo(index, positionMs)
|
|
649
|
+
standbyArmed = false
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
override fun skipToNext() {
|
|
653
|
+
cancelFade()
|
|
654
|
+
leadingPlayer.seekToNextMediaItem()
|
|
655
|
+
standbyArmed = false
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
override fun skipToPrevious() {
|
|
659
|
+
cancelFade()
|
|
660
|
+
leadingPlayer.seekToPreviousMediaItem()
|
|
661
|
+
standbyArmed = false
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
// --- PlaybackEngine: config ---
|
|
665
|
+
|
|
666
|
+
override fun setVolume(volume: Float) {
|
|
667
|
+
userVolume = volume
|
|
668
|
+
leadingPlayer.volume = volume
|
|
669
|
+
}
|
|
670
|
+
|
|
671
|
+
override fun setPlaybackSpeed(speed: Float) {
|
|
672
|
+
// Both legs carry the active pitch-correction mode's pitch; the
|
|
673
|
+
// standby prerolls at the same params so the fade-in is already at
|
|
674
|
+
// the right pitch.
|
|
675
|
+
val params = PitchCorrection.playbackParameters(pitchCorrectionMode, speed)
|
|
676
|
+
leadingPlayer.playbackParameters = params
|
|
677
|
+
standbyPlayer.playbackParameters = params
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
override fun setRepeatMode(mode: RepeatMode) {
|
|
681
|
+
val media3Mode = when (mode) {
|
|
682
|
+
RepeatMode.OFF -> Player.REPEAT_MODE_OFF
|
|
683
|
+
RepeatMode.TRACK -> Player.REPEAT_MODE_ONE
|
|
684
|
+
RepeatMode.QUEUE -> Player.REPEAT_MODE_ALL
|
|
685
|
+
}
|
|
686
|
+
leadingPlayer.repeatMode = media3Mode
|
|
687
|
+
standbyPlayer.repeatMode = media3Mode
|
|
688
|
+
}
|
|
689
|
+
|
|
690
|
+
override fun setPitchCorrectionMode(mode: PitchCorrectionMode) {
|
|
691
|
+
pitchCorrectionMode = mode
|
|
692
|
+
// Route both legs' chains to the matching stretcher BEFORE re-applying
|
|
693
|
+
// params, so applyPlaybackParameters sends the speed to the active one.
|
|
694
|
+
pitchChainA.setMode(mode)
|
|
695
|
+
pitchChainB.setMode(mode)
|
|
696
|
+
// Recompute against the current speed (both legs share it) so the
|
|
697
|
+
// pitch field flips immediately.
|
|
698
|
+
val speed = leadingPlayer.playbackParameters.speed
|
|
699
|
+
val params = PitchCorrection.playbackParameters(mode, speed)
|
|
700
|
+
leadingPlayer.playbackParameters = params
|
|
701
|
+
standbyPlayer.playbackParameters = params
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
override fun setWakeMode(mode: Int) {
|
|
705
|
+
// Both legs share the wake-mode policy: the standby leg also holds a
|
|
706
|
+
// wake/network lock while prerolling + fading in, so it must match
|
|
707
|
+
// the leading leg. Applying only to the leading leg would leave the
|
|
708
|
+
// post-swap leg on a stale (network) wake lock for an all-local queue.
|
|
709
|
+
leadingPlayer.setWakeMode(mode)
|
|
710
|
+
standbyPlayer.setWakeMode(mode)
|
|
711
|
+
}
|
|
712
|
+
|
|
713
|
+
// --- PlaybackEngine: state ---
|
|
714
|
+
|
|
715
|
+
/**
|
|
716
|
+
* During a crossfade the conceptual "active" track flips to the
|
|
717
|
+
* standby leg the moment the fade begins (matches the JS-facing
|
|
718
|
+
* `onTrackChange` + lock-screen / Now Playing flip the delegate
|
|
719
|
+
* performs in response to `onCrossfadeBegin`). The protocol
|
|
720
|
+
* accessors below honour this: when [fadeJob] is active they
|
|
721
|
+
* read from [standbyPlayer] (the incoming leg) so `useProgress`,
|
|
722
|
+
* seek clamps, preserve-current, and index lookups all see the
|
|
723
|
+
* incoming track. Engine-internal code reaches [leadingPlayer]
|
|
724
|
+
* directly. [isPlaying] continues to read [leadingPlayer] —
|
|
725
|
+
* during a fade the leading leg's `isPlaying` is the audible
|
|
726
|
+
* truth (the standby is also playing but at a fading-in volume).
|
|
727
|
+
*/
|
|
728
|
+
private val activePlayer: ExoPlayer
|
|
729
|
+
get() = if (fadeJob?.isActive == true) standbyPlayer else leadingPlayer
|
|
730
|
+
|
|
731
|
+
override val currentMediaItemIndex: Int
|
|
732
|
+
get() {
|
|
733
|
+
val count = activePlayer.mediaItemCount
|
|
734
|
+
if (count == 0) return -1
|
|
735
|
+
return activePlayer.currentMediaItemIndex
|
|
736
|
+
}
|
|
737
|
+
|
|
738
|
+
override val currentPositionMs: Long
|
|
739
|
+
get() = activePlayer.currentPosition
|
|
740
|
+
|
|
741
|
+
override val currentDurationMs: Long
|
|
742
|
+
get() {
|
|
743
|
+
val d = activePlayer.duration
|
|
744
|
+
return if (d == C.TIME_UNSET) 0L else d
|
|
745
|
+
}
|
|
746
|
+
|
|
747
|
+
override val bufferedPositionMs: Long
|
|
748
|
+
get() = activePlayer.bufferedPosition
|
|
749
|
+
|
|
750
|
+
override val isPlaying: Boolean
|
|
751
|
+
get() = leadingPlayer.isPlaying
|
|
752
|
+
|
|
753
|
+
override val currentMediaItem: MediaItem?
|
|
754
|
+
get() = activePlayer.currentMediaItem
|
|
755
|
+
|
|
756
|
+
override val allMediaItems: List<MediaItem>
|
|
757
|
+
get() {
|
|
758
|
+
val count = leadingPlayer.mediaItemCount
|
|
759
|
+
if (count == 0) return emptyList()
|
|
760
|
+
val out = ArrayList<MediaItem>(count)
|
|
761
|
+
for (i in 0 until count) out.add(leadingPlayer.getMediaItemAt(i))
|
|
762
|
+
return out
|
|
763
|
+
}
|
|
764
|
+
|
|
765
|
+
// --- PlaybackEngine: effect chain ---
|
|
766
|
+
|
|
767
|
+
override fun currentAudioSessionIds(): IntArray {
|
|
768
|
+
val fadeActive = fadeJob?.isActive == true
|
|
769
|
+
val leading = if (currentRoleIsA) sessionIdA else sessionIdB
|
|
770
|
+
val standby = if (currentRoleIsA) sessionIdB else sessionIdA
|
|
771
|
+
return when {
|
|
772
|
+
fadeActive && leading != C.AUDIO_SESSION_ID_UNSET && standby != C.AUDIO_SESSION_ID_UNSET ->
|
|
773
|
+
intArrayOf(leading, standby)
|
|
774
|
+
leading != C.AUDIO_SESSION_ID_UNSET -> intArrayOf(leading)
|
|
775
|
+
else -> IntArray(0)
|
|
776
|
+
}
|
|
777
|
+
}
|
|
778
|
+
|
|
779
|
+
override fun addAudioSessionIdListener(listener: (IntArray) -> Unit): () -> Unit {
|
|
780
|
+
sessionIdListeners.add(listener)
|
|
781
|
+
listener(currentAudioSessionIds())
|
|
782
|
+
return { sessionIdListeners.remove(listener) }
|
|
783
|
+
}
|
|
784
|
+
|
|
785
|
+
override fun attachAudioBufferSink(sink: TeeAudioProcessor.AudioBufferSink) {
|
|
786
|
+
// No Tee to fan out to when viz was disabled at engine
|
|
787
|
+
// construction (PlayerConfig.visualizationEnabled = false).
|
|
788
|
+
val teeA = visualizerTeeBuilderA
|
|
789
|
+
val teeB = visualizerTeeBuilderB
|
|
790
|
+
if (teeA == null && teeB == null) return
|
|
791
|
+
// Mirrors `GaplessEngine`: accumulate the sink for the
|
|
792
|
+
// visualizer to consume. Both legs share the same accumulator;
|
|
793
|
+
// each leg's mux fans out independently when its AudioSink
|
|
794
|
+
// produces a buffer.
|
|
795
|
+
bufferSinks.addIfAbsent(sink)
|
|
796
|
+
// Replay the most recent flush params from every produced mux
|
|
797
|
+
// onto the new sink so it has its encoding context the moment
|
|
798
|
+
// it attaches, regardless of which leg has already flushed.
|
|
799
|
+
teeA?.primeNewSink(sink)
|
|
800
|
+
teeB?.primeNewSink(sink)
|
|
801
|
+
}
|
|
802
|
+
|
|
803
|
+
override fun detachAudioBufferSink(sink: TeeAudioProcessor.AudioBufferSink) {
|
|
804
|
+
bufferSinks.remove(sink)
|
|
805
|
+
}
|
|
806
|
+
|
|
807
|
+
// --- ReplayGain — engine-internal accessors ---
|
|
808
|
+
|
|
809
|
+
/**
|
|
810
|
+
* Recompute BOTH legs' processor gain from each leg's cached data
|
|
811
|
+
* + the current [ReplayGainConfig.getMode]. Called from
|
|
812
|
+
* [TrackPlayer.setReplayGainMode] when the user flips mode
|
|
813
|
+
* mid-track. Per-leg recompute on listener fires uses
|
|
814
|
+
* [recomputeLegFromConfig] internally.
|
|
815
|
+
*
|
|
816
|
+
* Main thread only — Media3 dispatches `Player.Listener`
|
|
817
|
+
* callbacks on the application looper and
|
|
818
|
+
* [TrackPlayer.setReplayGainMode] hops to main before calling.
|
|
819
|
+
*/
|
|
820
|
+
@MainThread
|
|
821
|
+
internal fun recomputeReplayGainFromConfig() {
|
|
822
|
+
recomputeLegFromConfig(isA = true)
|
|
823
|
+
recomputeLegFromConfig(isA = false)
|
|
824
|
+
}
|
|
825
|
+
|
|
826
|
+
@MainThread
|
|
827
|
+
private fun recomputeLegFromConfig(isA: Boolean) {
|
|
828
|
+
val mode = ReplayGainConfig.getMode()
|
|
829
|
+
if (isA) {
|
|
830
|
+
replayGainProcessorA.setLinearGain(computeLinearGain(cachedReplayGainDataA, mode))
|
|
831
|
+
} else {
|
|
832
|
+
replayGainProcessorB.setLinearGain(computeLinearGain(cachedReplayGainDataB, mode))
|
|
833
|
+
}
|
|
834
|
+
}
|
|
835
|
+
|
|
836
|
+
/**
|
|
837
|
+
* Cache + recompute entry point shared by both per-leg listener
|
|
838
|
+
* paths. Called with the `ReplayGainData` produced by
|
|
839
|
+
* [ReplayGainExtractor] from either `Format.metadata`
|
|
840
|
+
* (container-level via `onTracksChanged`) or in-stream `Metadata`
|
|
841
|
+
* samples (via `onMetadata`). A `null` parse leaves the cache
|
|
842
|
+
* untouched — `onTracksChanged` fires for unrelated track-set
|
|
843
|
+
* changes too, and `onMetadata` fires for non-RG frames (TIT2
|
|
844
|
+
* title events, ICY now-playing, etc.).
|
|
845
|
+
*
|
|
846
|
+
* Per-leg shape: the listener routes to ONLY its own leg's cache
|
|
847
|
+
* + processor (never the other leg's), so a tag arrival on the
|
|
848
|
+
* standby leg cannot stomp the leading leg's gain mid-fade.
|
|
849
|
+
*
|
|
850
|
+
* Main thread only — Media3 dispatches `Player.Listener`
|
|
851
|
+
* callbacks on the application looper.
|
|
852
|
+
*/
|
|
853
|
+
@MainThread
|
|
854
|
+
internal fun handleReplayGainParse(isA: Boolean, parsed: ReplayGainData?) {
|
|
855
|
+
if (parsed != null) {
|
|
856
|
+
if (isA) cachedReplayGainDataA = parsed else cachedReplayGainDataB = parsed
|
|
857
|
+
}
|
|
858
|
+
recomputeLegFromConfig(isA)
|
|
859
|
+
}
|
|
860
|
+
|
|
861
|
+
/**
|
|
862
|
+
* Snapshot the LEADING leg's cached [ReplayGainData] for the
|
|
863
|
+
* [NowPlayingFormatExtractor] surface. The active item from JS's
|
|
864
|
+
* point of view is always the leading leg's current item except
|
|
865
|
+
* during the fade window — at which point [activePlayer] flips to
|
|
866
|
+
* standby and this read should follow. Returns `null` when no
|
|
867
|
+
* usable RG tags have been parsed yet for the active leg's track.
|
|
868
|
+
*/
|
|
869
|
+
override fun currentReplayGainData(): ReplayGainData? =
|
|
870
|
+
if (activePlayer === playerA) cachedReplayGainDataA else cachedReplayGainDataB
|
|
871
|
+
|
|
872
|
+
/**
|
|
873
|
+
* `true` when the active leg's cached data carries a tag for the
|
|
874
|
+
* current [ReplayGainConfig.getMode]. Used by the NowPlayingFormat
|
|
875
|
+
* path to populate `replayGainActive`.
|
|
876
|
+
*/
|
|
877
|
+
override fun currentReplayGainActive(): Boolean {
|
|
878
|
+
val data = currentReplayGainData() ?: return false
|
|
879
|
+
val (gainDb, _) = data.selectGain(ReplayGainConfig.getMode())
|
|
880
|
+
return gainDb != null
|
|
881
|
+
}
|
|
882
|
+
|
|
883
|
+
/**
|
|
884
|
+
* Pick the first selected audio [androidx.media3.common.Format]
|
|
885
|
+
* out of [tracks]. Used by the per-leg
|
|
886
|
+
* [Player.Listener.onTracksChanged] to route `Format.metadata` +
|
|
887
|
+
* `Format.sampleMimeType` into [ReplayGainExtractor.extract]
|
|
888
|
+
* directly from the listener's live payload. Falls back to the
|
|
889
|
+
* first audio track in any group when nothing is currently
|
|
890
|
+
* selected. Returns `null` when no audio track exists.
|
|
891
|
+
*/
|
|
892
|
+
private fun pickSelectedAudioFormat(
|
|
893
|
+
tracks: Tracks
|
|
894
|
+
): Pair<androidx.media3.common.Format, String?>? {
|
|
895
|
+
var firstAudioFormat: androidx.media3.common.Format? = null
|
|
896
|
+
for (group in tracks.groups) {
|
|
897
|
+
if (group.type != C.TRACK_TYPE_AUDIO) continue
|
|
898
|
+
val mediaGroup = group.mediaTrackGroup
|
|
899
|
+
for (i in 0 until mediaGroup.length) {
|
|
900
|
+
val format = group.getTrackFormat(i)
|
|
901
|
+
if (firstAudioFormat == null) firstAudioFormat = format
|
|
902
|
+
if (group.isTrackSelected(i)) {
|
|
903
|
+
return format to format.sampleMimeType
|
|
904
|
+
}
|
|
905
|
+
}
|
|
906
|
+
}
|
|
907
|
+
val fallback = firstAudioFormat ?: return null
|
|
908
|
+
return fallback to fallback.sampleMimeType
|
|
909
|
+
}
|
|
910
|
+
|
|
911
|
+
/**
|
|
912
|
+
* Best-effort sample-MIME-type read from [player]'s currently-
|
|
913
|
+
* selected audio track. Used by the in-stream
|
|
914
|
+
* [Player.Listener.onMetadata] path (HLS / ICY) to gate the
|
|
915
|
+
* Opus-container guard in [ReplayGainExtractor.extract]. The
|
|
916
|
+
* container-level [Player.Listener.onTracksChanged] path reads
|
|
917
|
+
* the sample MIME directly from the listener's [Tracks] payload
|
|
918
|
+
* via [pickSelectedAudioFormat].
|
|
919
|
+
*/
|
|
920
|
+
private fun currentSampleMimeType(player: ExoPlayer): String? {
|
|
921
|
+
for (group in player.currentTracks.groups) {
|
|
922
|
+
if (group.type != C.TRACK_TYPE_AUDIO) continue
|
|
923
|
+
val mediaGroup = group.mediaTrackGroup
|
|
924
|
+
for (i in 0 until mediaGroup.length) {
|
|
925
|
+
val format = group.getTrackFormat(i)
|
|
926
|
+
format.sampleMimeType?.let { return it }
|
|
927
|
+
}
|
|
928
|
+
}
|
|
929
|
+
return null
|
|
930
|
+
}
|
|
931
|
+
|
|
932
|
+
// --- PlaybackEngine: handoff ---
|
|
933
|
+
|
|
934
|
+
override suspend fun handoff(to: PlaybackEngine, atTrackBoundary: Boolean) {
|
|
935
|
+
// Engine handoff (gapless ↔ crossfade swap at TrackPlayer
|
|
936
|
+
// level) is wired in the swapEngine path on PlaybackService
|
|
937
|
+
// alongside this engine's own boundary detection. The
|
|
938
|
+
// crossfade-to-other-engine handoff lives at PlaybackService
|
|
939
|
+
// because TrackPlayer holds the canonical queue model that
|
|
940
|
+
// the receiving engine needs.
|
|
941
|
+
throw NotImplementedError(
|
|
942
|
+
"PlaybackEngine.handoff is driven by PlaybackService.swapEngine; do not call directly"
|
|
943
|
+
)
|
|
944
|
+
}
|
|
945
|
+
|
|
946
|
+
// --- Fade orchestration ---
|
|
947
|
+
|
|
948
|
+
/**
|
|
949
|
+
* Cancel any in-flight fade and snap the leading leg back to
|
|
950
|
+
* full volume + drop the standby leg's item. Called from manual
|
|
951
|
+
* skip / pause / seek paths and the route-degradation callback.
|
|
952
|
+
*/
|
|
953
|
+
internal fun cancelFade(emitRevert: Boolean = true) {
|
|
954
|
+
val job = fadeJob
|
|
955
|
+
val wasMidFade = job?.isActive == true
|
|
956
|
+
val leadingIdx = leadingPlayer.currentMediaItemIndex
|
|
957
|
+
fadeJob = null
|
|
958
|
+
job?.cancel()
|
|
959
|
+
leadingPlayer.volume = userVolume
|
|
960
|
+
standbyPlayer.volume = 0f
|
|
961
|
+
standbyPlayer.stop()
|
|
962
|
+
standbyPlayer.clearMediaItems()
|
|
963
|
+
if (wasMidFade && emitRevert) {
|
|
964
|
+
// Mid-fade cancel: fade-start fired `onCrossfadeBegin` →
|
|
965
|
+
// delegate moved JS-facing state to the incoming leg. Revert
|
|
966
|
+
// to the leading leg now: re-bind the wrapper back to
|
|
967
|
+
// leadingPlayer so MediaSession metadata reverts; fire the
|
|
968
|
+
// delegate so JS gets a `crossfade-cancelled` track-change
|
|
969
|
+
// for the leading track. `emitRevert: false` is for callers
|
|
970
|
+
// that will immediately set a new active track themselves
|
|
971
|
+
// (e.g. engine release during a setPlaybackMode swap — the
|
|
972
|
+
// new engine has already taken over the canonical active-
|
|
973
|
+
// track state, so an intermediate revert here would surface
|
|
974
|
+
// a spurious onTrackChange to the OUTGOING leg of a fade
|
|
975
|
+
// the consumer no longer cares about).
|
|
976
|
+
onCrossfadeCancel?.invoke()
|
|
977
|
+
delegate?.onCrossfadeCancel(this, leadingIdx)
|
|
978
|
+
}
|
|
979
|
+
}
|
|
980
|
+
|
|
981
|
+
/**
|
|
982
|
+
* Cancel the in-flight fade and immediately swap the standby
|
|
983
|
+
* leg to leading at the user's chosen volume. Used when the
|
|
984
|
+
* leading leg ends naturally before the fade completes
|
|
985
|
+
* (truncated track, wrong duration metadata).
|
|
986
|
+
*/
|
|
987
|
+
internal fun cancelFadeAndPromoteStandby() {
|
|
988
|
+
val job = fadeJob
|
|
989
|
+
fadeJob = null
|
|
990
|
+
job?.cancel()
|
|
991
|
+
leadingPlayer.volume = 0f
|
|
992
|
+
standbyPlayer.volume = userVolume
|
|
993
|
+
swapRoles()
|
|
994
|
+
}
|
|
995
|
+
|
|
996
|
+
/**
|
|
997
|
+
* Compute the effective fade duration for a transition,
|
|
998
|
+
* clamping to half of each leg's playable duration so the
|
|
999
|
+
* ramp never overflows track boundaries.
|
|
1000
|
+
*/
|
|
1001
|
+
internal fun effectiveCrossfadeMs(leadingDurationMs: Long, standbyDurationMs: Long): Long {
|
|
1002
|
+
val configured = crossfadeDurationMs
|
|
1003
|
+
val leadingHalf = if (leadingDurationMs > 0) leadingDurationMs / 2 else configured
|
|
1004
|
+
val standbyHalf = if (standbyDurationMs > 0) standbyDurationMs / 2 else configured
|
|
1005
|
+
return min(min(configured, leadingHalf), standbyHalf).coerceAtLeast(0L)
|
|
1006
|
+
}
|
|
1007
|
+
|
|
1008
|
+
/**
|
|
1009
|
+
* Equal-power gain pair for the given fade progress
|
|
1010
|
+
* `t ∈ [0, 1]`. `(outgoing, incoming)` summed power stays at
|
|
1011
|
+
* 1.0 across the ramp, avoiding the perceptible -3dB midpoint
|
|
1012
|
+
* dip that linear ramps produce on uncorrelated content.
|
|
1013
|
+
*/
|
|
1014
|
+
internal fun equalPowerGains(t: Float): Pair<Float, Float> {
|
|
1015
|
+
val clamped = t.coerceIn(0f, 1f)
|
|
1016
|
+
val angle = clamped * (PI / 2.0).toFloat()
|
|
1017
|
+
return cos(angle) to sin(angle)
|
|
1018
|
+
}
|
|
1019
|
+
|
|
1020
|
+
/**
|
|
1021
|
+
* Callback fired on every role swap so [CrossfadeForwardingPlayer]
|
|
1022
|
+
* (when bound by [PlaybackService.swapEngine]) can invalidate
|
|
1023
|
+
* its cached state + re-target `MediaSession.setPlayer` to the
|
|
1024
|
+
* new leading leg. Set by `PlaybackService` post-construction;
|
|
1025
|
+
* `null` when no wrapper is bound (e.g. headless tests).
|
|
1026
|
+
*/
|
|
1027
|
+
internal var onRoleSwap: (() -> Unit)? = null
|
|
1028
|
+
|
|
1029
|
+
/**
|
|
1030
|
+
* Callback fired the instant a crossfade begins ramping. Lets
|
|
1031
|
+
* [CrossfadeForwardingPlayer] re-bind `MediaSession` to the
|
|
1032
|
+
* standby (incoming) leg so the lock-screen / Auto / BT
|
|
1033
|
+
* scrubber + metadata flip to the incoming track from 00:00 the
|
|
1034
|
+
* moment it begins audibly playing. The standby's `ExoPlayer`
|
|
1035
|
+
* instance becomes the new leading on [swapRoles] at fade-end,
|
|
1036
|
+
* so the [onRoleSwap] re-bind a moment later is a no-op (already
|
|
1037
|
+
* pointing at the same instance).
|
|
1038
|
+
*/
|
|
1039
|
+
internal var onCrossfadeBegin: (() -> Unit)? = null
|
|
1040
|
+
|
|
1041
|
+
/**
|
|
1042
|
+
* Callback fired when an in-flight crossfade is cancelled
|
|
1043
|
+
* before [swapRoles] runs. Lets [CrossfadeForwardingPlayer]
|
|
1044
|
+
* re-bind `MediaSession` back to the leading leg so the lock-
|
|
1045
|
+
* screen / Auto / BT scrubber + metadata revert from the
|
|
1046
|
+
* incoming-track view that fade-start had pinned. Pairs with
|
|
1047
|
+
* [onCrossfadeBegin]: only fires when a `Begin` was fired
|
|
1048
|
+
* earlier and the fade did not complete.
|
|
1049
|
+
*/
|
|
1050
|
+
internal var onCrossfadeCancel: (() -> Unit)? = null
|
|
1051
|
+
|
|
1052
|
+
private fun swapRoles() {
|
|
1053
|
+
// Migrate listeners to follow the role swap so the leading-leg
|
|
1054
|
+
// listener (which forwards onTrackTransition / onPlaybackStateChanged
|
|
1055
|
+
// / onIsPlayingChanged / onPlayerError to the engine delegate)
|
|
1056
|
+
// fires from the player that's now leading. Without this the
|
|
1057
|
+
// listeners stay glued to playerA / playerB and post-swap events
|
|
1058
|
+
// route to the wrong listener, swallowing track transitions on
|
|
1059
|
+
// the new leading leg.
|
|
1060
|
+
//
|
|
1061
|
+
// Threading: this method runs from the runFade coroutine on
|
|
1062
|
+
// Dispatchers.Main, which is also the application looper for
|
|
1063
|
+
// both ExoPlayer instances. addListener / removeListener +
|
|
1064
|
+
// CrossfadeForwardingPlayer.setPlayer (via onRoleSwap) all
|
|
1065
|
+
// require the application looper.
|
|
1066
|
+
val oldLeading = leadingPlayer
|
|
1067
|
+
val oldStandby = standbyPlayer
|
|
1068
|
+
oldLeading.removeListener(leadingPlayerListener)
|
|
1069
|
+
oldStandby.removeListener(standbyPlayerListener)
|
|
1070
|
+
currentRoleIsA = !currentRoleIsA
|
|
1071
|
+
leadingPlayer.addListener(leadingPlayerListener)
|
|
1072
|
+
standbyPlayer.addListener(standbyPlayerListener)
|
|
1073
|
+
val newStandby = standbyPlayer
|
|
1074
|
+
newStandby.stop()
|
|
1075
|
+
newStandby.clearMediaItems()
|
|
1076
|
+
newStandby.volume = 0f
|
|
1077
|
+
standbyArmed = false
|
|
1078
|
+
// Notify the MediaSession-binding wrapper so the lock-screen
|
|
1079
|
+
// scrubber retargets to the now-leading leg.
|
|
1080
|
+
onRoleSwap?.invoke()
|
|
1081
|
+
}
|
|
1082
|
+
|
|
1083
|
+
/**
|
|
1084
|
+
* Boundary-arming position-poll. Runs every
|
|
1085
|
+
* [POSITION_POLL_INTERVAL_MS] while playback is active. When the
|
|
1086
|
+
* leading leg's `currentPosition` approaches
|
|
1087
|
+
* `duration - effectiveCrossfadeMs - PREROLL_LEAD_MS`, prerolls
|
|
1088
|
+
* standby with the next item from the leading leg's queue and
|
|
1089
|
+
* fires [runFade].
|
|
1090
|
+
*
|
|
1091
|
+
* Cancellation: `pause` / `stop` / `seekTo` / `skipToNext` /
|
|
1092
|
+
* `skipToPrevious` / `release` all stop the poll. `play` restarts
|
|
1093
|
+
* it. The poll itself cancels when the engine's coroutine scope
|
|
1094
|
+
* is cancelled (in `release`).
|
|
1095
|
+
*/
|
|
1096
|
+
private fun startArmingPoll() {
|
|
1097
|
+
if (armingJob?.isActive == true) return
|
|
1098
|
+
armingJob = engineScope.launch {
|
|
1099
|
+
while (isActive) {
|
|
1100
|
+
delay(POSITION_POLL_INTERVAL_MS)
|
|
1101
|
+
try {
|
|
1102
|
+
maybeArmCrossfade()
|
|
1103
|
+
} catch (_: IllegalStateException) {
|
|
1104
|
+
// ExoPlayer state read off main looper or post-release;
|
|
1105
|
+
// skip this tick. The next tick will re-evaluate.
|
|
1106
|
+
}
|
|
1107
|
+
}
|
|
1108
|
+
}
|
|
1109
|
+
}
|
|
1110
|
+
|
|
1111
|
+
private fun stopArmingPoll() {
|
|
1112
|
+
val job = armingJob
|
|
1113
|
+
job?.cancel()
|
|
1114
|
+
armingJob = null
|
|
1115
|
+
}
|
|
1116
|
+
|
|
1117
|
+
/**
|
|
1118
|
+
* Decide whether to arm the standby preroll + run the fade based
|
|
1119
|
+
* on the leading leg's current position relative to its
|
|
1120
|
+
* track-end. Idempotent — repeat fires within the same boundary
|
|
1121
|
+
* window are no-ops because [standbyArmed] tracks per-track.
|
|
1122
|
+
*/
|
|
1123
|
+
private fun maybeArmCrossfade() {
|
|
1124
|
+
if (standbyArmed) return
|
|
1125
|
+
if (fadeJob?.isActive == true) return
|
|
1126
|
+
if (!leadingPlayer.isPlaying) return
|
|
1127
|
+
if (crossfadeDurationMs <= 0L) return
|
|
1128
|
+
// Repeat-one loops the current item natively (REPEAT_MODE_ONE on the
|
|
1129
|
+
// leading leg). Never arm a look-ahead fade, which would advance past
|
|
1130
|
+
// the track the user asked to repeat — matches the gapless engine,
|
|
1131
|
+
// where the same native repeat loops the item in place.
|
|
1132
|
+
if (leadingPlayer.repeatMode == Player.REPEAT_MODE_ONE) return
|
|
1133
|
+
val currentIdx = leadingPlayer.currentMediaItemIndex
|
|
1134
|
+
val itemCount = leadingPlayer.mediaItemCount
|
|
1135
|
+
if (currentIdx < 0 || currentIdx >= itemCount - 1) return // last track or empty
|
|
1136
|
+
val duration = leadingPlayer.duration
|
|
1137
|
+
if (duration == C.TIME_UNSET || duration <= 0L) return
|
|
1138
|
+
val position = leadingPlayer.currentPosition
|
|
1139
|
+
// Best-effort lookahead: leading's queue has the next item but
|
|
1140
|
+
// its duration isn't known until prepared. Passing 0 falls
|
|
1141
|
+
// through to the configured value via [effectiveCrossfadeMs]
|
|
1142
|
+
// with the leading-half clamp only.
|
|
1143
|
+
val standbyDurationMs = 0L
|
|
1144
|
+
val effective = effectiveCrossfadeMs(duration, standbyDurationMs)
|
|
1145
|
+
val armThreshold = duration - effective - PREROLL_LEAD_MS
|
|
1146
|
+
if (position < armThreshold) return
|
|
1147
|
+
standbyArmed = true
|
|
1148
|
+
armCrossfade(effective)
|
|
1149
|
+
}
|
|
1150
|
+
|
|
1151
|
+
/**
|
|
1152
|
+
* Preroll the standby leg with the leading leg's tail (next item
|
|
1153
|
+
* onward) so Media3 auto-advance can take over after the fade
|
|
1154
|
+
* completes + role swap, then start the fade ramp.
|
|
1155
|
+
*/
|
|
1156
|
+
private fun armCrossfade(durationMs: Long) {
|
|
1157
|
+
val nextIdx = leadingPlayer.currentMediaItemIndex + 1
|
|
1158
|
+
val itemCount = leadingPlayer.mediaItemCount
|
|
1159
|
+
if (nextIdx >= itemCount) {
|
|
1160
|
+
standbyArmed = false
|
|
1161
|
+
return
|
|
1162
|
+
}
|
|
1163
|
+
// Load the FULL queue onto standby (not just the tail) so the
|
|
1164
|
+
// standby's `currentMediaItemIndex` is in the same global index
|
|
1165
|
+
// space as the consumer-supplied queue. After [swapRoles] flips
|
|
1166
|
+
// standby into leading, JS-facing reads of `currentMediaItemIndex`
|
|
1167
|
+
// remain meaningful and the lock-screen / Android Auto scrubber
|
|
1168
|
+
// sees the right "track N of M" — loading a tail with startIndex=0
|
|
1169
|
+
// collapses the index to 0 every swap.
|
|
1170
|
+
val full = ArrayList<MediaItem>(itemCount)
|
|
1171
|
+
for (i in 0 until itemCount) full.add(leadingPlayer.getMediaItemAt(i))
|
|
1172
|
+
standbyPlayer.setMediaItems(full, /* startIndex = */ nextIdx, /* startPositionMs = */ 0L)
|
|
1173
|
+
standbyPlayer.prepare()
|
|
1174
|
+
standbyPlayer.volume = 0f
|
|
1175
|
+
standbyPlayer.play()
|
|
1176
|
+
// Tiny settle so the standby's audio output graph is hot when
|
|
1177
|
+
// the volume ramp begins. The N-tick poll catches the case
|
|
1178
|
+
// where standby buffering is slow + falls back to STATE_ENDED
|
|
1179
|
+
// safety on the leading leg.
|
|
1180
|
+
engineScope.launch {
|
|
1181
|
+
delay(PREROLL_SETTLE_MS)
|
|
1182
|
+
runFade(durationMs)
|
|
1183
|
+
}
|
|
1184
|
+
}
|
|
1185
|
+
|
|
1186
|
+
/**
|
|
1187
|
+
* Schedule the equal-power fade ramp between [leadingPlayer] and
|
|
1188
|
+
* [standbyPlayer] over [durationMs] wall-clock. Caller (the
|
|
1189
|
+
* boundary-arming poll on `TrackPlayer`) prerolls the standby
|
|
1190
|
+
* leg and starts its playback at volume 0 before invoking.
|
|
1191
|
+
* Coroutine on `Dispatchers.Main` at 20ms tick; pause naturally
|
|
1192
|
+
* halts via `playWhenReady=false` because the leading player's
|
|
1193
|
+
* `isPlaying` flips false.
|
|
1194
|
+
*/
|
|
1195
|
+
private fun runFade(durationMs: Long) {
|
|
1196
|
+
// Cancel any prior in-flight ramp coroutine. Do NOT call
|
|
1197
|
+
// [cancelFade] here — it would also `clearMediaItems()` on the
|
|
1198
|
+
// standby leg, destroying the preroll [armCrossfade] just set up
|
|
1199
|
+
// and forcing an early-return below.
|
|
1200
|
+
fadeJob?.cancel()
|
|
1201
|
+
fadeJob = null
|
|
1202
|
+
// Standby must already hold the next item — caller prerolls
|
|
1203
|
+
// before invoking. Defensive guard: nothing prepared, nothing
|
|
1204
|
+
// to swap to. Reset `standbyArmed` so the next [maybeArmCrossfade]
|
|
1205
|
+
// tick can re-arm cleanly if a future caller invokes runFade
|
|
1206
|
+
// without an intervening [swapRoles].
|
|
1207
|
+
if (standbyPlayer.mediaItemCount == 0) {
|
|
1208
|
+
standbyArmed = false
|
|
1209
|
+
return
|
|
1210
|
+
}
|
|
1211
|
+
if (durationMs <= 0L) {
|
|
1212
|
+
// Hard-cut path — no perceptible fade requested. Snap
|
|
1213
|
+
// volumes + swap roles synchronously, preserving the
|
|
1214
|
+
// user's volume choice on the new leading leg.
|
|
1215
|
+
leadingPlayer.volume = 0f
|
|
1216
|
+
standbyPlayer.volume = effectiveUserVolume()
|
|
1217
|
+
swapRoles()
|
|
1218
|
+
return
|
|
1219
|
+
}
|
|
1220
|
+
val durationNs = durationMs * 1_000_000L
|
|
1221
|
+
val startNs = System.nanoTime()
|
|
1222
|
+
val incomingIdx = standbyPlayer.currentMediaItemIndex
|
|
1223
|
+
fadeJob = engineScope.launch {
|
|
1224
|
+
while (isActive) {
|
|
1225
|
+
val elapsedNs = System.nanoTime() - startNs
|
|
1226
|
+
val t = (elapsedNs.toFloat() / durationNs.toFloat()).coerceIn(0f, 1f)
|
|
1227
|
+
val (outVol, inVol) = equalPowerGains(t)
|
|
1228
|
+
// [effectiveUserVolume] honours an in-flight focus duck so
|
|
1229
|
+
// the fade ramp doesn't overwrite the duck on the next tick.
|
|
1230
|
+
val target = effectiveUserVolume()
|
|
1231
|
+
leadingPlayer.volume = outVol * target
|
|
1232
|
+
standbyPlayer.volume = inVol * target
|
|
1233
|
+
if (t >= 1f) break
|
|
1234
|
+
delay(20L)
|
|
1235
|
+
}
|
|
1236
|
+
// Fade complete — promote standby to leading at the
|
|
1237
|
+
// user's chosen (duck-aware) volume; the swap below resets
|
|
1238
|
+
// standby volume to 0.
|
|
1239
|
+
leadingPlayer.volume = 0f
|
|
1240
|
+
standbyPlayer.volume = effectiveUserVolume()
|
|
1241
|
+
swapRoles()
|
|
1242
|
+
fadeJob = null
|
|
1243
|
+
}
|
|
1244
|
+
// Active-track flip: standby leg has launched its fade-in (the
|
|
1245
|
+
// launch above schedules the coroutine onto Dispatchers.Main —
|
|
1246
|
+
// the assignment to `fadeJob` already published the in-flight
|
|
1247
|
+
// marker so the protocol accessors retarget to the standby leg
|
|
1248
|
+
// for any state read the delegate triggers here). Re-bind the
|
|
1249
|
+
// forwarding wrapper to the standby `ExoPlayer` so MediaSession's
|
|
1250
|
+
// auto-published metadata + scrubber flip to the incoming
|
|
1251
|
+
// track from 00:00 the moment it begins audibly playing. Then
|
|
1252
|
+
// notify the delegate to fire JS-facing `onTrackChange` +
|
|
1253
|
+
// refresh `MediaSessionCompat` extras.
|
|
1254
|
+
onCrossfadeBegin?.invoke()
|
|
1255
|
+
delegate?.onCrossfadeBegin(this, incomingIdx)
|
|
1256
|
+
}
|
|
1257
|
+
|
|
1258
|
+
// --- Builders ---
|
|
1259
|
+
|
|
1260
|
+
private fun buildAudioAttributes(): AudioAttributes {
|
|
1261
|
+
val contentType = if (audioContentType == AudioContentType.SPEECH) {
|
|
1262
|
+
C.AUDIO_CONTENT_TYPE_SPEECH
|
|
1263
|
+
} else {
|
|
1264
|
+
C.AUDIO_CONTENT_TYPE_MUSIC
|
|
1265
|
+
}
|
|
1266
|
+
return AudioAttributes.Builder()
|
|
1267
|
+
.setUsage(C.USAGE_MEDIA)
|
|
1268
|
+
.setContentType(contentType)
|
|
1269
|
+
.build()
|
|
1270
|
+
}
|
|
1271
|
+
|
|
1272
|
+
/**
|
|
1273
|
+
* Platform [android.media.AudioAttributes] that mirrors the
|
|
1274
|
+
* Media3 [AudioAttributes] above. The platform type is what
|
|
1275
|
+
* [AudioFocusRequest.Builder.setAudioAttributes] consumes;
|
|
1276
|
+
* we construct it directly rather than reaching into Media3's
|
|
1277
|
+
* deprecated `audioAttributesV21` accessor.
|
|
1278
|
+
*/
|
|
1279
|
+
private fun buildPlatformAudioAttributes(): android.media.AudioAttributes {
|
|
1280
|
+
val contentType = if (audioContentType == AudioContentType.SPEECH) {
|
|
1281
|
+
android.media.AudioAttributes.CONTENT_TYPE_SPEECH
|
|
1282
|
+
} else {
|
|
1283
|
+
android.media.AudioAttributes.CONTENT_TYPE_MUSIC
|
|
1284
|
+
}
|
|
1285
|
+
return android.media.AudioAttributes.Builder()
|
|
1286
|
+
.setUsage(android.media.AudioAttributes.USAGE_MEDIA)
|
|
1287
|
+
.setContentType(contentType)
|
|
1288
|
+
.build()
|
|
1289
|
+
}
|
|
1290
|
+
|
|
1291
|
+
private fun buildPlayer(isStandby: Boolean, renderersFactory: RenderersFactory): ExoPlayer {
|
|
1292
|
+
val builder = ExoPlayer.Builder(context)
|
|
1293
|
+
builder.setRenderersFactory(renderersFactory)
|
|
1294
|
+
val factory = mediaSourceFactoryProvider?.invoke()
|
|
1295
|
+
if (factory != null) {
|
|
1296
|
+
builder.setMediaSourceFactory(factory)
|
|
1297
|
+
}
|
|
1298
|
+
val loadControl: LoadControl = DefaultLoadControl.Builder()
|
|
1299
|
+
.setBufferDurationsMs(
|
|
1300
|
+
if (isStandby) STANDBY_MIN_BUFFER_MS else DEFAULT_MIN_BUFFER_MS,
|
|
1301
|
+
if (isStandby) STANDBY_MAX_BUFFER_MS else DEFAULT_MAX_BUFFER_MS,
|
|
1302
|
+
if (isStandby) STANDBY_BUFFER_FOR_PLAYBACK_MS else DEFAULT_BUFFER_FOR_PLAYBACK_MS,
|
|
1303
|
+
if (isStandby) STANDBY_BUFFER_FOR_PLAYBACK_AFTER_REBUFFER_MS
|
|
1304
|
+
else DEFAULT_BUFFER_FOR_PLAYBACK_AFTER_REBUFFER_MS,
|
|
1305
|
+
)
|
|
1306
|
+
.build()
|
|
1307
|
+
builder.setLoadControl(loadControl)
|
|
1308
|
+
val player = builder.build()
|
|
1309
|
+
player.pauseAtEndOfMediaItems = false
|
|
1310
|
+
player.setAudioAttributes(buildAudioAttributes(), /* handleAudioFocus = */ false)
|
|
1311
|
+
player.setHandleAudioBecomingNoisy(true)
|
|
1312
|
+
player.setWakeMode(C.WAKE_MODE_NETWORK)
|
|
1313
|
+
return player
|
|
1314
|
+
}
|
|
1315
|
+
|
|
1316
|
+
internal companion object {
|
|
1317
|
+
// Defaults match Media3 DefaultLoadControl.
|
|
1318
|
+
internal const val DEFAULT_MIN_BUFFER_MS: Int = 50_000
|
|
1319
|
+
internal const val DEFAULT_MAX_BUFFER_MS: Int = 50_000
|
|
1320
|
+
internal const val DEFAULT_BUFFER_FOR_PLAYBACK_MS: Int = 1_000
|
|
1321
|
+
internal const val DEFAULT_BUFFER_FOR_PLAYBACK_AFTER_REBUFFER_MS: Int = 2_000
|
|
1322
|
+
|
|
1323
|
+
// Standby leg: keep just enough for a few seconds of preroll
|
|
1324
|
+
// so it doesn't compete with the leading leg's lookahead
|
|
1325
|
+
// cache for memory headroom.
|
|
1326
|
+
internal const val STANDBY_MIN_BUFFER_MS: Int = 15_000
|
|
1327
|
+
internal const val STANDBY_MAX_BUFFER_MS: Int = 15_000
|
|
1328
|
+
internal const val STANDBY_BUFFER_FOR_PLAYBACK_MS: Int = 2_000
|
|
1329
|
+
internal const val STANDBY_BUFFER_FOR_PLAYBACK_AFTER_REBUFFER_MS: Int = 2_000
|
|
1330
|
+
|
|
1331
|
+
// Volume multiplier applied during AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK.
|
|
1332
|
+
// Matches Media3's `AudioFocusManager` default duck behaviour.
|
|
1333
|
+
internal const val DUCK_MULTIPLIER: Float = 0.2f
|
|
1334
|
+
|
|
1335
|
+
/** Boundary-arming poll cadence. 200ms is a good balance between
|
|
1336
|
+
* responsiveness (we won't miss the arm window even on a 12s
|
|
1337
|
+
* fade) and battery cost. */
|
|
1338
|
+
internal const val POSITION_POLL_INTERVAL_MS: Long = 200L
|
|
1339
|
+
|
|
1340
|
+
/** Lead time before fade-start to begin standby preroll. The
|
|
1341
|
+
* AudioSink needs ~50-100ms to warm up; 500ms gives comfortable
|
|
1342
|
+
* margin without the standby being ready way too early. */
|
|
1343
|
+
internal const val PREROLL_LEAD_MS: Long = 500L
|
|
1344
|
+
|
|
1345
|
+
/** Settle delay between standby.play() and fade-ramp start.
|
|
1346
|
+
* Lets the AudioSink ready up so the first ramp tick lands on
|
|
1347
|
+
* audible bytes. */
|
|
1348
|
+
internal const val PREROLL_SETTLE_MS: Long = 150L
|
|
1349
|
+
}
|
|
1350
|
+
}
|