react-native-queue-player 1.2.0 → 2.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/QueuePlayer.podspec +14 -9
- package/README.md +3 -3
- package/android/consumer-rules.pro +1 -1
- package/android/src/main/cpp/airplay2_control.c +4 -4
- package/android/src/main/cpp/airplay2_control.h +4 -4
- package/android/src/main/cpp/airplay2_jni.cpp +3 -19
- package/android/src/main/cpp/airplay2_pair.c +7 -7
- package/android/src/main/cpp/airplay2_pair.h +2 -2
- package/android/src/main/cpp/airplay2_rtsp.c +8 -8
- package/android/src/main/cpp/airplay2_session.c +2 -2
- package/android/src/main/cpp/airplay_jni.cpp +8 -69
- package/android/src/main/java/com/margelo/nitro/queueplayer/AirPlayEngine.kt +7 -16
- package/android/src/main/java/com/margelo/nitro/queueplayer/CacheMimeTypes.kt +1 -1
- package/android/src/main/java/com/margelo/nitro/queueplayer/CastManager.kt +2 -14
- package/android/src/main/java/com/margelo/nitro/queueplayer/CellularTransportMonitor.kt +77 -0
- package/android/src/main/java/com/margelo/nitro/queueplayer/CrossfadeEngine.kt +187 -52
- package/android/src/main/java/com/margelo/nitro/queueplayer/Equalizer.kt +27 -7
- package/android/src/main/java/com/margelo/nitro/queueplayer/EqualizerEngine.kt +33 -12
- package/android/src/main/java/com/margelo/nitro/queueplayer/EqualizerLegacyEngine.kt +31 -8
- package/android/src/main/java/com/margelo/nitro/queueplayer/FFTProcessorTee.kt +45 -30
- package/android/src/main/java/com/margelo/nitro/queueplayer/FifoCacheEvictor.kt +111 -15
- package/android/src/main/java/com/margelo/nitro/queueplayer/GaplessEngine.kt +60 -33
- package/android/src/main/java/com/margelo/nitro/queueplayer/HeadlessJsMediaService.kt +1 -1
- package/android/src/main/java/com/margelo/nitro/queueplayer/IEqualizerEngine.kt +3 -1
- package/android/src/main/java/com/margelo/nitro/queueplayer/LookaheadCache.kt +103 -29
- package/android/src/main/java/com/margelo/nitro/queueplayer/LookaheadCacheWriter.kt +77 -29
- package/android/src/main/java/com/margelo/nitro/queueplayer/MediaItemBuilder.kt +6 -6
- package/android/src/main/java/com/margelo/nitro/queueplayer/NowPlayingFormatExtractor.kt +48 -1
- package/android/src/main/java/com/margelo/nitro/queueplayer/PitchCorrection.kt +3 -2
- package/android/src/main/java/com/margelo/nitro/queueplayer/PlaybackEngine.kt +71 -76
- package/android/src/main/java/com/margelo/nitro/queueplayer/PlaybackErrorMapping.kt +3 -3
- package/android/src/main/java/com/margelo/nitro/queueplayer/PlaybackModeStateMachine.kt +2 -2
- package/android/src/main/java/com/margelo/nitro/queueplayer/PlaybackService.kt +68 -46
- package/android/src/main/java/com/margelo/nitro/queueplayer/PlaybackServiceCallback.kt +6 -5
- package/android/src/main/java/com/margelo/nitro/queueplayer/QueueMutationArithmetic.kt +5 -6
- package/android/src/main/java/com/margelo/nitro/queueplayer/QueueSkipArithmetic.kt +11 -8
- package/android/src/main/java/com/margelo/nitro/queueplayer/ReplayGainAudioProcessor.kt +26 -6
- package/android/src/main/java/com/margelo/nitro/queueplayer/ReplayGainData.kt +22 -3
- package/android/src/main/java/com/margelo/nitro/queueplayer/ReplayGainExtractor.kt +38 -12
- package/android/src/main/java/com/margelo/nitro/queueplayer/ReplayGainGain.kt +17 -13
- package/android/src/main/java/com/margelo/nitro/queueplayer/SuppliedReplayGain.kt +66 -0
- package/android/src/main/java/com/margelo/nitro/queueplayer/TrackPlayer.kt +839 -271
- package/android/src/main/java/com/margelo/nitro/queueplayer/Visualizer.kt +4 -8
- package/android/src/main/java/com/margelo/nitro/queueplayer/cast/CastBackend.kt +4 -3
- package/android/src/main/java/com/margelo/nitro/queueplayer/cast/CastSession.kt +5 -4
- package/android/src/main/java/com/margelo/nitro/queueplayer/cast/airplay/AirPlay2JNI.kt +0 -2
- package/android/src/main/java/com/margelo/nitro/queueplayer/cast/airplay/AirPlay2Session.kt +4 -2
- package/android/src/main/java/com/margelo/nitro/queueplayer/cast/airplay/AirPlayBackend.kt +16 -9
- package/android/src/main/java/com/margelo/nitro/queueplayer/cast/airplay/AirPlayMetadataSync.kt +1 -1
- package/android/src/main/java/com/margelo/nitro/queueplayer/cast/airplay/AirPlayRenderersFactory.kt +6 -14
- package/android/src/main/java/com/margelo/nitro/queueplayer/cast/airplay/AirplayJNI.kt +1 -13
- package/android/src/main/java/com/margelo/nitro/queueplayer/cast/airplay/MulticastLockHolder.kt +1 -1
- package/android/src/main/java/com/margelo/nitro/queueplayer/cast/chromecast/ChromecastBackend.kt +3 -3
- package/android/src/main/java/com/margelo/nitro/queueplayer/cast/http/LocalMediaServer.kt +11 -35
- package/android/src/main/java/com/margelo/nitro/queueplayer/cast/http/MediaServerHandle.kt +0 -5
- package/android/src/main/java/com/margelo/nitro/queueplayer/cast/http/MediaTokenRegistry.kt +0 -6
- package/android/src/main/java/com/margelo/nitro/queueplayer/cast/net/LocalAddressMonitor.kt +32 -33
- package/android/src/test/java/androidx/media3/session/{MediaSessionControllerRequestTestSeam.kt → MediaSessionControllerRequestTestSupport.kt} +1 -1
- package/android/src/test/java/com/margelo/nitro/queueplayer/AvrcpMetadataTest.kt +12 -0
- package/android/src/test/java/com/margelo/nitro/queueplayer/CrossfadeEngineFocusLossTest.kt +115 -0
- package/android/src/test/java/com/margelo/nitro/queueplayer/CrossfadeEngineLifecycleTest.kt +45 -0
- package/android/src/test/java/com/margelo/nitro/queueplayer/EngineEndSignalOrderTest.kt +70 -0
- package/android/src/test/java/com/margelo/nitro/queueplayer/EqualizerEngineTest.kt +17 -0
- package/android/src/test/java/com/margelo/nitro/queueplayer/EqualizerKtTest.kt +0 -6
- package/android/src/test/java/com/margelo/nitro/queueplayer/EqualizerLegacyEngineTest.kt +17 -0
- package/android/src/test/java/com/margelo/nitro/queueplayer/FFTProcessorTeeTest.kt +15 -0
- package/android/src/test/java/com/margelo/nitro/queueplayer/FifoCacheEvictorTest.kt +135 -4
- package/android/src/test/java/com/margelo/nitro/queueplayer/GaplessEngineLifecycleTest.kt +1 -0
- package/android/src/test/java/com/margelo/nitro/queueplayer/GaplessEngineReplayGainTest.kt +36 -2
- package/android/src/test/java/com/margelo/nitro/queueplayer/LookaheadCacheTest.kt +24 -18
- package/android/src/test/java/com/margelo/nitro/queueplayer/LookaheadCacheWriterCancelTest.kt +85 -0
- package/android/src/test/java/com/margelo/nitro/queueplayer/LookaheadCacheWriterTest.kt +112 -1
- package/android/src/test/java/com/margelo/nitro/queueplayer/MediaItemBuilderTest.kt +5 -3
- package/android/src/test/java/com/margelo/nitro/queueplayer/NowPlayingFormatExtractorTest.kt +45 -0
- package/android/src/test/java/com/margelo/nitro/queueplayer/PitchAwareAudioProcessorChainTest.kt +1 -1
- package/android/src/test/java/com/margelo/nitro/queueplayer/PlaybackServiceCallbackSearchTest.kt +47 -0
- package/android/src/test/java/com/margelo/nitro/queueplayer/PlaybackServiceCallbackTest.kt +53 -29
- package/android/src/test/java/com/margelo/nitro/queueplayer/PlaybackServiceLifecycleTest.kt +8 -8
- package/android/src/test/java/com/margelo/nitro/queueplayer/QueueSkipArithmeticTest.kt +33 -1
- package/android/src/test/java/com/margelo/nitro/queueplayer/ReplayGainAudioProcessorTest.kt +128 -5
- package/android/src/test/java/com/margelo/nitro/queueplayer/ReplayGainExtractorTest.kt +209 -14
- package/android/src/test/java/com/margelo/nitro/queueplayer/ReplayGainGainTest.kt +150 -10
- package/android/src/test/java/com/margelo/nitro/queueplayer/ReplayGainMergeTest.kt +258 -0
- package/android/src/test/java/com/margelo/nitro/queueplayer/RobolectricServiceBindHelper.kt +2 -2
- package/android/src/test/java/com/margelo/nitro/queueplayer/SessionCommandForwardingPlayerTest.kt +2 -2
- package/android/src/test/java/com/margelo/nitro/queueplayer/ShadowDynamicsProcessingRejectingEnable.kt +19 -0
- package/android/src/test/java/com/margelo/nitro/queueplayer/ShadowEqualizer.kt +1 -1
- package/android/src/test/java/com/margelo/nitro/queueplayer/ShadowEqualizerRejectingBandWrites.kt +27 -0
- package/android/src/test/java/com/margelo/nitro/queueplayer/SuppliedReplayGainTest.kt +61 -0
- package/android/src/test/java/com/margelo/nitro/queueplayer/TrackPlayerAirPlayMetadataWiringTest.kt +4 -4
- package/android/src/test/java/com/margelo/nitro/queueplayer/TrackPlayerAudioFocusTest.kt +1 -1
- package/android/src/test/java/com/margelo/nitro/queueplayer/TrackPlayerCastCommandRoutingTest.kt +2 -1
- package/android/src/test/java/com/margelo/nitro/queueplayer/TrackPlayerCastStateTest.kt +2 -1
- package/android/src/test/java/com/margelo/nitro/queueplayer/TrackPlayerEventsTest.kt +88 -38
- package/android/src/test/java/com/margelo/nitro/queueplayer/TrackPlayerLifecycleTest.kt +205 -27
- package/android/src/test/java/com/margelo/nitro/queueplayer/TrackPlayerLookaheadConfigTest.kt +277 -38
- package/android/src/test/java/com/margelo/nitro/queueplayer/TrackPlayerMutationTest.kt +5 -3
- package/android/src/test/java/com/margelo/nitro/queueplayer/TrackPlayerProgressThrottleTest.kt +2 -1
- package/android/src/test/java/com/margelo/nitro/queueplayer/TrackPlayerQueueChangeTest.kt +85 -3
- package/android/src/test/java/com/margelo/nitro/queueplayer/TrackPlayerQueueTest.kt +11 -7
- package/android/src/test/java/com/margelo/nitro/queueplayer/TrackPlayerReadersTest.kt +2 -1
- package/android/src/test/java/com/margelo/nitro/queueplayer/TrackPlayerSkipCapabilityTest.kt +90 -4
- package/android/src/test/java/com/margelo/nitro/queueplayer/TrackPlayerSkipTest.kt +8 -26
- package/android/src/test/java/com/margelo/nitro/queueplayer/TrackPlayerTransportTest.kt +3 -3
- package/android/src/test/java/com/margelo/nitro/queueplayer/VisualizerKtTest.kt +0 -4
- package/android/src/test/java/com/margelo/nitro/queueplayer/cast/FakeRemotePlayer.kt +2 -2
- package/android/src/test/java/com/margelo/nitro/queueplayer/cast/net/LocalAddressMonitorTest.kt +173 -0
- package/android/src/test/java/com/margelo/nitro/queueplayer/cast/net/ShadowConnectivityManagerRecordingRequests.kt +27 -0
- package/app.plugin.js +17 -1
- package/ios/AVPlayerItemQueueItemId.swift +26 -12
- package/ios/AVQueueBuilder.swift +120 -85
- package/ios/ArtworkLoader.swift +0 -7
- package/ios/ArtworkResolver.swift +22 -4
- package/ios/AssetReplayGainReader.swift +118 -0
- package/ios/AudioSession.swift +67 -16
- package/ios/AudioTapProvider.swift +284 -149
- package/ios/CarPlayBridge.swift +0 -12
- package/ios/CarPlayCoordinator.swift +18 -2
- package/ios/CarPlaySceneDelegate.swift +2 -2
- package/ios/Cast/AirPlayRouteState.swift +9 -8
- package/ios/Cast/Core/CastEventBridge.swift +66 -26
- package/ios/Cast/Core/CastNowPlayingController.swift +38 -16
- package/ios/Cast/Core/CastSession.swift +2 -2
- package/ios/Cast/Core/CastTransportRouter.swift +21 -9
- package/ios/Cast/Core/LocalMediaServer.swift +41 -52
- package/ios/Cast/Core/LocalNetworkPermissionProbe.swift +32 -11
- package/ios/Cast/Core/MediaServerHandle.swift +0 -4
- package/ios/Cast/Core/MediaTokenRegistry.swift +0 -6
- package/ios/CastManager.swift +1 -1
- package/ios/CrossfadeEngine.swift +660 -226
- package/ios/EQTap.swift +20 -17
- package/ios/Equalizer.swift +1 -1
- package/ios/FLACStreamInfo.swift +65 -0
- package/ios/GaplessEngine.swift +57 -30
- package/ios/InputGuards.swift +35 -5
- package/ios/LookaheadCache.swift +202 -58
- package/ios/LookaheadCachePrefetcher.swift +48 -36
- package/ios/MediaServer/MediaHTTPConnection.swift +691 -0
- package/ios/NetworkRecoveryPolicy.swift +32 -0
- package/ios/NowPlayingFormatExtractor.swift +25 -18
- package/ios/NowPlayingInfo.swift +84 -48
- package/ios/OutputRouteMonitor.swift +16 -3
- package/ios/PendingBrowseRequests.swift +1 -1
- package/ios/PlaceholderArtwork.swift +9 -5
- package/ios/PlaybackEngine.swift +165 -76
- package/ios/PlaybackErrorMapping.swift +3 -3
- package/ios/PlaybackModeStateMachine.swift +2 -2
- package/ios/PlaybackNetworkMonitor.swift +73 -0
- package/ios/PlayerStateDerivation.swift +6 -6
- package/ios/QueueMutationArithmetic.swift +25 -0
- package/ios/QueueSkipArithmetic.swift +5 -3
- package/ios/QueueWindowArithmetic.swift +152 -0
- package/ios/ReadThroughServer.swift +283 -0
- package/ios/RemoteCommands.swift +44 -4
- package/ios/ReplayGainData.swift +22 -3
- package/ios/ReplayGainExtractor.swift +70 -27
- package/ios/Siri/VoiceDonation.swift +14 -9
- package/ios/StreamingBitrateProbe.swift +22 -68
- package/ios/SuppliedReplayGain.swift +83 -0
- package/ios/Tests/AVQueueBuilderTests.swift +158 -221
- package/ios/Tests/ActiveItemEngineStub.swift +44 -0
- package/ios/Tests/AssetReplayGainReaderTests.swift +228 -0
- package/ios/Tests/AudioTapProviderDispatchTargetTests.swift +158 -0
- package/ios/Tests/AudioTapProviderReplayGainTests.swift +122 -10
- package/ios/Tests/BitrateReresolveBudgetTests.swift +71 -0
- package/ios/Tests/CastMediaItemTranslationTests.swift +72 -0
- package/ios/Tests/CastNowPlayingControllerTests.swift +1 -1
- package/ios/Tests/CrossfadeAdvanceWithoutFadeTests.swift +120 -0
- package/ios/Tests/CrossfadeEngineIncomingMixTests.swift +122 -0
- package/ios/Tests/CrossfadeEngineTests.swift +164 -0
- package/ios/Tests/CrossfadeMixReapplyTests.swift +66 -0
- package/ios/Tests/CrossfadePlaybackIntentTests.swift +81 -0
- package/ios/Tests/EngineSwapTests.swift +91 -0
- package/ios/Tests/EqualizerAudioMixProviderTests.swift +46 -0
- package/ios/Tests/EqualizerHybridTests.swift +5 -2
- package/ios/Tests/FLACStreamInfoTests.swift +88 -0
- package/ios/Tests/GaplessEngineLifecycleTests.swift +4 -4
- package/ios/Tests/InputGuardsTests.swift +41 -0
- package/ios/Tests/InterruptionIntentTests.swift +77 -0
- package/ios/Tests/LookaheadCacheCellularAccessTests.swift +185 -0
- package/ios/Tests/LookaheadCachePrefetcherTests.swift +53 -30
- package/ios/Tests/LookaheadCacheRuntimeConfigTests.swift +160 -1
- package/ios/Tests/LookaheadCacheTests.swift +44 -18
- package/ios/Tests/MediaHTTPConnectionTests.swift +79 -0
- package/ios/Tests/MutationDeferralTests.swift +220 -0
- package/ios/Tests/NowPlayingInfoTests.swift +86 -99
- package/ios/Tests/NowPlayingSnapshotTests.swift +127 -0
- package/ios/Tests/OriginRestartStitcherTests.swift +60 -0
- package/ios/Tests/PlaybackNetworkMonitorTests.swift +95 -0
- package/ios/Tests/PlayerFixtures.swift +48 -0
- package/ios/Tests/PlayerStateDerivationTests.swift +10 -10
- package/ios/Tests/QueueMutationGenerationTests.swift +141 -0
- package/ios/Tests/QueueRebuildPrefixTests.swift +317 -0
- package/ios/Tests/QueueStateTests.swift +3 -1
- package/ios/Tests/QueueWindowArithmeticTests.swift +56 -0
- package/ios/Tests/QueueWindowSliceTests.swift +162 -0
- package/ios/Tests/ReadThroughRoutingLifecycleTests.swift +63 -0
- package/ios/Tests/ReadThroughServerTests.swift +345 -0
- package/ios/Tests/RemoteCommandsTests.swift +28 -0
- package/ios/Tests/ReplayGainExtractorTests.swift +216 -6
- package/ios/Tests/ReplayGainMergeTests.swift +230 -0
- package/ios/Tests/RetryRecoveryTests.swift +303 -0
- package/ios/Tests/SkipCapabilityTests.swift +233 -8
- package/ios/Tests/SkipIndexTests.swift +32 -2
- package/ios/Tests/SleepTimerPauseIntentTests.swift +43 -0
- package/ios/Tests/StallRecoveryTests.swift +97 -0
- package/ios/Tests/StreamingBitrateProbeTests.swift +17 -20
- package/ios/Tests/TopUpWindowGateTests.swift +394 -0
- package/ios/Tests/TrackPlayer+TestHops.swift +14 -0
- package/ios/Tests/TrackPlayerCallOrderTests.swift +93 -0
- package/ios/Tests/TrackPlayerConfigureTeardownTests.swift +86 -0
- package/ios/Tests/TrackPlayerEndVerdictTests.swift +165 -0
- package/ios/Tests/TrackPlayerSeekTests.swift +137 -0
- package/ios/Tests/TrackPlayerThreadingTests.swift +127 -0
- package/ios/Tests/TrackSourceClassifierTests.swift +33 -7
- package/ios/Tests/VoiceVocabularyOptInTests.swift +38 -0
- package/ios/TrackPlayer+Automotive.swift +101 -0
- package/ios/TrackPlayer+Cache.swift +232 -0
- package/ios/TrackPlayer+Config.swift +291 -0
- package/ios/TrackPlayer+EngineDelegate.swift +209 -0
- package/ios/TrackPlayer+EventsAPI.swift +111 -0
- package/ios/TrackPlayer+EventsDispatch.swift +968 -0
- package/ios/TrackPlayer+EventsWiring.swift +173 -0
- package/ios/TrackPlayer+Lifecycle.swift +930 -0
- package/ios/TrackPlayer+NowPlayingFormat.swift +258 -0
- package/ios/TrackPlayer+Queue.swift +940 -0
- package/ios/TrackPlayer+Recovery.swift +196 -0
- package/ios/TrackPlayer+Skip.swift +455 -0
- package/ios/TrackPlayer+SleepTimer.swift +175 -0
- package/ios/TrackPlayer+State.swift +108 -0
- package/ios/TrackPlayer+Threading.swift +135 -0
- package/ios/TrackPlayer+Transport.swift +267 -0
- package/ios/TrackPlayer+Window.swift +195 -0
- package/ios/TrackPlayer.swift +305 -4619
- package/ios/Visualizer.swift +6 -5
- package/ios/tests-harness/Podfile +1 -1
- package/ios/tests-harness/TestHost.xcodeproj/project.pbxproj +19 -11
- package/ios/tests-harness/scripts/seed-xcodeproj.rb +2 -2
- package/lib/module/hooks/useActiveTrack.js +29 -22
- package/lib/module/hooks/useActiveTrack.js.map +1 -1
- package/lib/module/hooks/useCast.js +8 -26
- package/lib/module/hooks/useCast.js.map +1 -1
- package/lib/module/hooks/useEqualizer.js +19 -12
- package/lib/module/hooks/useEqualizer.js.map +1 -1
- package/lib/module/hooks/useLookaheadCache.js +3 -7
- package/lib/module/hooks/useLookaheadCache.js.map +1 -1
- package/lib/module/hooks/useQueue.js +66 -19
- package/lib/module/hooks/useQueue.js.map +1 -1
- package/lib/module/index.js +6 -3
- package/lib/module/index.js.map +1 -1
- package/lib/module/queueDelta.js +41 -0
- package/lib/module/queueDelta.js.map +1 -0
- package/lib/module/types.js +28 -5
- package/lib/module/types.js.map +1 -1
- package/lib/typescript/TrackPlayer.nitro.d.ts +53 -28
- package/lib/typescript/TrackPlayer.nitro.d.ts.map +1 -1
- package/lib/typescript/hooks/useActiveTrack.d.ts +5 -7
- package/lib/typescript/hooks/useActiveTrack.d.ts.map +1 -1
- package/lib/typescript/hooks/useCast.d.ts +6 -1
- package/lib/typescript/hooks/useCast.d.ts.map +1 -1
- package/lib/typescript/hooks/useEqualizer.d.ts +2 -1
- package/lib/typescript/hooks/useEqualizer.d.ts.map +1 -1
- package/lib/typescript/hooks/useLookaheadCache.d.ts.map +1 -1
- package/lib/typescript/hooks/useQueue.d.ts +4 -4
- package/lib/typescript/hooks/useQueue.d.ts.map +1 -1
- package/lib/typescript/index.d.ts +2 -1
- package/lib/typescript/index.d.ts.map +1 -1
- package/lib/typescript/queueDelta.d.ts +10 -0
- package/lib/typescript/queueDelta.d.ts.map +1 -0
- package/lib/typescript/types.d.ts +168 -12
- package/lib/typescript/types.d.ts.map +1 -1
- package/nitrogen/generated/android/c++/JFunc_void_QueueChangeDelta_double_QueueChangeReason.hpp +85 -0
- package/nitrogen/generated/android/c++/JHybridTrackPlayerSpec.cpp +45 -19
- package/nitrogen/generated/android/c++/JHybridTrackPlayerSpec.hpp +3 -3
- package/nitrogen/generated/android/c++/JLookaheadCacheConfig.hpp +8 -4
- package/nitrogen/generated/android/c++/JPlayerConfig.hpp +5 -1
- package/nitrogen/generated/android/c++/JQueueChangeDelta.hpp +128 -0
- package/nitrogen/generated/android/c++/JTrackItem.hpp +19 -3
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/Func_std__shared_ptr_Promise_std__shared_ptr_Promise_std__vector_BrowseItem______std__string.kt +0 -2
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/Func_std__shared_ptr_Promise_std__shared_ptr_Promise_std__vector_TrackItem______MediaSearchRequest.kt +0 -2
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/Func_std__shared_ptr_Promise_std__shared_ptr_Promise_std__vector_TrackItem______std__string.kt +0 -2
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/Func_void.kt +0 -2
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/Func_void_AudioRoute.kt +0 -2
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/Func_void_BufferState.kt +0 -2
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/Func_void_CacheStatus.kt +0 -2
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/Func_void_CastDiscoveryState.kt +0 -2
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/Func_void_CastLocalNetworkPermissionEvent.kt +0 -2
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/Func_void_CastRoute.kt +0 -2
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/Func_void_CastSessionDiedEvent.kt +0 -2
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/Func_void_PlaybackError.kt +0 -2
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/Func_void_PlayerProgress.kt +0 -2
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/Func_void_PlayerState_StateChangeReason.kt +0 -2
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/{Func_void_std__vector_TrackItem__double_QueueChangeReason.kt → Func_void_QueueChangeDelta_double_QueueChangeReason.kt} +14 -16
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/Func_void_ServiceReadyReason.kt +0 -2
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/Func_void_SkipCapability.kt +0 -2
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/Func_void_SleepTimerState.kt +0 -2
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/Func_void_VisualizerErrorReason.kt +0 -2
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/Func_void_VisualizerFrame.kt +0 -2
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/Func_void_bool.kt +0 -2
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/Func_void_double_double.kt +0 -2
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/Func_void_std__optional_TrackItem__double_TrackChangeReason_std__optional_double_.kt +0 -2
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/Func_void_std__optional_std__variant_nitro__NullType__NowPlayingFormat__.kt +0 -2
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/Func_void_std__vector_CastReceiver_.kt +0 -2
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/Func_void_std__vector_EqualizerBand_.kt +0 -2
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/HybridCastManagerSpec.kt +2 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/HybridEqualizerSpec.kt +2 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/HybridTrackPlayerSpec.kt +6 -4
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/HybridVisualizerSpec.kt +2 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/LookaheadCacheConfig.kt +9 -4
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/PlayerConfig.kt +7 -2
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/QueueChangeDelta.kt +86 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/TrackItem.kt +24 -4
- package/nitrogen/generated/android/queueplayerOnLoad.cpp +2 -2
- package/nitrogen/generated/ios/QueuePlayer-Swift-Cxx-Bridge.cpp +16 -16
- package/nitrogen/generated/ios/QueuePlayer-Swift-Cxx-Bridge.hpp +80 -65
- package/nitrogen/generated/ios/QueuePlayer-Swift-Cxx-Umbrella.hpp +3 -0
- package/nitrogen/generated/ios/c++/HybridTrackPlayerSpecSwift.hpp +6 -3
- package/nitrogen/generated/ios/swift/Func_void_CacheStatus.swift +5 -5
- package/nitrogen/generated/ios/swift/Func_void_QueueChangeDelta_double_QueueChangeReason.swift +46 -0
- package/nitrogen/generated/ios/swift/HybridTrackPlayerSpec.swift +3 -3
- package/nitrogen/generated/ios/swift/HybridTrackPlayerSpec_cxx.swift +32 -24
- package/nitrogen/generated/ios/swift/LookaheadCacheConfig.swift +20 -2
- package/nitrogen/generated/ios/swift/PlayerConfig.swift +19 -1
- package/nitrogen/generated/ios/swift/QueueChangeDelta.swift +82 -0
- package/nitrogen/generated/ios/swift/TrackItem.swift +73 -1
- package/nitrogen/generated/shared/c++/HybridTrackPlayerSpec.hpp +6 -3
- package/nitrogen/generated/shared/c++/LookaheadCacheConfig.hpp +7 -3
- package/nitrogen/generated/shared/c++/PlayerConfig.hpp +5 -1
- package/nitrogen/generated/shared/c++/QueueChangeDelta.hpp +113 -0
- package/nitrogen/generated/shared/c++/TrackItem.hpp +18 -2
- package/package.json +8 -8
- package/src/TrackPlayer.nitro.ts +53 -27
- package/src/hooks/useActiveTrack.ts +29 -22
- package/src/hooks/useCast.ts +14 -15
- package/src/hooks/useEqualizer.ts +19 -12
- package/src/hooks/useLookaheadCache.ts +3 -7
- package/src/hooks/useQueue.ts +66 -17
- package/src/index.ts +13 -3
- package/src/queueDelta.ts +41 -0
- package/src/types.ts +169 -12
- package/android/src/main/java/com/margelo/nitro/queueplayer/PendingIntentBuffer.kt +0 -82
- package/android/src/main/java/com/margelo/nitro/queueplayer/cast/http/LocalMediaServerLifecycle.kt +0 -15
- package/android/src/test/java/com/margelo/nitro/queueplayer/PendingIntentBufferTest.kt +0 -192
- package/android/src/test/java/com/margelo/nitro/queueplayer/PlaybackEngineInterfaceTest.kt +0 -135
- package/android/src/test/java/com/margelo/nitro/queueplayer/SmokeTest.kt +0 -27
- package/ios/Cast/Core/LocalAddressMonitor.swift +0 -110
- package/ios/Cast/Core/MediaHTTPConnection.swift +0 -349
- package/ios/MetadataReader.swift +0 -555
- package/ios/Tests/CrossfadeEngineStubTests.swift +0 -43
- package/ios/Tests/MetadataReaderTests.swift +0 -603
- package/ios/Tests/PlaybackEngineProtocolTests.swift +0 -92
- package/ios/Tests/VoiceDonationTests.swift +0 -26
- package/nitrogen/generated/android/c++/JFunc_void_std__vector_TrackItem__double_QueueChangeReason.hpp +0 -101
- package/nitrogen/generated/ios/swift/Func_void_std__vector_TrackItem__double_QueueChangeReason.swift +0 -46
- /package/ios/{Cast/Core → MediaServer}/MimeTypes.swift +0 -0
package/ios/EQTap.swift
CHANGED
|
@@ -21,7 +21,7 @@ import MediaToolbox
|
|
|
21
21
|
/// (e.g. ReplayGain pre-gain in front of the EQ cascade) may take
|
|
22
22
|
/// short-held NSLock critical sections to read freshly-published
|
|
23
23
|
/// coefficients; the project accepts that cost in exchange for
|
|
24
|
-
/// cross-class consistency with the rest of the audio
|
|
24
|
+
/// cross-class consistency with the rest of the audio path
|
|
25
25
|
/// (`AudioTapProvider.gainsSnapshot`, `Visualizer.subsLock`).
|
|
26
26
|
///
|
|
27
27
|
/// AVFoundation delivers samples as deinterleaved float32: each
|
|
@@ -53,12 +53,17 @@ final class EQTap {
|
|
|
53
53
|
) -> Void
|
|
54
54
|
|
|
55
55
|
/// Optional render-thread prepare hook. Fired once per tap from
|
|
56
|
-
/// `MTAudioProcessingTapPrepareCallback` with the tap's
|
|
57
|
-
///
|
|
58
|
-
///
|
|
59
|
-
///
|
|
60
|
-
/// of the render-thread process loop.
|
|
61
|
-
|
|
56
|
+
/// `MTAudioProcessingTapPrepareCallback` with the tap's `maxFrames`
|
|
57
|
+
/// budget and the format the tap will actually deliver, so DSP closures
|
|
58
|
+
/// can size scratch buffers and configure themselves before the first
|
|
59
|
+
/// process callback lands. Allocation here is safe because prepare runs
|
|
60
|
+
/// synchronously on tap setup, ahead of the render-thread process loop.
|
|
61
|
+
///
|
|
62
|
+
/// The format is the tap's own processing format, which is what the DSP
|
|
63
|
+
/// sees — not the encoded source format the asset's track advertises.
|
|
64
|
+
typealias PrepareFn = (
|
|
65
|
+
_ maxFrames: Int, _ format: AudioStreamBasicDescription
|
|
66
|
+
) -> Void
|
|
62
67
|
|
|
63
68
|
/// Default pass-through process body. Copies input → output.
|
|
64
69
|
static let passThrough: ProcessFn = { input, output, numberFrames, channels, _, _ in
|
|
@@ -87,11 +92,9 @@ final class EQTap {
|
|
|
87
92
|
let asset = item.asset
|
|
88
93
|
if isHLS(asset) { return nil }
|
|
89
94
|
|
|
90
|
-
|
|
91
|
-
let status = asset.statusOfValue(forKey: "tracks", error: &keyError)
|
|
92
|
-
guard status == .loaded else { return nil }
|
|
95
|
+
guard case .loaded(let tracks) = asset.status(of: .tracks) else { return nil }
|
|
93
96
|
|
|
94
|
-
guard let track =
|
|
97
|
+
guard let track = tracks.first(where: { $0.mediaType == .audio }) else {
|
|
95
98
|
return nil
|
|
96
99
|
}
|
|
97
100
|
|
|
@@ -186,14 +189,14 @@ private let tapFinalize: MTAudioProcessingTapFinalizeCallback = { tap in
|
|
|
186
189
|
|
|
187
190
|
private let tapPrepare: MTAudioProcessingTapPrepareCallback = {
|
|
188
191
|
tap, maxFrames, processingFormat in
|
|
189
|
-
// Forward the tap's per-render-batch frame budget
|
|
190
|
-
// prepare hook so DSP closures size scratch buffers
|
|
191
|
-
// the process callback. The default
|
|
192
|
-
// prepare hook installed, so this is a no-op
|
|
193
|
-
|
|
192
|
+
// Forward the tap's per-render-batch frame budget and processing format
|
|
193
|
+
// to the holder's prepare hook so DSP closures size scratch buffers and
|
|
194
|
+
// configure their cascade here, not on the process callback. The default
|
|
195
|
+
// pass-through closure has no prepare hook installed, so this is a no-op
|
|
196
|
+
// for non-DSP taps.
|
|
194
197
|
let storage = MTAudioProcessingTapGetStorage(tap)
|
|
195
198
|
let holder = Unmanaged<ProcessHolder>.fromOpaque(storage).takeUnretainedValue()
|
|
196
|
-
holder.prepare?(maxFrames)
|
|
199
|
+
holder.prepare?(maxFrames, processingFormat.pointee)
|
|
197
200
|
}
|
|
198
201
|
|
|
199
202
|
private let tapUnprepare: MTAudioProcessingTapUnprepareCallback = { tap in
|
package/ios/Equalizer.swift
CHANGED
|
@@ -23,6 +23,7 @@ import NitroModules
|
|
|
23
23
|
/// because the JS side already knows it just dropped the singleton.
|
|
24
24
|
final class Equalizer: HybridEqualizerSpec {
|
|
25
25
|
|
|
26
|
+
private static let currentLock = NSLock()
|
|
26
27
|
/// The most recently constructed Equalizer instance. The iOS
|
|
27
28
|
/// playback engine reaches for this at attach time so it can
|
|
28
29
|
/// wire the shared `AudioTapProvider` to the active player without
|
|
@@ -37,7 +38,6 @@ final class Equalizer: HybridEqualizerSpec {
|
|
|
37
38
|
/// Hybrid when the JS-side `getEqualizer()` singleton drops),
|
|
38
39
|
/// so the strong static doesn't cause a leak — JS reload zeroes
|
|
39
40
|
/// it via the second-construction teardown path below.
|
|
40
|
-
private static let currentLock = NSLock()
|
|
41
41
|
private static var _current: Equalizer?
|
|
42
42
|
static var current: Equalizer? {
|
|
43
43
|
get {
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import Foundation
|
|
2
|
+
|
|
3
|
+
/// FLAC STREAMINFO header values relevant to bitrate computation.
|
|
4
|
+
/// Reading these + the file's byte size gives an accurate average
|
|
5
|
+
/// encoded bitrate without involving AVFoundation.
|
|
6
|
+
///
|
|
7
|
+
/// The only source of a **local** FLAC bitrate:
|
|
8
|
+
/// `estimatedDataRate` reports 0 for FLAC and Core Audio's
|
|
9
|
+
/// `AudioFileStream` probe does not model FLAC's variable-frame
|
|
10
|
+
/// container, so `NowPlayingFormatExtractor.readLocalFLACBitrate`
|
|
11
|
+
/// derives duration from STREAMINFO and divides the file size by it.
|
|
12
|
+
struct FLACStreamInfo {
|
|
13
|
+
let sampleRate: Int // Hz, 1..655350
|
|
14
|
+
let channelCount: Int // 1..8
|
|
15
|
+
let bitsPerSample: Int // 4..32
|
|
16
|
+
let totalSamples: Int64 // 0 = unknown
|
|
17
|
+
|
|
18
|
+
/// Parse a FLAC STREAMINFO block from a `fLaC`-prefixed buffer.
|
|
19
|
+
/// STREAMINFO is type 0 and is required by the FLAC spec to be the
|
|
20
|
+
/// FIRST metadata block. Returns nil when the buffer doesn't start
|
|
21
|
+
/// with the FLAC magic or the block layout is malformed.
|
|
22
|
+
///
|
|
23
|
+
/// STREAMINFO body layout (34 bytes after the 4-byte block header):
|
|
24
|
+
/// - 16 bits: minBlockSize (samples)
|
|
25
|
+
/// - 16 bits: maxBlockSize
|
|
26
|
+
/// - 24 bits: minFrameSize (bytes)
|
|
27
|
+
/// - 24 bits: maxFrameSize
|
|
28
|
+
/// - 20 bits: sampleRate
|
|
29
|
+
/// - 3 bits: channels - 1
|
|
30
|
+
/// - 5 bits: bitsPerSample - 1
|
|
31
|
+
/// - 36 bits: totalSamples
|
|
32
|
+
/// - 128 bits: MD5
|
|
33
|
+
static func parse(_ data: Data) -> FLACStreamInfo? {
|
|
34
|
+
guard data.count >= 4 + 4 + 34 else { return nil }
|
|
35
|
+
guard data[0] == 0x66, data[1] == 0x4c,
|
|
36
|
+
data[2] == 0x61, data[3] == 0x43 else { return nil }
|
|
37
|
+
let blockType = data[4] & 0x7f
|
|
38
|
+
guard blockType == 0 else { return nil }
|
|
39
|
+
let blockSize = Int(data[5]) << 16 | Int(data[6]) << 8 | Int(data[7])
|
|
40
|
+
guard blockSize >= 34, data.count >= 8 + blockSize else { return nil }
|
|
41
|
+
// STREAMINFO body starts at offset 8.
|
|
42
|
+
// Bits 64..83 (offset 10, 11, 12 in the body — i.e. data offsets
|
|
43
|
+
// 18, 19, 20): sampleRate (20 bits, big-endian).
|
|
44
|
+
let b18 = UInt64(data[18])
|
|
45
|
+
let b19 = UInt64(data[19])
|
|
46
|
+
let b20 = UInt64(data[20])
|
|
47
|
+
let sampleRate = Int((b18 << 12) | (b19 << 4) | (b20 >> 4))
|
|
48
|
+
let channels = Int((data[20] >> 1) & 0x07) + 1
|
|
49
|
+
let bitsPerSample = Int(((data[20] & 0x01) << 4) | (data[21] >> 4)) + 1
|
|
50
|
+
// totalSamples — 36 bits starting at byte 21 bit 4.
|
|
51
|
+
let t21 = Int64(data[21] & 0x0f)
|
|
52
|
+
let t22 = Int64(data[22])
|
|
53
|
+
let t23 = Int64(data[23])
|
|
54
|
+
let t24 = Int64(data[24])
|
|
55
|
+
let t25 = Int64(data[25])
|
|
56
|
+
let totalSamples =
|
|
57
|
+
(t21 << 32) | (t22 << 24) | (t23 << 16) | (t24 << 8) | t25
|
|
58
|
+
return FLACStreamInfo(
|
|
59
|
+
sampleRate: sampleRate,
|
|
60
|
+
channelCount: channels,
|
|
61
|
+
bitsPerSample: bitsPerSample,
|
|
62
|
+
totalSamples: totalSamples
|
|
63
|
+
)
|
|
64
|
+
}
|
|
65
|
+
}
|
package/ios/GaplessEngine.swift
CHANGED
|
@@ -20,9 +20,14 @@ import Foundation
|
|
|
20
20
|
/// callbacks; on the iOS side the equivalent fan-out continues to
|
|
21
21
|
/// run through `TrackPlayer`'s direct observer code.
|
|
22
22
|
///
|
|
23
|
-
/// Threading:
|
|
24
|
-
///
|
|
25
|
-
///
|
|
23
|
+
/// Threading: `TrackPlayer` calls every method on its `playerQueue`,
|
|
24
|
+
/// the one serial queue that owns the player. This engine never fires
|
|
25
|
+
/// its `delegate`; its events reach `TrackPlayer` through the KVO and
|
|
26
|
+
/// NotificationCenter observers `TrackPlayer` installs on `player`
|
|
27
|
+
/// (`installGaplessObservers` / `installEventObservers`), each of which
|
|
28
|
+
/// hops onto `playerQueue` before touching state. Main-only frameworks
|
|
29
|
+
/// (`MPNowPlayingInfoCenter`, CarPlay) are reached by `TrackPlayer`
|
|
30
|
+
/// hopping to main with the payload already computed.
|
|
26
31
|
final class GaplessEngine: PlaybackEngine {
|
|
27
32
|
|
|
28
33
|
/// Underlying AVQueuePlayer. Internal to the lib so service-level
|
|
@@ -36,6 +41,13 @@ final class GaplessEngine: PlaybackEngine {
|
|
|
36
41
|
|
|
37
42
|
private var mixProvider: AudioMixProvider?
|
|
38
43
|
private var released = false
|
|
44
|
+
/// Whether the transport wants playback, independent of what the player is
|
|
45
|
+
/// doing. `AVQueuePlayer` does not drop its rate when it runs out of items,
|
|
46
|
+
/// so a drained player is indistinguishable from a playing one by rate
|
|
47
|
+
/// alone — and a rebuild that reads the rate would start the next queue
|
|
48
|
+
/// installed on it. Set by `play()`, cleared by `pause()`, `stop()`,
|
|
49
|
+
/// `release()` and every path that drains the queue.
|
|
50
|
+
private var wantsPlayback = false
|
|
39
51
|
|
|
40
52
|
init() {
|
|
41
53
|
let p = AVQueuePlayer()
|
|
@@ -46,29 +58,15 @@ final class GaplessEngine: PlaybackEngine {
|
|
|
46
58
|
|
|
47
59
|
// MARK: - Lifecycle
|
|
48
60
|
|
|
49
|
-
func prepare(
|
|
50
|
-
items: [AVPlayerItem],
|
|
51
|
-
startIndex: Int,
|
|
52
|
-
startPositionSeconds: TimeInterval
|
|
53
|
-
) async {
|
|
54
|
-
// Queue construction lives on `TrackPlayer.fullRebuildPlayerQueue`
|
|
55
|
-
// (see queue-mutation comment on `PlaybackEngine.swift`). The
|
|
56
|
-
// engine's `prepare` is reserved for when a future engine impl
|
|
57
|
-
// (e.g. a `CrossfadeEngine` standby preroll) needs an async
|
|
58
|
-
// hook; for the gapless engine the queue arrives via direct
|
|
59
|
-
// `engine.player.insert(...)` calls and `prepare` is a no-op.
|
|
60
|
-
_ = items
|
|
61
|
-
_ = startIndex
|
|
62
|
-
_ = startPositionSeconds
|
|
63
|
-
}
|
|
64
|
-
|
|
65
61
|
func release() {
|
|
66
62
|
if released { return }
|
|
67
63
|
released = true
|
|
64
|
+
wantsPlayback = false
|
|
68
65
|
// Releasing the queue empties the AVQueuePlayer's items; the
|
|
69
66
|
// player itself is held by the strong `let player` until the
|
|
70
67
|
// engine is deallocated, which TrackPlayer triggers when it
|
|
71
68
|
// drops its engine reference.
|
|
69
|
+
AVQueueBuilder.cancelLoading(dropping: player.items())
|
|
72
70
|
player.removeAllItems()
|
|
73
71
|
mixProvider = nil
|
|
74
72
|
// Detach the delegate so any in-flight event still on the run
|
|
@@ -95,11 +93,19 @@ final class GaplessEngine: PlaybackEngine {
|
|
|
95
93
|
startIndex: Int,
|
|
96
94
|
startPositionSeconds: TimeInterval
|
|
97
95
|
) {
|
|
98
|
-
let
|
|
96
|
+
let shouldResume = wantsPlayback
|
|
97
|
+
// The outgoing items are discarded here; anything the incoming set reuses
|
|
98
|
+
// keeps its loads, since a cancelled asset never becomes playable.
|
|
99
|
+
AVQueueBuilder.cancelLoading(dropping: player.items(), keeping: items)
|
|
99
100
|
player.removeAllItems()
|
|
100
101
|
guard !items.isEmpty,
|
|
101
102
|
startIndex >= 0,
|
|
102
103
|
startIndex < items.count else {
|
|
104
|
+
// Nothing seated, so nothing is being played. Removing the items does
|
|
105
|
+
// not stop the player, and a leg left running would start whatever is
|
|
106
|
+
// installed next on contact.
|
|
107
|
+
wantsPlayback = false
|
|
108
|
+
player.pause()
|
|
103
109
|
// Empty queue, out-of-range startIndex, or startIndex past
|
|
104
110
|
// end: leave drained. Matches `TrackPlayer.fullRebuildPlayerQueue`
|
|
105
111
|
// where bad index also drains via the same removeAllItems-then-guard
|
|
@@ -129,8 +135,8 @@ final class GaplessEngine: PlaybackEngine {
|
|
|
129
135
|
}
|
|
130
136
|
}
|
|
131
137
|
let restoreRate: () -> Void = { [weak self] in
|
|
132
|
-
guard let
|
|
133
|
-
player.rate =
|
|
138
|
+
guard let self, shouldResume, self.player.rate != self.userSpeed else { return }
|
|
139
|
+
self.player.rate = self.userSpeed
|
|
134
140
|
}
|
|
135
141
|
if startPositionSeconds > 0, let head = player.currentItem {
|
|
136
142
|
head.seek(
|
|
@@ -177,6 +183,12 @@ final class GaplessEngine: PlaybackEngine {
|
|
|
177
183
|
|
|
178
184
|
/// Drain the queue. After this call `currentMediaItem == nil`.
|
|
179
185
|
func removeAllItems() {
|
|
186
|
+
// Emptying the queue is not the same as stopping: `AVQueuePlayer` keeps
|
|
187
|
+
// its rate across `removeAllItems`, so without this the next item seated
|
|
188
|
+
// on the player would start on contact.
|
|
189
|
+
wantsPlayback = false
|
|
190
|
+
player.pause()
|
|
191
|
+
AVQueueBuilder.cancelLoading(dropping: player.items())
|
|
180
192
|
player.removeAllItems()
|
|
181
193
|
}
|
|
182
194
|
|
|
@@ -200,10 +212,18 @@ final class GaplessEngine: PlaybackEngine {
|
|
|
200
212
|
/// (the intended rate), not `player.rate` (0 while paused).
|
|
201
213
|
private var pitchCorrectionMode: PitchCorrectionMode = .voice
|
|
202
214
|
|
|
203
|
-
func play() {
|
|
204
|
-
|
|
215
|
+
func play() {
|
|
216
|
+
wantsPlayback = true
|
|
217
|
+
player.rate = userSpeed
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
func pause() {
|
|
221
|
+
wantsPlayback = false
|
|
222
|
+
player.pause()
|
|
223
|
+
}
|
|
205
224
|
|
|
206
225
|
func stop() {
|
|
226
|
+
wantsPlayback = false
|
|
207
227
|
player.pause()
|
|
208
228
|
if let item = player.currentItem {
|
|
209
229
|
item.seek(to: .zero, completionHandler: nil)
|
|
@@ -353,6 +373,14 @@ final class GaplessEngine: PlaybackEngine {
|
|
|
353
373
|
player.currentItem
|
|
354
374
|
}
|
|
355
375
|
|
|
376
|
+
/// This engine has one leg and no fade to schedule.
|
|
377
|
+
var isFadePendingOrActive: Bool { false }
|
|
378
|
+
|
|
379
|
+
/// One leg, so whatever is current is the whole of what is attached.
|
|
380
|
+
var attachedQueueItemIds: [String] {
|
|
381
|
+
player.currentItem?.queueItemId.map { [$0] } ?? []
|
|
382
|
+
}
|
|
383
|
+
|
|
356
384
|
var allMediaItems: [AVPlayerItem] {
|
|
357
385
|
player.items()
|
|
358
386
|
}
|
|
@@ -381,11 +409,10 @@ final class GaplessEngine: PlaybackEngine {
|
|
|
381
409
|
if item.audioMix != nil { continue }
|
|
382
410
|
item.audioMix = provider?.audioMix(for: item)
|
|
383
411
|
}
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
preconditionFailure("PlaybackEngine.handoff is not implemented for the single-engine configuration")
|
|
412
|
+
// `audioMix(for:)` announces each item it is given as the active asset,
|
|
413
|
+
// so a walk leaves the last item refreshed standing as active. Re-assert
|
|
414
|
+
// the one that is playing, or consumers read the wrong asset — the
|
|
415
|
+
// visualizer's HLS check is answered from it.
|
|
416
|
+
provider?.noteActiveAsset(player.currentItem?.asset)
|
|
390
417
|
}
|
|
391
418
|
}
|
package/ios/InputGuards.swift
CHANGED
|
@@ -27,10 +27,7 @@ import Foundation
|
|
|
27
27
|
/// project-empirical working pattern; both shapes are valid SPI
|
|
28
28
|
/// per the Swift language spec, but the per-method form is what
|
|
29
29
|
/// the rest of the codebase uses (see `AVQueueBuilder`,
|
|
30
|
-
/// `QueueMutationArithmetic`).
|
|
31
|
-
/// deferral note in `plan/backlog.md` documents a separate
|
|
32
|
-
/// SPI-related compiler oddity that does NOT manifest with this
|
|
33
|
-
/// shape.
|
|
30
|
+
/// `QueueMutationArithmetic`).
|
|
34
31
|
public enum InputGuards {
|
|
35
32
|
|
|
36
33
|
/// Validate a `Double` that JS sends as a queue index. Returns
|
|
@@ -49,6 +46,18 @@ public enum InputGuards {
|
|
|
49
46
|
return Int(value)
|
|
50
47
|
}
|
|
51
48
|
|
|
49
|
+
/// Convert a `Double` insert position to the `Int` the queue arithmetic
|
|
50
|
+
/// clamps. `nil` for non-finite values (dropped: append). A finite value
|
|
51
|
+
/// past what `Int` holds is clamped to `count`, since every position past
|
|
52
|
+
/// the end means "append" anyway; `Int(value)` on such a value traps.
|
|
53
|
+
@_spi(QueuePlayerTests)
|
|
54
|
+
public static func insertPosition(_ value: Double?, count: Int) -> Int? {
|
|
55
|
+
guard let value, value.isFinite else { return nil }
|
|
56
|
+
if value < 0 { return 0 }
|
|
57
|
+
if value >= Double(count) { return count }
|
|
58
|
+
return Int(value)
|
|
59
|
+
}
|
|
60
|
+
|
|
52
61
|
/// Validate a `Double` seek position (seconds). Returns `nil` for
|
|
53
62
|
/// non-finite values; otherwise returns the value unchanged. The
|
|
54
63
|
/// caller is responsible for clamping negative values to 0 and
|
|
@@ -58,6 +67,27 @@ public enum InputGuards {
|
|
|
58
67
|
@_spi(QueuePlayerTests)
|
|
59
68
|
public static func sanitisedSeekPosition(_ value: Double) -> Double? {
|
|
60
69
|
guard value.isFinite else { return nil }
|
|
61
|
-
|
|
70
|
+
// Finiteness alone is not enough: a finite 1e300 traps the same way
|
|
71
|
+
// `.infinity` does once it is scaled to milliseconds and converted to
|
|
72
|
+
// `Int64`. The bound is far past any real media duration, so clamping to
|
|
73
|
+
// it never changes a seek a consumer could observe.
|
|
74
|
+
return max(-maxSeekSeconds, min(maxSeekSeconds, value))
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
private static let maxSeekSeconds: Double = 1e12
|
|
78
|
+
|
|
79
|
+
/// Validate a `Double` lookahead count. Returns a non-negative `Int`,
|
|
80
|
+
/// clamped in `Double` space so an enormous or infinite consumer value
|
|
81
|
+
/// cannot trap the conversion. The prefetcher bounds its own window by the
|
|
82
|
+
/// queue length, so the upper clamp loses nothing observable.
|
|
83
|
+
///
|
|
84
|
+
/// `.infinity` clamps to the bound rather than falling back to zero — it
|
|
85
|
+
/// asks for as much lookahead as possible, and answering "none" would invert
|
|
86
|
+
/// the request. Only `NaN`, which asks for nothing intelligible, reads as
|
|
87
|
+
/// zero; comparison against it is always false, so it cannot be clamped.
|
|
88
|
+
@_spi(QueuePlayerTests)
|
|
89
|
+
public static func validLookaheadCount(_ value: Double) -> Int {
|
|
90
|
+
guard !value.isNaN else { return 0 }
|
|
91
|
+
return Int(max(0, min(value, 10_000)))
|
|
62
92
|
}
|
|
63
93
|
}
|