├── .editorconfig ├── .git-blame-ignore-revs ├── .github └── workflows │ ├── build.yml │ └── release.yml ├── .gitignore ├── CHANGELOG.md ├── FAQ.md ├── LICENSE ├── README.md ├── build.gradle.kts ├── common ├── build.gradle.kts └── src │ └── main │ └── java │ └── com │ └── sedmelluq │ └── lava │ └── common │ ├── natives │ ├── NativeLibraryBinaryProvider.java │ ├── NativeLibraryLoader.java │ ├── NativeLibraryProperties.java │ ├── NativeResourceHolder.java │ ├── ResourceNativeLibraryBinaryProvider.java │ ├── SystemNativeLibraryProperties.java │ └── architecture │ │ ├── ArchitectureType.java │ │ ├── DefaultArchitectureTypes.java │ │ ├── DefaultOperatingSystemTypes.java │ │ ├── OperatingSystemType.java │ │ └── SystemType.java │ └── tools │ ├── DaemonThreadFactory.java │ └── ExecutorTools.java ├── demo-d4j ├── build.gradle ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── src │ └── main │ └── java │ └── com │ └── sedmelluq │ └── discord │ └── lavaplayer │ └── demo │ └── d4j │ ├── D4jAudioProvider.java │ ├── GuildMusicManager.java │ ├── Main.java │ └── TrackScheduler.java ├── demo-jda ├── build.gradle ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── src │ └── main │ └── java │ └── com │ └── sedmelluq │ └── discord │ └── lavaplayer │ └── demo │ └── jda │ ├── AudioPlayerSendHandler.java │ ├── GuildMusicManager.java │ ├── Main.java │ └── TrackScheduler.java ├── extensions ├── format-xm │ ├── build.gradle.kts │ └── src │ │ └── main │ │ └── java │ │ └── com │ │ └── sedmelluq │ │ └── lavaplayer │ │ └── extensions │ │ └── format │ │ └── xm │ │ ├── XmAudioTrack.java │ │ ├── XmContainerProbe.java │ │ ├── XmFileLoader.java │ │ └── XmTrackProvider.java └── youtube-rotator │ ├── build.gradle.kts │ └── src │ └── main │ └── java │ └── com │ └── sedmelluq │ └── lava │ └── extensions │ └── youtuberotator │ ├── YoutubeIpRotatorFilter.java │ ├── YoutubeIpRotatorRetryHandler.java │ ├── YoutubeIpRotatorSetup.java │ ├── planner │ ├── AbstractRoutePlanner.java │ ├── BalancingIpRoutePlanner.java │ ├── NanoIpRoutePlanner.java │ ├── RotatingIpRoutePlanner.java │ └── RotatingNanoIpRoutePlanner.java │ └── tools │ ├── BigRandom.java │ ├── RateLimitException.java │ ├── Tuple.java │ └── ip │ ├── CombinedIpBlock.java │ ├── IpAddressTools.java │ ├── IpBlock.java │ ├── Ipv4Block.java │ └── Ipv6Block.java ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── jitpack.yml ├── main ├── build.gradle.kts └── src │ └── main │ ├── java │ └── com │ │ └── sedmelluq │ │ └── discord │ │ └── lavaplayer │ │ ├── container │ │ ├── Formats.java │ │ ├── MediaContainer.java │ │ ├── MediaContainerDescriptor.java │ │ ├── MediaContainerDetection.java │ │ ├── MediaContainerDetectionResult.java │ │ ├── MediaContainerHints.java │ │ ├── MediaContainerProbe.java │ │ ├── MediaContainerRegistry.java │ │ ├── adts │ │ │ ├── AdtsAudioTrack.java │ │ │ ├── AdtsContainerProbe.java │ │ │ ├── AdtsPacketHeader.java │ │ │ ├── AdtsStreamProvider.java │ │ │ └── AdtsStreamReader.java │ │ ├── common │ │ │ ├── AacPacketRouter.java │ │ │ └── OpusPacketRouter.java │ │ ├── flac │ │ │ ├── FlacAudioTrack.java │ │ │ ├── FlacContainerProbe.java │ │ │ ├── FlacFileLoader.java │ │ │ ├── FlacMetadataHeader.java │ │ │ ├── FlacMetadataReader.java │ │ │ ├── FlacSeekPoint.java │ │ │ ├── FlacStreamInfo.java │ │ │ ├── FlacTrackInfo.java │ │ │ ├── FlacTrackInfoBuilder.java │ │ │ ├── FlacTrackProvider.java │ │ │ └── frame │ │ │ │ ├── FlacFrameHeaderReader.java │ │ │ │ ├── FlacFrameInfo.java │ │ │ │ ├── FlacFrameReader.java │ │ │ │ └── FlacSubFrameReader.java │ │ ├── matroska │ │ │ ├── MatroskaAacTrackConsumer.java │ │ │ ├── MatroskaAudioTrack.java │ │ │ ├── MatroskaContainerProbe.java │ │ │ ├── MatroskaOpusTrackConsumer.java │ │ │ ├── MatroskaStreamingFile.java │ │ │ ├── MatroskaTrackConsumer.java │ │ │ ├── MatroskaVorbisTrackConsumer.java │ │ │ └── format │ │ │ │ ├── MatroskaBlock.java │ │ │ │ ├── MatroskaCuePoint.java │ │ │ │ ├── MatroskaEbmlReader.java │ │ │ │ ├── MatroskaElement.java │ │ │ │ ├── MatroskaElementType.java │ │ │ │ ├── MatroskaFileReader.java │ │ │ │ ├── MatroskaFileTrack.java │ │ │ │ ├── MutableMatroskaBlock.java │ │ │ │ └── MutableMatroskaElement.java │ │ ├── mp3 │ │ │ ├── Mp3AudioTrack.java │ │ │ ├── Mp3ConstantRateSeeker.java │ │ │ ├── Mp3ContainerProbe.java │ │ │ ├── Mp3FrameReader.java │ │ │ ├── Mp3Seeker.java │ │ │ ├── Mp3StreamSeeker.java │ │ │ ├── Mp3TrackProvider.java │ │ │ └── Mp3XingSeeker.java │ │ ├── mpeg │ │ │ ├── MpegAacTrackConsumer.java │ │ │ ├── MpegAudioTrack.java │ │ │ ├── MpegContainerProbe.java │ │ │ ├── MpegFileLoader.java │ │ │ ├── MpegNoopTrackConsumer.java │ │ │ ├── MpegTrackConsumer.java │ │ │ ├── MpegTrackInfo.java │ │ │ └── reader │ │ │ │ ├── MpegFileTrackProvider.java │ │ │ │ ├── MpegParseStopChecker.java │ │ │ │ ├── MpegReader.java │ │ │ │ ├── MpegSectionHandler.java │ │ │ │ ├── MpegSectionInfo.java │ │ │ │ ├── MpegVersionedSectionHandler.java │ │ │ │ ├── MpegVersionedSectionInfo.java │ │ │ │ ├── fragmented │ │ │ │ ├── MpegFragmentedFileTrackProvider.java │ │ │ │ ├── MpegGlobalSeekInfo.java │ │ │ │ ├── MpegSegmentEntry.java │ │ │ │ └── MpegTrackFragmentHeader.java │ │ │ │ └── standard │ │ │ │ └── MpegStandardFileTrackProvider.java │ │ ├── mpegts │ │ │ ├── MpegAdtsAudioTrack.java │ │ │ ├── MpegAdtsContainerProbe.java │ │ │ ├── MpegTsElementaryInputStream.java │ │ │ └── PesPacketInputStream.java │ │ ├── ogg │ │ │ ├── OggAudioTrack.java │ │ │ ├── OggCodecHandler.java │ │ │ ├── OggContainerProbe.java │ │ │ ├── OggMetadata.java │ │ │ ├── OggPacketInputStream.java │ │ │ ├── OggPageHeader.java │ │ │ ├── OggPageScanner.java │ │ │ ├── OggSeekPoint.java │ │ │ ├── OggStreamSizeInfo.java │ │ │ ├── OggTrackBlueprint.java │ │ │ ├── OggTrackHandler.java │ │ │ ├── OggTrackLoader.java │ │ │ ├── flac │ │ │ │ ├── OggFlacCodecHandler.java │ │ │ │ └── OggFlacTrackHandler.java │ │ │ ├── opus │ │ │ │ ├── OggOpusCodecHandler.java │ │ │ │ └── OggOpusTrackHandler.java │ │ │ └── vorbis │ │ │ │ ├── OggVorbisCodecHandler.java │ │ │ │ ├── OggVorbisTrackHandler.java │ │ │ │ └── VorbisCommentParser.java │ │ ├── playlists │ │ │ ├── ExtendedM3uParser.java │ │ │ ├── HlsStreamSegment.java │ │ │ ├── HlsStreamSegmentParser.java │ │ │ ├── HlsStreamSegmentUrlProvider.java │ │ │ ├── HlsStreamTrack.java │ │ │ ├── M3uPlaylistContainerProbe.java │ │ │ ├── PlainPlaylistContainerProbe.java │ │ │ └── PlsPlaylistContainerProbe.java │ │ └── wav │ │ │ ├── WavAudioTrack.java │ │ │ ├── WavContainerProbe.java │ │ │ ├── WavFileInfo.java │ │ │ ├── WavFileLoader.java │ │ │ ├── WavTrackProvider.java │ │ │ └── WaveFormatType.java │ │ ├── filter │ │ ├── AudioFilter.java │ │ ├── AudioFilterChain.java │ │ ├── AudioPipeline.java │ │ ├── AudioPipelineFactory.java │ │ ├── AudioPostProcessor.java │ │ ├── BufferingPostProcessor.java │ │ ├── ChannelCountPcmAudioFilter.java │ │ ├── CompositeAudioFilter.java │ │ ├── FilterChainBuilder.java │ │ ├── FinalPcmAudioFilter.java │ │ ├── FloatPcmAudioFilter.java │ │ ├── PcmFilterFactory.java │ │ ├── PcmFormat.java │ │ ├── ResamplingPcmAudioFilter.java │ │ ├── ShortPcmAudioFilter.java │ │ ├── SplitShortPcmAudioFilter.java │ │ ├── UniversalPcmAudioFilter.java │ │ ├── UserProvidedAudioFilters.java │ │ ├── converter │ │ │ ├── ConverterAudioFilter.java │ │ │ ├── ToFloatAudioFilter.java │ │ │ ├── ToShortAudioFilter.java │ │ │ └── ToSplitShortAudioFilter.java │ │ ├── equalizer │ │ │ ├── Equalizer.java │ │ │ ├── EqualizerConfiguration.java │ │ │ └── EqualizerFactory.java │ │ └── volume │ │ │ ├── AudioFrameVolumeChanger.java │ │ │ ├── PcmVolumeProcessor.java │ │ │ └── VolumePostProcessor.java │ │ ├── format │ │ ├── AudioDataFormat.java │ │ ├── AudioDataFormatTools.java │ │ ├── AudioPlayerInputStream.java │ │ ├── OpusAudioDataFormat.java │ │ ├── Pcm16AudioDataFormat.java │ │ ├── StandardAudioDataFormats.java │ │ └── transcoder │ │ │ ├── AudioChunkDecoder.java │ │ │ ├── AudioChunkEncoder.java │ │ │ ├── OpusChunkDecoder.java │ │ │ ├── OpusChunkEncoder.java │ │ │ ├── PcmChunkDecoder.java │ │ │ └── PcmChunkEncoder.java │ │ ├── natives │ │ ├── ConnectorNativeLibLoader.java │ │ ├── aac │ │ │ ├── AacDecoder.java │ │ │ └── AacDecoderLibrary.java │ │ ├── mp3 │ │ │ ├── Mp3Decoder.java │ │ │ └── Mp3DecoderLibrary.java │ │ ├── opus │ │ │ ├── OpusDecoder.java │ │ │ ├── OpusDecoderLibrary.java │ │ │ ├── OpusEncoder.java │ │ │ └── OpusEncoderLibrary.java │ │ ├── samplerate │ │ │ ├── SampleRateConverter.java │ │ │ └── SampleRateLibrary.java │ │ ├── statistics │ │ │ ├── CpuStatistics.java │ │ │ └── CpuStatisticsLibrary.java │ │ └── vorbis │ │ │ ├── VorbisDecoder.java │ │ │ └── VorbisDecoderLibrary.java │ │ ├── player │ │ ├── AudioConfiguration.java │ │ ├── AudioLoadResultHandler.java │ │ ├── AudioPlayer.java │ │ ├── AudioPlayerLifecycleManager.java │ │ ├── AudioPlayerManager.java │ │ ├── AudioPlayerOptions.java │ │ ├── DefaultAudioPlayer.java │ │ ├── DefaultAudioPlayerManager.java │ │ ├── FunctionalResultHandler.java │ │ ├── event │ │ │ ├── AudioEvent.java │ │ │ ├── AudioEventAdapter.java │ │ │ ├── AudioEventListener.java │ │ │ ├── PlayerPauseEvent.java │ │ │ ├── PlayerResumeEvent.java │ │ │ ├── TrackEndEvent.java │ │ │ ├── TrackExceptionEvent.java │ │ │ ├── TrackStartEvent.java │ │ │ └── TrackStuckEvent.java │ │ └── hook │ │ │ ├── AudioOutputHook.java │ │ │ └── AudioOutputHookFactory.java │ │ ├── source │ │ ├── AudioSourceManager.java │ │ ├── AudioSourceManagers.java │ │ ├── ProbingAudioSourceManager.java │ │ ├── bandcamp │ │ │ ├── BandcampAudioSourceManager.java │ │ │ └── BandcampAudioTrack.java │ │ ├── beam │ │ │ ├── BeamAudioSourceManager.java │ │ │ ├── BeamAudioTrack.java │ │ │ └── BeamSegmentUrlProvider.java │ │ ├── getyarn │ │ │ ├── GetyarnAudioSourceManager.java │ │ │ └── GetyarnAudioTrack.java │ │ ├── http │ │ │ ├── HttpAudioSourceManager.java │ │ │ └── HttpAudioTrack.java │ │ ├── local │ │ │ ├── LocalAudioSourceManager.java │ │ │ ├── LocalAudioTrack.java │ │ │ └── LocalSeekableInputStream.java │ │ ├── nico │ │ │ ├── HeartbeatingHttpStream.java │ │ │ ├── NicoAudioSourceManager.java │ │ │ └── NicoAudioTrack.java │ │ ├── soundcloud │ │ │ ├── DefaultSoundCloudDataLoader.java │ │ │ ├── DefaultSoundCloudDataReader.java │ │ │ ├── DefaultSoundCloudFormatHandler.java │ │ │ ├── DefaultSoundCloudPlaylistLoader.java │ │ │ ├── DefaultSoundCloudTrackFormat.java │ │ │ ├── SoundCloudAudioSourceManager.java │ │ │ ├── SoundCloudAudioTrack.java │ │ │ ├── SoundCloudClientIdTracker.java │ │ │ ├── SoundCloudDataLoader.java │ │ │ ├── SoundCloudDataReader.java │ │ │ ├── SoundCloudFormatHandler.java │ │ │ ├── SoundCloudHelper.java │ │ │ ├── SoundCloudHttpContextFilter.java │ │ │ ├── SoundCloudM3uAudioTrack.java │ │ │ ├── SoundCloudM3uInfo.java │ │ │ ├── SoundCloudMp3SegmentDecoder.java │ │ │ ├── SoundCloudOpusSegmentDecoder.java │ │ │ ├── SoundCloudPlaylistLoader.java │ │ │ ├── SoundCloudSegmentDecoder.java │ │ │ └── SoundCloudTrackFormat.java │ │ ├── stream │ │ │ ├── M3uStreamAudioTrack.java │ │ │ ├── M3uStreamSegmentUrlProvider.java │ │ │ └── MpegTsM3uStreamAudioTrack.java │ │ ├── twitch │ │ │ ├── TwitchConstants.java │ │ │ ├── TwitchStreamAudioSourceManager.java │ │ │ ├── TwitchStreamAudioTrack.java │ │ │ └── TwitchStreamSegmentUrlProvider.java │ │ ├── vimeo │ │ │ ├── VimeoAudioSourceManager.java │ │ │ └── VimeoAudioTrack.java │ │ ├── yamusic │ │ │ ├── AbstractYandexMusicApiLoader.java │ │ │ ├── DefaultYandexMusicDirectUrlLoader.java │ │ │ ├── DefaultYandexMusicPlaylistLoader.java │ │ │ ├── DefaultYandexMusicTrackLoader.java │ │ │ ├── DefaultYandexSearchProvider.java │ │ │ ├── YandexHttpContextFilter.java │ │ │ ├── YandexMusicApiLoader.java │ │ │ ├── YandexMusicAudioSourceManager.java │ │ │ ├── YandexMusicAudioTrack.java │ │ │ ├── YandexMusicDirectUrlLoader.java │ │ │ ├── YandexMusicPlaylistLoader.java │ │ │ ├── YandexMusicSearchResultLoader.java │ │ │ ├── YandexMusicTrackLoader.java │ │ │ └── YandexMusicUtils.java │ │ └── youtube │ │ │ ├── DefaultYoutubeLinkRouter.java │ │ │ ├── DefaultYoutubePlaylistLoader.java │ │ │ ├── DefaultYoutubeTrackDetails.java │ │ │ ├── DefaultYoutubeTrackDetailsLoader.java │ │ │ ├── YoutubeAccessTokenTracker.java │ │ │ ├── YoutubeAudioSourceManager.java │ │ │ ├── YoutubeAudioTrack.java │ │ │ ├── YoutubeCipherOperation.java │ │ │ ├── YoutubeCipherOperationType.java │ │ │ ├── YoutubeClientConfig.java │ │ │ ├── YoutubeConstants.java │ │ │ ├── YoutubeFormatInfo.java │ │ │ ├── YoutubeHttpContextFilter.java │ │ │ ├── YoutubeLinkRouter.java │ │ │ ├── YoutubeMixLoader.java │ │ │ ├── YoutubeMixProvider.java │ │ │ ├── YoutubeMpegStreamAudioTrack.java │ │ │ ├── YoutubePayloadHelper.java │ │ │ ├── YoutubePersistentHttpStream.java │ │ │ ├── YoutubePlaylistLoader.java │ │ │ ├── YoutubeSearchMusicProvider.java │ │ │ ├── YoutubeSearchMusicResultLoader.java │ │ │ ├── YoutubeSearchProvider.java │ │ │ ├── YoutubeSearchResultLoader.java │ │ │ ├── YoutubeSignatureCipher.java │ │ │ ├── YoutubeSignatureCipherManager.java │ │ │ ├── YoutubeSignatureResolver.java │ │ │ ├── YoutubeTrackDetails.java │ │ │ ├── YoutubeTrackDetailsLoader.java │ │ │ ├── YoutubeTrackFormat.java │ │ │ ├── YoutubeTrackJsonData.java │ │ │ └── format │ │ │ ├── LegacyAdaptiveFormatsExtractor.java │ │ │ ├── LegacyDashMpdFormatsExtractor.java │ │ │ ├── LegacyStreamMapFormatsExtractor.java │ │ │ ├── OfflineYoutubeTrackFormatExtractor.java │ │ │ ├── StreamingDataFormatsExtractor.java │ │ │ └── YoutubeTrackFormatExtractor.java │ │ ├── tools │ │ ├── CopyOnUpdateIdentityList.java │ │ ├── DataFormatTools.java │ │ ├── DecodedException.java │ │ ├── ExceptionTools.java │ │ ├── FriendlyException.java │ │ ├── FutureTools.java │ │ ├── GarbageCollectionMonitor.java │ │ ├── JsonBrowser.java │ │ ├── OrderedExecutor.java │ │ ├── PlayerLibrary.java │ │ ├── RingBufferMath.java │ │ ├── ThumbnailTools.java │ │ ├── Units.java │ │ ├── http │ │ │ ├── AbstractHttpContextFilter.java │ │ │ ├── ExtendedConnectionOperator.java │ │ │ ├── ExtendedHttpClientBuilder.java │ │ │ ├── ExtendedHttpConfigurable.java │ │ │ ├── HttpContextFilter.java │ │ │ ├── HttpContextRetryCounter.java │ │ │ ├── HttpStreamTools.java │ │ │ ├── MultiHttpConfigurable.java │ │ │ ├── SettableHttpRequestFilter.java │ │ │ └── SimpleHttpClientConnectionManager.java │ │ └── io │ │ │ ├── AbstractHttpInterfaceManager.java │ │ │ ├── BitBufferReader.java │ │ │ ├── BitStreamReader.java │ │ │ ├── BitStreamWriter.java │ │ │ ├── ByteBufferInputStream.java │ │ │ ├── ByteBufferOutputStream.java │ │ │ ├── ChainedInputStream.java │ │ │ ├── DetachedByteChannel.java │ │ │ ├── DirectBufferStreamBroker.java │ │ │ ├── EmptyInputStream.java │ │ │ ├── ExtendedBufferedInputStream.java │ │ │ ├── GreedyInputStream.java │ │ │ ├── HttpClientTools.java │ │ │ ├── HttpConfigurable.java │ │ │ ├── HttpInterface.java │ │ │ ├── HttpInterfaceManager.java │ │ │ ├── MessageInput.java │ │ │ ├── MessageOutput.java │ │ │ ├── NonSeekableInputStream.java │ │ │ ├── PersistentHttpStream.java │ │ │ ├── ResettableBoundedInputStream.java │ │ │ ├── SavedHeadSeekableInputStream.java │ │ │ ├── SeekableInputStream.java │ │ │ ├── SimpleHttpInterfaceManager.java │ │ │ ├── StreamTools.java │ │ │ ├── ThreadLocalHttpInterfaceManager.java │ │ │ └── TrustManagerBuilder.java │ │ └── track │ │ ├── AudioItem.java │ │ ├── AudioPlaylist.java │ │ ├── AudioReference.java │ │ ├── AudioTrack.java │ │ ├── AudioTrackEndReason.java │ │ ├── AudioTrackInfo.java │ │ ├── AudioTrackState.java │ │ ├── BaseAudioTrack.java │ │ ├── BasicAudioPlaylist.java │ │ ├── DecodedTrackHolder.java │ │ ├── DelegatedAudioTrack.java │ │ ├── InternalAudioTrack.java │ │ ├── TrackMarker.java │ │ ├── TrackMarkerHandler.java │ │ ├── TrackMarkerTracker.java │ │ ├── TrackStateListener.java │ │ ├── info │ │ ├── AudioTrackInfoBuilder.java │ │ └── AudioTrackInfoProvider.java │ │ └── playback │ │ ├── AbstractAudioFrameBuffer.java │ │ ├── AbstractMutableAudioFrame.java │ │ ├── AllocatingAudioFrameBuffer.java │ │ ├── AudioFrame.java │ │ ├── AudioFrameBuffer.java │ │ ├── AudioFrameBufferFactory.java │ │ ├── AudioFrameConsumer.java │ │ ├── AudioFrameProvider.java │ │ ├── AudioFrameProviderTools.java │ │ ├── AudioFrameRebuilder.java │ │ ├── AudioProcessingContext.java │ │ ├── AudioTrackExecutor.java │ │ ├── ImmutableAudioFrame.java │ │ ├── LocalAudioTrackExecutor.java │ │ ├── MutableAudioFrame.java │ │ ├── NonAllocatingAudioFrameBuffer.java │ │ ├── PrimordialAudioTrackExecutor.java │ │ ├── ReferenceMutableAudioFrame.java │ │ └── TerminatorAudioFrame.java │ └── resources │ └── certificates │ ├── bundled.txt │ └── dst-root-ca-x3.jks ├── natives-publish ├── build.gradle.kts └── src │ └── main │ └── resources │ └── natives │ ├── darwin │ └── libconnector.dylib │ ├── linux-aarch32 │ └── libconnector.so │ ├── linux-aarch64 │ └── libconnector.so │ ├── linux-arm │ └── libconnector.so │ ├── linux-armhf │ └── libconnector.so │ ├── linux-musl-aarch64 │ └── libconnector.so │ ├── linux-musl-x86-64 │ └── libconnector.so │ ├── linux-x86-64 │ └── libconnector.so │ ├── linux-x86 │ └── libconnector.so │ ├── win-x86-64 │ ├── connector.dll │ └── libmpg123-0.dll │ └── win-x86 │ ├── connector.dll │ └── libmpg123-0.dll ├── natives ├── CMakeLists.txt ├── build.gradle ├── connector │ ├── CMakeLists.txt │ ├── connector.h │ ├── fdk-aac.c │ ├── linux │ │ └── statistics.c │ ├── mpg123.c │ ├── opus.c │ ├── samplerate.c │ ├── vorbis.c │ └── win │ │ └── statistics.c ├── fdk-aac │ └── CMakeLists.txt ├── natives.gradle ├── samplerate │ ├── CMakeLists.txt │ └── config.h └── vorbis │ └── CMakeLists.txt ├── settings.gradle.kts └── testbot ├── build.gradle.kts └── src └── main └── java └── com └── sedmelluq └── discord └── lavaplayer └── demo └── LocalPlayerDemo.java /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | indent_size = 4 6 | end_of_line = lf 7 | charset = utf-8 8 | trim_trailing_whitespace = true 9 | insert_final_newline = true 10 | 11 | [*.java] 12 | ij_java_names_count_to_use_import_on_demand = 5 13 | ij_java_class_count_to_use_import_on_demand = 5 14 | -------------------------------------------------------------------------------- /.git-blame-ignore-revs: -------------------------------------------------------------------------------- 1 | # Reformat code to 4 space indents 2 | 9565a0d600a7ab8e302b840b93d2d4e05de7b573 3 | 4 | # Reformat missed code to 4 space indents 5 | 2d4aa92ef49d27aad40c50ddc454844cf58d7ebe 6 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .gradle 2 | build/ 3 | out/ 4 | 5 | # Ignore Gradle GUI config 6 | gradle-app.setting 7 | 8 | # Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored) 9 | !gradle-wrapper.jar 10 | 11 | # Cache of project 12 | .gradletasknamecache 13 | 14 | # # Work around https://youtrack.jetbrains.com/issue/IDEA-116898 15 | # gradle/wrapper/gradle-wrapper.properties 16 | 17 | .idea/ 18 | jebml/src 19 | *.iml 20 | 21 | **/resources/natives 22 | natives/dist 23 | natives/fdk-aac/** 24 | !natives/fdk-aac/CMakeLists.txt 25 | natives/libs 26 | natives/mp3/mpg* 27 | natives/opus/opus* 28 | natives/samplerate/src 29 | natives/vorbis/*/ 30 | udpqueue-natives/dist 31 | version.txt 32 | 33 | *.log 34 | *.json 35 | *.project 36 | *.html 37 | -------------------------------------------------------------------------------- /common/build.gradle.kts: -------------------------------------------------------------------------------- 1 | import com.vanniktech.maven.publish.JavaLibrary 2 | import com.vanniktech.maven.publish.JavadocJar 3 | 4 | plugins { 5 | `java-library` 6 | alias(libs.plugins.maven.publish.base) 7 | } 8 | 9 | base { 10 | archivesName = "lava-common" 11 | } 12 | 13 | dependencies { 14 | implementation(libs.slf4j) 15 | implementation(libs.commons.io) 16 | } 17 | 18 | mavenPublishing { 19 | configure(JavaLibrary(JavadocJar.Javadoc())) 20 | } 21 | -------------------------------------------------------------------------------- /common/src/main/java/com/sedmelluq/lava/common/natives/NativeLibraryBinaryProvider.java: -------------------------------------------------------------------------------- 1 | package com.sedmelluq.lava.common.natives; 2 | 3 | import com.sedmelluq.lava.common.natives.architecture.SystemType; 4 | 5 | import java.io.InputStream; 6 | 7 | public interface NativeLibraryBinaryProvider { 8 | /** 9 | * @param systemType Detected system type. 10 | * @param libraryName Name of the library to load. 11 | * @return Stream to the library binary. null causes failure. 12 | */ 13 | InputStream getLibraryStream(SystemType systemType, String libraryName); 14 | } 15 | -------------------------------------------------------------------------------- /common/src/main/java/com/sedmelluq/lava/common/natives/NativeResourceHolder.java: -------------------------------------------------------------------------------- 1 | package com.sedmelluq.lava.common.natives; 2 | 3 | import org.slf4j.Logger; 4 | import org.slf4j.LoggerFactory; 5 | 6 | import java.util.concurrent.atomic.AtomicBoolean; 7 | 8 | /** 9 | * Abstract instance of a class which holds native resources that must be freed. 10 | */ 11 | public abstract class NativeResourceHolder { 12 | private static final Logger log = LoggerFactory.getLogger(NativeResourceHolder.class); 13 | 14 | private final AtomicBoolean released = new AtomicBoolean(); 15 | 16 | /** 17 | * Assert that the native resources have not been freed. 18 | */ 19 | protected void checkNotReleased() { 20 | if (released.get()) { 21 | throw new IllegalStateException("Cannot use the decoder after closing it."); 22 | } 23 | } 24 | 25 | /** 26 | * Free up native resources of the decoder. Using other methods after this will throw IllegalStateException. 27 | */ 28 | public void close() { 29 | closeInternal(false); 30 | } 31 | 32 | /** 33 | * Free the native resources. 34 | */ 35 | protected abstract void freeResources(); 36 | 37 | private synchronized void closeInternal(boolean inFinalizer) { 38 | if (released.compareAndSet(false, true)) { 39 | if (inFinalizer) { 40 | log.warn("Should have been closed before finalization ({}).", this.getClass().getName()); 41 | } 42 | 43 | freeResources(); 44 | } 45 | } 46 | 47 | @Override 48 | protected void finalize() throws Throwable { 49 | super.finalize(); 50 | closeInternal(true); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /common/src/main/java/com/sedmelluq/lava/common/natives/ResourceNativeLibraryBinaryProvider.java: -------------------------------------------------------------------------------- 1 | package com.sedmelluq.lava.common.natives; 2 | 3 | import com.sedmelluq.lava.common.natives.architecture.SystemType; 4 | import org.slf4j.Logger; 5 | import org.slf4j.LoggerFactory; 6 | 7 | import java.io.InputStream; 8 | 9 | public class ResourceNativeLibraryBinaryProvider implements NativeLibraryBinaryProvider { 10 | private static final Logger log = LoggerFactory.getLogger(ResourceNativeLibraryBinaryProvider.class); 11 | 12 | private final Class classLoaderSample; 13 | private final String nativesRoot; 14 | 15 | public ResourceNativeLibraryBinaryProvider(Class classLoaderSample, String nativesRoot) { 16 | this.classLoaderSample = classLoaderSample != null ? classLoaderSample : ResourceNativeLibraryBinaryProvider.class; 17 | this.nativesRoot = nativesRoot; 18 | } 19 | 20 | @Override 21 | public InputStream getLibraryStream(SystemType systemType, String libraryName) { 22 | String resourcePath = nativesRoot + systemType.formatSystemName() + "/" + systemType.formatLibraryName(libraryName); 23 | 24 | log.debug("Native library {}: trying to find from resources at {} with {} as classloader reference", libraryName, 25 | resourcePath, classLoaderSample.getName()); 26 | 27 | return classLoaderSample.getResourceAsStream(resourcePath); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /common/src/main/java/com/sedmelluq/lava/common/natives/SystemNativeLibraryProperties.java: -------------------------------------------------------------------------------- 1 | package com.sedmelluq.lava.common.natives; 2 | 3 | public class SystemNativeLibraryProperties implements NativeLibraryProperties { 4 | private final String libraryName; 5 | private final String propertyPrefix; 6 | 7 | public SystemNativeLibraryProperties(String libraryName, String propertyPrefix) { 8 | this.libraryName = libraryName; 9 | this.propertyPrefix = propertyPrefix; 10 | } 11 | 12 | @Override 13 | public String getLibraryPath() { 14 | return get("path"); 15 | } 16 | 17 | @Override 18 | public String getLibraryDirectory() { 19 | return get("dir"); 20 | } 21 | 22 | @Override 23 | public String getExtractionPath() { 24 | return get("extractPath"); 25 | } 26 | 27 | @Override 28 | public String getSystemName() { 29 | return get("system"); 30 | } 31 | 32 | @Override 33 | public String getArchitectureName() { 34 | return get("arch"); 35 | } 36 | 37 | @Override 38 | public String getLibraryFileNamePrefix() { 39 | return get("libPrefix"); 40 | } 41 | 42 | @Override 43 | public String getLibraryFileNameSuffix() { 44 | return get("libSuffix"); 45 | } 46 | 47 | private String get(String property) { 48 | return System.getProperty( 49 | propertyPrefix + libraryName + "." + property, 50 | System.getProperty(propertyPrefix + property) 51 | ); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /common/src/main/java/com/sedmelluq/lava/common/natives/architecture/ArchitectureType.java: -------------------------------------------------------------------------------- 1 | package com.sedmelluq.lava.common.natives.architecture; 2 | 3 | public interface ArchitectureType { 4 | /** 5 | * @return Identifier used in directory names (combined with OS identifier) for this ABI 6 | */ 7 | String identifier(); 8 | } 9 | -------------------------------------------------------------------------------- /common/src/main/java/com/sedmelluq/lava/common/natives/architecture/OperatingSystemType.java: -------------------------------------------------------------------------------- 1 | package com.sedmelluq.lava.common.natives.architecture; 2 | 3 | public interface OperatingSystemType { 4 | /** 5 | * @return Identifier used in directory names (combined with architecture) for this OS 6 | */ 7 | String identifier(); 8 | 9 | /** 10 | * @return Prefix used for library file names. lib on most Unix flavors. 11 | */ 12 | String libraryFilePrefix(); 13 | 14 | /** 15 | * @return Suffix (extension) used for library file names. 16 | */ 17 | String libraryFileSuffix(); 18 | } 19 | -------------------------------------------------------------------------------- /demo-d4j/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'java' 3 | id 'application' 4 | } 5 | 6 | repositories { 7 | jcenter() 8 | maven { 9 | url 'https://jitpack.io' 10 | } 11 | } 12 | 13 | ext.d4jVersion = '3.0.11' 14 | 15 | dependencies { 16 | compile "com.discord4j:discord4j-core:$d4jVersion" 17 | compile "com.discord4j:discord4j-voice:$d4jVersion" 18 | compile 'com.sedmelluq:lavaplayer:1.3.49' 19 | runtime 'ch.qos.logback:logback-classic:1.2.3' 20 | } 21 | 22 | mainClassName = 'com.sedmelluq.discord.lavaplayer.demo.d4j.Main' 23 | -------------------------------------------------------------------------------- /demo-d4j/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lavalink-devs/lavaplayer/fbcc546cb6cb64ea2a7062259e67d612e20e7d1e/demo-d4j/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /demo-d4j/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Sun Dec 18 00:27:39 EET 2016 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-3.2.1-bin.zip 7 | -------------------------------------------------------------------------------- /demo-d4j/src/main/java/com/sedmelluq/discord/lavaplayer/demo/d4j/D4jAudioProvider.java: -------------------------------------------------------------------------------- 1 | package com.sedmelluq.discord.lavaplayer.demo.d4j; 2 | 3 | import com.sedmelluq.discord.lavaplayer.format.StandardAudioDataFormats; 4 | import com.sedmelluq.discord.lavaplayer.player.AudioPlayer; 5 | import com.sedmelluq.discord.lavaplayer.track.playback.MutableAudioFrame; 6 | import discord4j.voice.AudioProvider; 7 | 8 | import java.nio.ByteBuffer; 9 | 10 | /** 11 | * This is a wrapper around AudioPlayer which makes it behave as an IAudioProvider for D4J. As D4J calls canProvide 12 | * before every call to provide(), we pull the frame in canProvide() and use the frame we already pulled in 13 | * provide(). 14 | */ 15 | public class D4jAudioProvider extends AudioProvider { 16 | private final MutableAudioFrame frame = new MutableAudioFrame(); 17 | private final AudioPlayer player; 18 | 19 | public D4jAudioProvider(AudioPlayer player) { 20 | super(ByteBuffer.allocate(StandardAudioDataFormats.DISCORD_OPUS.maximumChunkSize())); 21 | this.player = player; 22 | this.frame.setBuffer(getBuffer()); 23 | } 24 | 25 | @Override 26 | public boolean provide() { 27 | boolean didProvide = player.provide(frame); 28 | if (didProvide) getBuffer().flip(); 29 | return didProvide; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /demo-d4j/src/main/java/com/sedmelluq/discord/lavaplayer/demo/d4j/GuildMusicManager.java: -------------------------------------------------------------------------------- 1 | package com.sedmelluq.discord.lavaplayer.demo.d4j; 2 | 3 | import com.sedmelluq.discord.lavaplayer.player.AudioPlayer; 4 | import com.sedmelluq.discord.lavaplayer.player.AudioPlayerManager; 5 | 6 | /** 7 | * Holder for both the player and a track scheduler for one guild. 8 | */ 9 | public class GuildMusicManager { 10 | /** 11 | * Audio player for the guild. 12 | */ 13 | public final AudioPlayer player; 14 | /** 15 | * Track scheduler for the player. 16 | */ 17 | public final TrackScheduler scheduler; 18 | 19 | public final D4jAudioProvider provider; 20 | 21 | /** 22 | * Creates a player and a track scheduler. 23 | * 24 | * @param manager Audio player manager to use for creating the player. 25 | */ 26 | public GuildMusicManager(AudioPlayerManager manager) { 27 | player = manager.createPlayer(); 28 | scheduler = new TrackScheduler(player); 29 | player.addListener(scheduler); 30 | provider = new D4jAudioProvider(player); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /demo-jda/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'java' 3 | id 'application' 4 | } 5 | 6 | repositories { 7 | jcenter() 8 | } 9 | 10 | dependencies { 11 | compile 'net.dv8tion:JDA:4.0.0_68' 12 | compile 'com.sedmelluq:lavaplayer:1.3.49' 13 | runtime 'ch.qos.logback:logback-classic:1.2.3' 14 | } 15 | 16 | mainClassName = 'com.sedmelluq.discord.lavaplayer.demo.jda.Main' 17 | -------------------------------------------------------------------------------- /demo-jda/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lavalink-devs/lavaplayer/fbcc546cb6cb64ea2a7062259e67d612e20e7d1e/demo-jda/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /demo-jda/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Sun Dec 18 00:27:39 EET 2016 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-3.2.1-bin.zip 7 | -------------------------------------------------------------------------------- /demo-jda/src/main/java/com/sedmelluq/discord/lavaplayer/demo/jda/AudioPlayerSendHandler.java: -------------------------------------------------------------------------------- 1 | package com.sedmelluq.discord.lavaplayer.demo.jda; 2 | 3 | import com.sedmelluq.discord.lavaplayer.player.AudioPlayer; 4 | import com.sedmelluq.discord.lavaplayer.track.playback.MutableAudioFrame; 5 | 6 | import java.nio.Buffer; 7 | 8 | import net.dv8tion.jda.api.audio.AudioSendHandler; 9 | 10 | import java.nio.ByteBuffer; 11 | 12 | /** 13 | * This is a wrapper around AudioPlayer which makes it behave as an AudioSendHandler for JDA. As JDA calls canProvide 14 | * before every call to provide20MsAudio(), we pull the frame in canProvide() and use the frame we already pulled in 15 | * provide20MsAudio(). 16 | */ 17 | public class AudioPlayerSendHandler implements AudioSendHandler { 18 | private final AudioPlayer audioPlayer; 19 | private final ByteBuffer buffer; 20 | private final MutableAudioFrame frame; 21 | 22 | /** 23 | * @param audioPlayer Audio player to wrap. 24 | */ 25 | public AudioPlayerSendHandler(AudioPlayer audioPlayer) { 26 | this.audioPlayer = audioPlayer; 27 | this.buffer = ByteBuffer.allocate(1024); 28 | this.frame = new MutableAudioFrame(); 29 | this.frame.setBuffer(buffer); 30 | } 31 | 32 | @Override 33 | public boolean canProvide() { 34 | // returns true if audio was provided 35 | return audioPlayer.provide(frame); 36 | } 37 | 38 | @Override 39 | public ByteBuffer provide20MsAudio() { 40 | // flip to make it a read buffer 41 | ((Buffer) buffer).flip(); 42 | return buffer; 43 | } 44 | 45 | @Override 46 | public boolean isOpus() { 47 | return true; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /demo-jda/src/main/java/com/sedmelluq/discord/lavaplayer/demo/jda/GuildMusicManager.java: -------------------------------------------------------------------------------- 1 | package com.sedmelluq.discord.lavaplayer.demo.jda; 2 | 3 | import com.sedmelluq.discord.lavaplayer.player.AudioPlayer; 4 | import com.sedmelluq.discord.lavaplayer.player.AudioPlayerManager; 5 | 6 | /** 7 | * Holder for both the player and a track scheduler for one guild. 8 | */ 9 | public class GuildMusicManager { 10 | /** 11 | * Audio player for the guild. 12 | */ 13 | public final AudioPlayer player; 14 | /** 15 | * Track scheduler for the player. 16 | */ 17 | public final TrackScheduler scheduler; 18 | 19 | /** 20 | * Creates a player and a track scheduler. 21 | * 22 | * @param manager Audio player manager to use for creating the player. 23 | */ 24 | public GuildMusicManager(AudioPlayerManager manager) { 25 | player = manager.createPlayer(); 26 | scheduler = new TrackScheduler(player); 27 | player.addListener(scheduler); 28 | } 29 | 30 | /** 31 | * @return Wrapper around AudioPlayer to use it as an AudioSendHandler. 32 | */ 33 | public AudioPlayerSendHandler getSendHandler() { 34 | return new AudioPlayerSendHandler(player); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /extensions/format-xm/build.gradle.kts: -------------------------------------------------------------------------------- 1 | import com.vanniktech.maven.publish.JavaLibrary 2 | import com.vanniktech.maven.publish.JavadocJar 3 | 4 | plugins { 5 | `java-library` 6 | alias(libs.plugins.maven.publish.base) 7 | } 8 | base { 9 | archivesName = "lavaplayer-ext-format-xm" 10 | } 11 | 12 | dependencies { 13 | compileOnly(projects.main) 14 | implementation(libs.ibxm.fork) 15 | implementation(libs.slf4j) 16 | } 17 | 18 | mavenPublishing { 19 | configure(JavaLibrary(JavadocJar.Javadoc())) 20 | } 21 | -------------------------------------------------------------------------------- /extensions/format-xm/src/main/java/com/sedmelluq/lavaplayer/extensions/format/xm/XmAudioTrack.java: -------------------------------------------------------------------------------- 1 | package com.sedmelluq.lavaplayer.extensions.format.xm; 2 | 3 | import com.sedmelluq.discord.lavaplayer.container.wav.WavAudioTrack; 4 | import com.sedmelluq.discord.lavaplayer.tools.io.SeekableInputStream; 5 | import com.sedmelluq.discord.lavaplayer.track.AudioTrackInfo; 6 | import com.sedmelluq.discord.lavaplayer.track.BaseAudioTrack; 7 | import com.sedmelluq.discord.lavaplayer.track.playback.LocalAudioTrackExecutor; 8 | import org.slf4j.Logger; 9 | import org.slf4j.LoggerFactory; 10 | 11 | public class XmAudioTrack extends BaseAudioTrack { 12 | private static final Logger log = LoggerFactory.getLogger(WavAudioTrack.class); 13 | 14 | private final SeekableInputStream inputStream; 15 | 16 | /** 17 | * @param trackInfo Track info 18 | * @param inputStream Input stream for the WAV file 19 | */ 20 | public XmAudioTrack(AudioTrackInfo trackInfo, SeekableInputStream inputStream) { 21 | super(trackInfo); 22 | 23 | this.inputStream = inputStream; 24 | } 25 | 26 | @Override 27 | public void process(LocalAudioTrackExecutor localExecutor) throws Exception { 28 | XmTrackProvider trackProvider = new XmFileLoader(inputStream).loadTrack(localExecutor.getProcessingContext()); 29 | 30 | try { 31 | log.debug("Starting to play module {}", getIdentifier()); 32 | localExecutor.executeProcessingLoop(trackProvider::provideFrames, null); 33 | } finally { 34 | trackProvider.close(); 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /extensions/format-xm/src/main/java/com/sedmelluq/lavaplayer/extensions/format/xm/XmFileLoader.java: -------------------------------------------------------------------------------- 1 | package com.sedmelluq.lavaplayer.extensions.format.xm; 2 | 3 | import com.sedmelluq.discord.lavaplayer.tools.io.SeekableInputStream; 4 | import com.sedmelluq.discord.lavaplayer.track.playback.AudioProcessingContext; 5 | import ibxm.Channel; 6 | import ibxm.IBXM; 7 | import ibxm.Module; 8 | 9 | import java.io.IOException; 10 | 11 | public class XmFileLoader { 12 | private final SeekableInputStream inputStream; 13 | 14 | public XmFileLoader(SeekableInputStream inputStream) { 15 | this.inputStream = inputStream; 16 | } 17 | 18 | public XmTrackProvider loadTrack(AudioProcessingContext context) throws IOException { 19 | Module module = new Module(inputStream); 20 | IBXM ibxm = new IBXM(module, context.outputFormat.sampleRate); 21 | ibxm.setInterpolation(Channel.SINC); 22 | return new XmTrackProvider(context, ibxm); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /extensions/format-xm/src/main/java/com/sedmelluq/lavaplayer/extensions/format/xm/XmTrackProvider.java: -------------------------------------------------------------------------------- 1 | package com.sedmelluq.lavaplayer.extensions.format.xm; 2 | 3 | import com.sedmelluq.discord.lavaplayer.filter.AudioPipeline; 4 | import com.sedmelluq.discord.lavaplayer.filter.AudioPipelineFactory; 5 | import com.sedmelluq.discord.lavaplayer.filter.PcmFormat; 6 | import com.sedmelluq.discord.lavaplayer.track.playback.AudioProcessingContext; 7 | import ibxm.IBXM; 8 | 9 | public class XmTrackProvider { 10 | private final IBXM ibxm; 11 | private final AudioPipeline downstream; 12 | private final int blocksInBuffer; 13 | 14 | public XmTrackProvider(AudioProcessingContext context, IBXM ibxm) { 15 | this.ibxm = ibxm; 16 | this.downstream = AudioPipelineFactory.create(context, new PcmFormat(2, ibxm.getSampleRate())); 17 | this.blocksInBuffer = ibxm.getMixBufferLength(); 18 | } 19 | 20 | public void provideFrames() throws InterruptedException { 21 | int blockCount; 22 | int[] buffer = new int[blocksInBuffer]; 23 | short[] shortBuffer = new short[blocksInBuffer]; 24 | 25 | while ((blockCount = ibxm.getAudio(buffer)) > 0) { 26 | for (int i = 0; i < blocksInBuffer; i++) { 27 | shortBuffer[i] = (short) Math.max(-32678, Math.min(buffer[i], 32767)); 28 | } 29 | 30 | downstream.process(shortBuffer, 0, blockCount * 2); 31 | } 32 | } 33 | 34 | public void close() { 35 | downstream.close(); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /extensions/youtube-rotator/build.gradle.kts: -------------------------------------------------------------------------------- 1 | import com.vanniktech.maven.publish.JavaLibrary 2 | import com.vanniktech.maven.publish.JavadocJar 3 | 4 | plugins { 5 | `java-library` 6 | alias(libs.plugins.maven.publish.base) 7 | } 8 | 9 | base { 10 | archivesName = "lavaplayer-ext-youtube-rotator" 11 | } 12 | 13 | dependencies { 14 | compileOnly(projects.main) 15 | implementation(libs.slf4j) 16 | } 17 | 18 | mavenPublishing { 19 | configure(JavaLibrary(JavadocJar.Javadoc())) 20 | } 21 | -------------------------------------------------------------------------------- /extensions/youtube-rotator/src/main/java/com/sedmelluq/lava/extensions/youtuberotator/YoutubeIpRotatorRetryHandler.java: -------------------------------------------------------------------------------- 1 | package com.sedmelluq.lava.extensions.youtuberotator; 2 | 3 | import org.apache.http.client.HttpRequestRetryHandler; 4 | import org.apache.http.impl.client.DefaultHttpRequestRetryHandler; 5 | import org.apache.http.protocol.HttpContext; 6 | 7 | import java.io.IOException; 8 | import java.net.BindException; 9 | import java.net.SocketException; 10 | 11 | public class YoutubeIpRotatorRetryHandler implements HttpRequestRetryHandler { 12 | @Override 13 | public boolean retryRequest(IOException exception, int executionCount, HttpContext context) { 14 | if (exception instanceof BindException) { 15 | return false; 16 | } else if (exception instanceof SocketException) { 17 | String message = exception.getMessage(); 18 | 19 | if (message != null && message.contains("Protocol family unavailable")) { 20 | return false; 21 | } 22 | } 23 | 24 | return DefaultHttpRequestRetryHandler.INSTANCE.retryRequest(exception, executionCount, context); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /extensions/youtube-rotator/src/main/java/com/sedmelluq/lava/extensions/youtuberotator/tools/BigRandom.java: -------------------------------------------------------------------------------- 1 | package com.sedmelluq.lava.extensions.youtuberotator.tools; 2 | 3 | import java.math.BigInteger; 4 | import java.util.Random; 5 | 6 | public final class BigRandom extends Random { 7 | 8 | public BigInteger nextBigInt(int bits) { 9 | if (bits < 32) { 10 | return BigInteger.valueOf(next(31)); 11 | } 12 | BigInteger value = BigInteger.ZERO; 13 | int index = 0; 14 | while (bits >= 32) { 15 | bits -= 32; 16 | value = value.add(BigInteger.valueOf(next(32)).shiftLeft((index++) * 32)); 17 | } 18 | if (bits > 0) 19 | value = value.add(BigInteger.valueOf(next(bits)).shiftLeft(index * 32)); 20 | return value; 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /extensions/youtube-rotator/src/main/java/com/sedmelluq/lava/extensions/youtuberotator/tools/RateLimitException.java: -------------------------------------------------------------------------------- 1 | package com.sedmelluq.lava.extensions.youtuberotator.tools; 2 | 3 | public final class RateLimitException extends RuntimeException { 4 | 5 | public RateLimitException() { 6 | super(); 7 | } 8 | 9 | public RateLimitException(final String message) { 10 | super(message); 11 | } 12 | 13 | public RateLimitException(final String message, final Throwable cause) { 14 | super(message, cause); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /extensions/youtube-rotator/src/main/java/com/sedmelluq/lava/extensions/youtuberotator/tools/Tuple.java: -------------------------------------------------------------------------------- 1 | package com.sedmelluq.lava.extensions.youtuberotator.tools; 2 | 3 | public class Tuple { 4 | public final L l; 5 | public final R r; 6 | 7 | public Tuple(L l, R r) { 8 | this.l = l; 9 | this.r = r; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /extensions/youtube-rotator/src/main/java/com/sedmelluq/lava/extensions/youtuberotator/tools/ip/IpAddressTools.java: -------------------------------------------------------------------------------- 1 | package com.sedmelluq.lava.extensions.youtuberotator.tools.ip; 2 | 3 | import com.sedmelluq.lava.extensions.youtuberotator.tools.Tuple; 4 | import org.apache.http.HttpException; 5 | import org.apache.http.HttpHost; 6 | 7 | import java.net.Inet4Address; 8 | import java.net.Inet6Address; 9 | import java.net.InetAddress; 10 | import java.net.UnknownHostException; 11 | import java.util.*; 12 | 13 | public final class IpAddressTools { 14 | 15 | private static final Random RANDOM = new Random(); 16 | 17 | public static Tuple getRandomAddressesFromHost(final HttpHost host) throws HttpException { 18 | final List ipList; 19 | try { 20 | ipList = Arrays.asList(InetAddress.getAllByName(host.getHostName())); 21 | } catch (final UnknownHostException e) { 22 | throw new HttpException("Could not resolve " + host.getHostName(), e); 23 | } 24 | final List ip6 = new ArrayList<>(); 25 | final List ip4 = new ArrayList<>(); 26 | 27 | Collections.reverse(ipList); 28 | for (final InetAddress ip : ipList) { 29 | if (ip instanceof Inet6Address) 30 | ip6.add((Inet6Address) ip); 31 | else if (ip instanceof Inet4Address) 32 | ip4.add((Inet4Address) ip); 33 | } 34 | return new Tuple<>(getRandomFromList(ip4), getRandomFromList(ip6)); 35 | } 36 | 37 | public static T getRandomFromList(final List list) { 38 | if (list.isEmpty()) 39 | return null; 40 | if (list.size() == 1) 41 | return list.get(0); 42 | return list.get(RANDOM.nextInt(list.size())); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /extensions/youtube-rotator/src/main/java/com/sedmelluq/lava/extensions/youtuberotator/tools/ip/IpBlock.java: -------------------------------------------------------------------------------- 1 | package com.sedmelluq.lava.extensions.youtuberotator.tools.ip; 2 | 3 | import java.math.BigInteger; 4 | import java.net.InetAddress; 5 | 6 | public abstract class IpBlock { 7 | 8 | public abstract I getRandomAddress(); 9 | 10 | public I getAddressAtIndex(long index) { 11 | return getAddressAtIndex(BigInteger.valueOf(index)); 12 | } 13 | 14 | public I getAddressAtIndex(BigInteger index) { 15 | return getAddressAtIndex(index.longValue()); 16 | } 17 | 18 | public abstract Class getType(); 19 | 20 | public abstract BigInteger getSize(); 21 | 22 | public abstract int getMaskBits(); 23 | } 24 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lavalink-devs/lavaplayer/fbcc546cb6cb64ea2a7062259e67d612e20e7d1e/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.1-bin.zip 4 | networkTimeout=10000 5 | validateDistributionUrl=true 6 | zipStoreBase=GRADLE_USER_HOME 7 | zipStorePath=wrapper/dists 8 | -------------------------------------------------------------------------------- /jitpack.yml: -------------------------------------------------------------------------------- 1 | install: 2 | - ./gradlew clean build publishToMavenLocal 3 | jdk: 4 | - openjdk11 5 | -------------------------------------------------------------------------------- /main/build.gradle.kts: -------------------------------------------------------------------------------- 1 | import com.vanniktech.maven.publish.JavaLibrary 2 | import com.vanniktech.maven.publish.JavadocJar 3 | import kotlin.io.path.Path 4 | import kotlin.io.path.createDirectories 5 | import kotlin.io.path.writeText 6 | 7 | plugins { 8 | `java-library` 9 | alias(libs.plugins.maven.publish.base) 10 | } 11 | 12 | base { 13 | archivesName = "lavaplayer" 14 | } 15 | 16 | dependencies { 17 | api(projects.common) 18 | implementation(projects.nativesPublish) 19 | implementation(libs.rhino.engine) 20 | implementation(libs.slf4j) 21 | 22 | api(libs.httpclient) 23 | implementation(libs.commons.io) 24 | 25 | api(libs.jackson.core) 26 | api(libs.jackson.databind) 27 | 28 | implementation(libs.jsoup) 29 | implementation(libs.base64) 30 | implementation(libs.json) 31 | 32 | implementation(libs.intellij.annotations) 33 | 34 | testImplementation(libs.groovy) 35 | testImplementation(libs.spock.core) 36 | testImplementation(libs.logback.classic) 37 | } 38 | 39 | tasks { 40 | val updateVersion by registering { 41 | val output = "$buildDir/resources/main/com/sedmelluq/discord/lavaplayer/tools/version.txt" 42 | inputs.property("version", version) 43 | outputs.file(output) 44 | 45 | doLast { 46 | Path(output).let { 47 | it.parent.createDirectories() 48 | it.writeText(version.toString()) 49 | } 50 | } 51 | } 52 | 53 | classes { 54 | dependsOn(updateVersion) 55 | } 56 | } 57 | 58 | mavenPublishing { 59 | configure(JavaLibrary(JavadocJar.Javadoc())) 60 | } 61 | -------------------------------------------------------------------------------- /main/src/main/java/com/sedmelluq/discord/lavaplayer/container/Formats.java: -------------------------------------------------------------------------------- 1 | package com.sedmelluq.discord.lavaplayer.container; 2 | 3 | /** 4 | * Standard mime types and codec names for supported formats. 5 | */ 6 | public class Formats { 7 | public static final String MIME_AUDIO_WEBM = "audio/webm"; 8 | public static final String MIME_VIDEO_WEBM = "video/webm"; 9 | public static final String MIME_AUDIO_MP4 = "audio/mp4"; 10 | public static final String MIME_VIDEO_MP4 = "video/mp4"; 11 | 12 | public static final String CODEC_OPUS = "opus"; 13 | public static final String CODEC_VORBIS = "vorbis"; 14 | public static final String CODEC_AAC_LC = "mp4a.40.2"; 15 | } 16 | -------------------------------------------------------------------------------- /main/src/main/java/com/sedmelluq/discord/lavaplayer/container/MediaContainerDescriptor.java: -------------------------------------------------------------------------------- 1 | package com.sedmelluq.discord.lavaplayer.container; 2 | 3 | import com.sedmelluq.discord.lavaplayer.tools.io.SeekableInputStream; 4 | import com.sedmelluq.discord.lavaplayer.track.AudioTrack; 5 | import com.sedmelluq.discord.lavaplayer.track.AudioTrackInfo; 6 | 7 | public class MediaContainerDescriptor { 8 | public final MediaContainerProbe probe; 9 | public final String parameters; 10 | 11 | public MediaContainerDescriptor(MediaContainerProbe probe, String parameters) { 12 | this.probe = probe; 13 | this.parameters = parameters; 14 | } 15 | 16 | public AudioTrack createTrack(AudioTrackInfo trackInfo, SeekableInputStream inputStream) { 17 | return probe.createTrack(parameters, trackInfo, inputStream); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /main/src/main/java/com/sedmelluq/discord/lavaplayer/container/MediaContainerHints.java: -------------------------------------------------------------------------------- 1 | package com.sedmelluq.discord.lavaplayer.container; 2 | 3 | /** 4 | * Optional meta-information about a stream which may narrow down the list of possible containers. 5 | */ 6 | public class MediaContainerHints { 7 | private static final MediaContainerHints NO_INFORMATION = new MediaContainerHints(null, null); 8 | 9 | /** 10 | * Mime type, null if not known. 11 | */ 12 | public final String mimeType; 13 | /** 14 | * File extension, null if not known. 15 | */ 16 | public final String fileExtension; 17 | 18 | private MediaContainerHints(String mimeType, String fileExtension) { 19 | this.mimeType = mimeType; 20 | this.fileExtension = fileExtension; 21 | } 22 | 23 | /** 24 | * @return true if any hint parameters have a value. 25 | */ 26 | public boolean present() { 27 | return mimeType != null || fileExtension != null; 28 | } 29 | 30 | /** 31 | * @param mimeType Mime type 32 | * @param fileExtension File extension 33 | * @return Instance of hints object with the specified parameters 34 | */ 35 | public static MediaContainerHints from(String mimeType, String fileExtension) { 36 | if (mimeType == null && fileExtension == null) { 37 | return NO_INFORMATION; 38 | } else { 39 | return new MediaContainerHints(mimeType, fileExtension); 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /main/src/main/java/com/sedmelluq/discord/lavaplayer/container/MediaContainerRegistry.java: -------------------------------------------------------------------------------- 1 | package com.sedmelluq.discord.lavaplayer.container; 2 | 3 | import java.util.List; 4 | 5 | public class MediaContainerRegistry { 6 | public static final MediaContainerRegistry DEFAULT_REGISTRY = new MediaContainerRegistry(MediaContainer.asList()); 7 | 8 | private final List probes; 9 | 10 | public MediaContainerRegistry(List probes) { 11 | this.probes = probes; 12 | } 13 | 14 | public MediaContainerProbe find(String name) { 15 | for (MediaContainerProbe probe : probes) { 16 | if (name.equals(probe.getName())) { 17 | return probe; 18 | } 19 | } 20 | 21 | return null; 22 | } 23 | 24 | public List getAll() { 25 | return probes; 26 | } 27 | 28 | public static MediaContainerRegistry extended(MediaContainerProbe... additional) { 29 | List probes = MediaContainer.asList(); 30 | 31 | for (MediaContainerProbe probe : additional) { 32 | probes.add(probe); 33 | } 34 | 35 | return new MediaContainerRegistry(probes); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /main/src/main/java/com/sedmelluq/discord/lavaplayer/container/adts/AdtsAudioTrack.java: -------------------------------------------------------------------------------- 1 | package com.sedmelluq.discord.lavaplayer.container.adts; 2 | 3 | import com.sedmelluq.discord.lavaplayer.track.AudioTrackInfo; 4 | import com.sedmelluq.discord.lavaplayer.track.BaseAudioTrack; 5 | import com.sedmelluq.discord.lavaplayer.track.playback.LocalAudioTrackExecutor; 6 | import org.slf4j.Logger; 7 | import org.slf4j.LoggerFactory; 8 | 9 | import java.io.InputStream; 10 | 11 | /** 12 | * Audio track that handles an ADTS packet stream 13 | */ 14 | public class AdtsAudioTrack extends BaseAudioTrack { 15 | private static final Logger log = LoggerFactory.getLogger(AdtsAudioTrack.class); 16 | 17 | private final InputStream inputStream; 18 | 19 | /** 20 | * @param trackInfo Track info 21 | * @param inputStream Input stream for the ADTS stream 22 | */ 23 | public AdtsAudioTrack(AudioTrackInfo trackInfo, InputStream inputStream) { 24 | super(trackInfo); 25 | 26 | this.inputStream = inputStream; 27 | } 28 | 29 | @Override 30 | public void process(LocalAudioTrackExecutor localExecutor) throws Exception { 31 | AdtsStreamProvider provider = new AdtsStreamProvider(inputStream, localExecutor.getProcessingContext()); 32 | 33 | try { 34 | log.debug("Starting to play ADTS stream {}", getIdentifier()); 35 | 36 | localExecutor.executeProcessingLoop(provider::provideFrames, null); 37 | } finally { 38 | provider.close(); 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /main/src/main/java/com/sedmelluq/discord/lavaplayer/container/flac/FlacAudioTrack.java: -------------------------------------------------------------------------------- 1 | package com.sedmelluq.discord.lavaplayer.container.flac; 2 | 3 | import com.sedmelluq.discord.lavaplayer.tools.io.SeekableInputStream; 4 | import com.sedmelluq.discord.lavaplayer.track.AudioTrackInfo; 5 | import com.sedmelluq.discord.lavaplayer.track.BaseAudioTrack; 6 | import com.sedmelluq.discord.lavaplayer.track.playback.LocalAudioTrackExecutor; 7 | import org.slf4j.Logger; 8 | import org.slf4j.LoggerFactory; 9 | 10 | /** 11 | * Audio track that handles a FLAC stream 12 | */ 13 | public class FlacAudioTrack extends BaseAudioTrack { 14 | private static final Logger log = LoggerFactory.getLogger(FlacAudioTrack.class); 15 | 16 | private final SeekableInputStream inputStream; 17 | 18 | /** 19 | * @param trackInfo Track info 20 | * @param inputStream Input stream for the FLAC file 21 | */ 22 | public FlacAudioTrack(AudioTrackInfo trackInfo, SeekableInputStream inputStream) { 23 | super(trackInfo); 24 | 25 | this.inputStream = inputStream; 26 | } 27 | 28 | @Override 29 | public void process(LocalAudioTrackExecutor localExecutor) throws Exception { 30 | FlacFileLoader file = new FlacFileLoader(inputStream); 31 | FlacTrackProvider trackProvider = file.loadTrack(localExecutor.getProcessingContext()); 32 | 33 | try { 34 | log.debug("Starting to play FLAC track {}", getIdentifier()); 35 | localExecutor.executeProcessingLoop(trackProvider::provideFrames, trackProvider::seekToTimecode); 36 | } finally { 37 | trackProvider.close(); 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /main/src/main/java/com/sedmelluq/discord/lavaplayer/container/flac/FlacMetadataHeader.java: -------------------------------------------------------------------------------- 1 | package com.sedmelluq.discord.lavaplayer.container.flac; 2 | 3 | import com.sedmelluq.discord.lavaplayer.tools.io.BitBufferReader; 4 | 5 | import java.nio.ByteBuffer; 6 | 7 | /** 8 | * A header of FLAC metadata. 9 | */ 10 | public class FlacMetadataHeader { 11 | public static final int LENGTH = 4; 12 | 13 | public static final int BLOCK_SEEKTABLE = 3; 14 | public static final int BLOCK_COMMENT = 4; 15 | 16 | /** 17 | * If this header is for the last metadata block. If this is true, then the current metadata block is followed by 18 | * frames. 19 | */ 20 | public final boolean isLastBlock; 21 | 22 | /** 23 | * Block type, see: https://xiph.org/flac/format.html#metadata_block_header 24 | */ 25 | public final int blockType; 26 | 27 | /** 28 | * Length of the block, current header excluded 29 | */ 30 | public final int blockLength; 31 | 32 | /** 33 | * @param data The raw header data 34 | */ 35 | public FlacMetadataHeader(byte[] data) { 36 | BitBufferReader bitReader = new BitBufferReader(ByteBuffer.wrap(data)); 37 | isLastBlock = bitReader.asInteger(1) == 1; 38 | blockType = bitReader.asInteger(7); 39 | blockLength = bitReader.asInteger(24); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /main/src/main/java/com/sedmelluq/discord/lavaplayer/container/flac/FlacSeekPoint.java: -------------------------------------------------------------------------------- 1 | package com.sedmelluq.discord.lavaplayer.container.flac; 2 | 3 | /** 4 | * FLAC seek point info. Field descriptions are from: 5 | * https://xiph.org/flac/format.html#seekpoint 6 | *

7 | * - For placeholder points, the second and third field values are undefined. 8 | * - Seek points within a table must be sorted in ascending order by sample number. 9 | * - Seek points within a table must be unique by sample number, with the exception of placeholder points. 10 | * - The previous two notes imply that there may be any number of placeholder points, but they must all occur at the end 11 | * of the table. 12 | */ 13 | public class FlacSeekPoint { 14 | public static final int LENGTH = 18; 15 | 16 | /** 17 | * Sample number of first sample in the target frame, or 0xFFFFFFFFFFFFFFFF for a placeholder point. 18 | */ 19 | public final long sampleIndex; 20 | 21 | /** 22 | * Offset (in bytes) from the first byte of the first frame header to the first byte of the target frame's header. 23 | */ 24 | public final long byteOffset; 25 | 26 | /** 27 | * Number of samples in the target frame. 28 | */ 29 | public final int sampleCount; 30 | 31 | /** 32 | * @param sampleIndex Index of the first sample in the frame 33 | * @param byteOffset Offset in bytes from first frame start to target frame start 34 | * @param sampleCount Number of samples in the frame 35 | */ 36 | public FlacSeekPoint(long sampleIndex, long byteOffset, int sampleCount) { 37 | this.sampleIndex = sampleIndex; 38 | this.byteOffset = byteOffset; 39 | this.sampleCount = sampleCount; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /main/src/main/java/com/sedmelluq/discord/lavaplayer/container/flac/frame/FlacFrameInfo.java: -------------------------------------------------------------------------------- 1 | package com.sedmelluq.discord.lavaplayer.container.flac.frame; 2 | 3 | /** 4 | * Information of a FLAC frame that is required for reading its subframes. Most of the fields in the frame info are not 5 | * actually needed, since it is an error if they differ from the ones specified in the file metadata. 6 | */ 7 | public class FlacFrameInfo { 8 | /** 9 | * Number of samples in each subframe of this frame. 10 | */ 11 | public final int sampleCount; 12 | 13 | /** 14 | * The way stereo channel data is related. With stereo frames, one channel can contain its original data and the other 15 | * just the difference from the first one, which allows for better compression for the other channel. 16 | */ 17 | public final ChannelDelta channelDelta; 18 | 19 | /** 20 | * @param sampleCount Number of samples in each subframe of this frame 21 | * @param channelDelta Channel data delta setting 22 | */ 23 | public FlacFrameInfo(int sampleCount, ChannelDelta channelDelta) { 24 | this.sampleCount = sampleCount; 25 | this.channelDelta = channelDelta; 26 | } 27 | 28 | /** 29 | * The relationship between stereo channels. 30 | */ 31 | public enum ChannelDelta { 32 | NONE(-1), 33 | LEFT_SIDE(1), 34 | RIGHT_SIDE(0), 35 | MID_SIDE(1); 36 | 37 | /** 38 | * The index of the channel containing delta values. 39 | */ 40 | public final int deltaChannel; 41 | 42 | ChannelDelta(int deltaChannel) { 43 | this.deltaChannel = deltaChannel; 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /main/src/main/java/com/sedmelluq/discord/lavaplayer/container/matroska/MatroskaOpusTrackConsumer.java: -------------------------------------------------------------------------------- 1 | package com.sedmelluq.discord.lavaplayer.container.matroska; 2 | 3 | import com.sedmelluq.discord.lavaplayer.container.common.OpusPacketRouter; 4 | import com.sedmelluq.discord.lavaplayer.container.matroska.format.MatroskaFileTrack; 5 | import com.sedmelluq.discord.lavaplayer.track.playback.AudioProcessingContext; 6 | 7 | import java.nio.ByteBuffer; 8 | 9 | /** 10 | * Consumes OPUS track data from a matroska file. 11 | */ 12 | public class MatroskaOpusTrackConsumer implements MatroskaTrackConsumer { 13 | private final MatroskaFileTrack track; 14 | private final OpusPacketRouter opusPacketRouter; 15 | 16 | /** 17 | * @param context Configuration and output information for processing 18 | * @param track The associated matroska track 19 | */ 20 | public MatroskaOpusTrackConsumer(AudioProcessingContext context, MatroskaFileTrack track) { 21 | this.track = track; 22 | this.opusPacketRouter = new OpusPacketRouter(context, (int) track.audio.samplingFrequency, track.audio.channels); 23 | } 24 | 25 | @Override 26 | public MatroskaFileTrack getTrack() { 27 | return track; 28 | } 29 | 30 | @Override 31 | public void initialise() { 32 | // Nothing to do here 33 | } 34 | 35 | @Override 36 | public void seekPerformed(long requestedTimecode, long providedTimecode) { 37 | opusPacketRouter.seekPerformed(requestedTimecode, providedTimecode); 38 | } 39 | 40 | @Override 41 | public void flush() throws InterruptedException { 42 | opusPacketRouter.flush(); 43 | } 44 | 45 | @Override 46 | public void consume(ByteBuffer data) throws InterruptedException { 47 | opusPacketRouter.process(data); 48 | } 49 | 50 | @Override 51 | public void close() { 52 | opusPacketRouter.close(); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /main/src/main/java/com/sedmelluq/discord/lavaplayer/container/matroska/MatroskaTrackConsumer.java: -------------------------------------------------------------------------------- 1 | package com.sedmelluq.discord.lavaplayer.container.matroska; 2 | 3 | import com.sedmelluq.discord.lavaplayer.container.matroska.format.MatroskaFileTrack; 4 | 5 | import java.nio.ByteBuffer; 6 | 7 | /** 8 | * Consumer for the file frames of a specific matroska file track 9 | */ 10 | public interface MatroskaTrackConsumer extends AutoCloseable { 11 | /** 12 | * @return The associated matroska file track 13 | */ 14 | MatroskaFileTrack getTrack(); 15 | 16 | /** 17 | * Initialise the consumer, called before first consume() 18 | */ 19 | void initialise(); 20 | 21 | /** 22 | * Indicates that the next frame is not a direct continuation of the previous one 23 | * 24 | * @param requestedTimecode Timecode in milliseconds to which the seek was requested to 25 | * @param providedTimecode Timecode in milliseconds to which the seek was actually performed to 26 | */ 27 | void seekPerformed(long requestedTimecode, long providedTimecode); 28 | 29 | /** 30 | * Indicates that no more input will come, all remaining buffers should be flushed 31 | * 32 | * @throws InterruptedException When interrupted externally (or for seek/stop). 33 | */ 34 | void flush() throws InterruptedException; 35 | 36 | /** 37 | * Consume one frame from the track 38 | * 39 | * @param data The data of the frame 40 | * @throws InterruptedException When interrupted externally (or for seek/stop). 41 | */ 42 | void consume(ByteBuffer data) throws InterruptedException; 43 | 44 | /** 45 | * Already flushed, no more input coming. Free all resources. 46 | */ 47 | @Override 48 | void close() throws Exception; 49 | } 50 | -------------------------------------------------------------------------------- /main/src/main/java/com/sedmelluq/discord/lavaplayer/container/matroska/format/MatroskaBlock.java: -------------------------------------------------------------------------------- 1 | package com.sedmelluq.discord.lavaplayer.container.matroska.format; 2 | 3 | import java.io.IOException; 4 | import java.nio.ByteBuffer; 5 | 6 | /** 7 | * Provides information and buffer to read from for a Matroska block. 8 | */ 9 | public interface MatroskaBlock { 10 | /** 11 | * @return The timecode of this block relative to its cluster 12 | */ 13 | int getTimecode(); 14 | 15 | /** 16 | * @return The track number which this block is for 17 | */ 18 | int getTrackNumber(); 19 | 20 | /** 21 | * @return Whether this block is a keyframe 22 | */ 23 | boolean isKeyFrame(); 24 | 25 | /** 26 | * @return The number of frames in this block 27 | */ 28 | int getFrameCount(); 29 | 30 | /** 31 | * The reader must already be positioned at the frame that is to be read next. 32 | * 33 | * @param reader The reader to use to read the block contents into a buffer. 34 | * @param index The index of the frame to get the buffer for. 35 | * @return A buffer where the range between position and limit contains the data for the specific frame. The contents 36 | * of this buffer are only valid until the next call to this method. 37 | * @throws IOException On read error. 38 | */ 39 | ByteBuffer getNextFrameBuffer(MatroskaFileReader reader, int index) throws IOException; 40 | } 41 | -------------------------------------------------------------------------------- /main/src/main/java/com/sedmelluq/discord/lavaplayer/container/matroska/format/MatroskaCuePoint.java: -------------------------------------------------------------------------------- 1 | package com.sedmelluq.discord.lavaplayer.container.matroska.format; 2 | 3 | /** 4 | * Matroska file cue point. Provides the offsets at a specific timecode for each track 5 | */ 6 | public class MatroskaCuePoint { 7 | /** 8 | * Timecode using the file timescale 9 | */ 10 | public final long timecode; 11 | /** 12 | * Absolute offset to the cluster 13 | */ 14 | public final long[] trackClusterOffsets; 15 | 16 | /** 17 | * @param timecode Timecode using the file timescale 18 | * @param trackClusterOffsets Absolute offset to the cluster 19 | */ 20 | public MatroskaCuePoint(long timecode, long[] trackClusterOffsets) { 21 | this.timecode = timecode; 22 | this.trackClusterOffsets = trackClusterOffsets; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /main/src/main/java/com/sedmelluq/discord/lavaplayer/container/matroska/format/MutableMatroskaElement.java: -------------------------------------------------------------------------------- 1 | package com.sedmelluq.discord.lavaplayer.container.matroska.format; 2 | 3 | /** 4 | * Mutable instance of {@link MatroskaElement} for reducing allocation rate during parsing. 5 | */ 6 | public class MutableMatroskaElement extends MatroskaElement { 7 | protected MutableMatroskaElement(int level) { 8 | super(level); 9 | } 10 | 11 | public void setId(long id) { 12 | this.id = id; 13 | } 14 | 15 | public void setType(MatroskaElementType type) { 16 | this.type = type; 17 | } 18 | 19 | public void setPosition(long position) { 20 | this.position = position; 21 | } 22 | 23 | public void setHeaderSize(int headerSize) { 24 | this.headerSize = headerSize; 25 | } 26 | 27 | public void setDataSize(int dataSize) { 28 | this.dataSize = dataSize; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /main/src/main/java/com/sedmelluq/discord/lavaplayer/container/mp3/Mp3AudioTrack.java: -------------------------------------------------------------------------------- 1 | package com.sedmelluq.discord.lavaplayer.container.mp3; 2 | 3 | import com.sedmelluq.discord.lavaplayer.tools.io.SeekableInputStream; 4 | import com.sedmelluq.discord.lavaplayer.track.AudioTrackInfo; 5 | import com.sedmelluq.discord.lavaplayer.track.BaseAudioTrack; 6 | import com.sedmelluq.discord.lavaplayer.track.playback.LocalAudioTrackExecutor; 7 | import org.slf4j.Logger; 8 | import org.slf4j.LoggerFactory; 9 | 10 | /** 11 | * Audio track that handles an MP3 stream 12 | */ 13 | public class Mp3AudioTrack extends BaseAudioTrack { 14 | private static final Logger log = LoggerFactory.getLogger(Mp3AudioTrack.class); 15 | 16 | private final SeekableInputStream inputStream; 17 | 18 | /** 19 | * @param trackInfo Track info 20 | * @param inputStream Input stream for the MP3 file 21 | */ 22 | public Mp3AudioTrack(AudioTrackInfo trackInfo, SeekableInputStream inputStream) { 23 | super(trackInfo); 24 | 25 | this.inputStream = inputStream; 26 | } 27 | 28 | @Override 29 | public void process(LocalAudioTrackExecutor localExecutor) throws Exception { 30 | Mp3TrackProvider provider = new Mp3TrackProvider(localExecutor.getProcessingContext(), inputStream); 31 | 32 | try { 33 | provider.parseHeaders(); 34 | 35 | log.debug("Starting to play MP3 track {}", getIdentifier()); 36 | localExecutor.executeProcessingLoop(provider::provideFrames, provider::seekToTimecode); 37 | } finally { 38 | provider.close(); 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /main/src/main/java/com/sedmelluq/discord/lavaplayer/container/mp3/Mp3Seeker.java: -------------------------------------------------------------------------------- 1 | package com.sedmelluq.discord.lavaplayer.container.mp3; 2 | 3 | import com.sedmelluq.discord.lavaplayer.tools.io.SeekableInputStream; 4 | 5 | import java.io.IOException; 6 | 7 | /** 8 | * A seeking handler for MP3 files. 9 | */ 10 | public interface Mp3Seeker { 11 | /** 12 | * @return The duration of the file in milliseconds. May be an estimate. 13 | */ 14 | long getDuration(); 15 | 16 | /** 17 | * @return True if the track is seekable. 18 | */ 19 | boolean isSeekable(); 20 | 21 | /** 22 | * @param timecode The timecode that the seek is requested to 23 | * @param inputStream The input stream to perform the seek on 24 | * @return The index of the frame that the seek was performed to 25 | * @throws IOException On IO error 26 | */ 27 | long seekAndGetFrameIndex(long timecode, SeekableInputStream inputStream) throws IOException; 28 | } 29 | -------------------------------------------------------------------------------- /main/src/main/java/com/sedmelluq/discord/lavaplayer/container/mp3/Mp3StreamSeeker.java: -------------------------------------------------------------------------------- 1 | package com.sedmelluq.discord.lavaplayer.container.mp3; 2 | 3 | import com.sedmelluq.discord.lavaplayer.tools.Units; 4 | import com.sedmelluq.discord.lavaplayer.tools.io.SeekableInputStream; 5 | 6 | import java.io.IOException; 7 | 8 | /** 9 | * Seeker for an MP3 stream, which actually does not allow seeking and reports UnitConstants.DURATION_MS_UNKNOWN as 10 | * duration. 11 | */ 12 | public class Mp3StreamSeeker implements Mp3Seeker { 13 | @Override 14 | public long getDuration() { 15 | return Units.DURATION_MS_UNKNOWN; 16 | } 17 | 18 | @Override 19 | public boolean isSeekable() { 20 | return false; 21 | } 22 | 23 | @Override 24 | public long seekAndGetFrameIndex(long timecode, SeekableInputStream inputStream) throws IOException { 25 | throw new UnsupportedOperationException("Cannot seek on a stream."); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /main/src/main/java/com/sedmelluq/discord/lavaplayer/container/mpeg/MpegNoopTrackConsumer.java: -------------------------------------------------------------------------------- 1 | package com.sedmelluq.discord.lavaplayer.container.mpeg; 2 | 3 | import java.nio.channels.ReadableByteChannel; 4 | 5 | /** 6 | * No-op MP4 track consumer, for probing purposes. 7 | */ 8 | public class MpegNoopTrackConsumer implements MpegTrackConsumer { 9 | private final MpegTrackInfo trackInfo; 10 | 11 | /** 12 | * @param trackInfo Track info. 13 | */ 14 | public MpegNoopTrackConsumer(MpegTrackInfo trackInfo) { 15 | this.trackInfo = trackInfo; 16 | } 17 | 18 | @Override 19 | public MpegTrackInfo getTrack() { 20 | return trackInfo; 21 | } 22 | 23 | @Override 24 | public void initialise() { 25 | // Nothing to do 26 | } 27 | 28 | @Override 29 | public void seekPerformed(long requestedTimecode, long providedTimecode) { 30 | // Nothing to do 31 | } 32 | 33 | @Override 34 | public void flush() throws InterruptedException { 35 | // Nothing to do 36 | } 37 | 38 | @Override 39 | public void consume(ReadableByteChannel channel, int length) throws InterruptedException { 40 | // Nothing to do 41 | } 42 | 43 | @Override 44 | public void close() { 45 | // Nothing to do 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /main/src/main/java/com/sedmelluq/discord/lavaplayer/container/mpeg/MpegTrackConsumer.java: -------------------------------------------------------------------------------- 1 | package com.sedmelluq.discord.lavaplayer.container.mpeg; 2 | 3 | import java.nio.channels.ReadableByteChannel; 4 | 5 | /** 6 | * Consumer for the data of one MP4 track 7 | */ 8 | public interface MpegTrackConsumer { 9 | /** 10 | * @return The associated MP4 track 11 | */ 12 | MpegTrackInfo getTrack(); 13 | 14 | /** 15 | * Initialise the consumer, called before first consume() 16 | */ 17 | void initialise(); 18 | 19 | /** 20 | * Indicates that the next frame is not a direct continuation of the previous one 21 | * 22 | * @param requestedTimecode Timecode in milliseconds to which the seek was requested to 23 | * @param providedTimecode Timecode in milliseconds to which the seek was actually performed to 24 | */ 25 | void seekPerformed(long requestedTimecode, long providedTimecode); 26 | 27 | /** 28 | * Indicates that no more input is coming. Flush any buffers to output. 29 | * 30 | * @throws InterruptedException When interrupted externally (or for seek/stop). 31 | */ 32 | void flush() throws InterruptedException; 33 | 34 | /** 35 | * Consume one chunk from the track 36 | * 37 | * @param channel Byte channel to consume from 38 | * @param length Lenth of the chunk in bytes 39 | * @throws InterruptedException When interrupted externally (or for seek/stop). 40 | */ 41 | void consume(ReadableByteChannel channel, int length) throws InterruptedException; 42 | 43 | /** 44 | * Free all resources 45 | */ 46 | void close(); 47 | } 48 | -------------------------------------------------------------------------------- /main/src/main/java/com/sedmelluq/discord/lavaplayer/container/mpeg/reader/MpegFileTrackProvider.java: -------------------------------------------------------------------------------- 1 | package com.sedmelluq.discord.lavaplayer.container.mpeg.reader; 2 | 3 | import com.sedmelluq.discord.lavaplayer.container.mpeg.MpegTrackConsumer; 4 | 5 | import java.io.IOException; 6 | 7 | /** 8 | * Track provider for a type of MP4 file. 9 | */ 10 | public interface MpegFileTrackProvider { 11 | /** 12 | * @param trackConsumer Track consumer which defines the track this will provide and the consumer for packets. 13 | * @return Returns true if it had enough information for initialisation. 14 | */ 15 | boolean initialise(MpegTrackConsumer trackConsumer); 16 | 17 | /** 18 | * @return Total duration of the file in milliseconds 19 | */ 20 | long getDuration(); 21 | 22 | /** 23 | * Provide audio frames to the frame consumer until the end of the track or interruption. 24 | * 25 | * @throws InterruptedException When interrupted externally (or for seek/stop). 26 | * @throws IOException When network exception is happened, currently only throw from MpegFragmentedFileTrackProvider. 27 | */ 28 | void provideFrames() throws InterruptedException, IOException; 29 | 30 | /** 31 | * Perform a seek to the given timecode (ms). On the next call to provideFrames, the seekPerformed method of frame 32 | * consumer is called with the position where it actually seeked to and the position where the seek was requested to 33 | * as arguments. 34 | * 35 | * @param timecode The timecode to seek to in milliseconds 36 | */ 37 | void seekToTimecode(long timecode); 38 | } 39 | -------------------------------------------------------------------------------- /main/src/main/java/com/sedmelluq/discord/lavaplayer/container/mpeg/reader/MpegParseStopChecker.java: -------------------------------------------------------------------------------- 1 | package com.sedmelluq.discord.lavaplayer.container.mpeg.reader; 2 | 3 | /** 4 | * Stop checker which is called before and after parsing each section in an MP4 file to check if parsing should be 5 | * stopped. 6 | */ 7 | public interface MpegParseStopChecker { 8 | /** 9 | * @param child Section before or after which this is called. 10 | * @param start Whether this is called before (true) or after (false). 11 | * @return True to stop, false to continue. 12 | */ 13 | boolean check(MpegSectionInfo child, boolean start); 14 | } 15 | -------------------------------------------------------------------------------- /main/src/main/java/com/sedmelluq/discord/lavaplayer/container/mpeg/reader/MpegSectionHandler.java: -------------------------------------------------------------------------------- 1 | package com.sedmelluq.discord.lavaplayer.container.mpeg.reader; 2 | 3 | import java.io.IOException; 4 | 5 | /** 6 | * Handles one MPEG section which has no version info 7 | */ 8 | public interface MpegSectionHandler { 9 | /** 10 | * @param child The section 11 | * @throws IOException On read error 12 | */ 13 | void handle(MpegSectionInfo child) throws IOException; 14 | } 15 | -------------------------------------------------------------------------------- /main/src/main/java/com/sedmelluq/discord/lavaplayer/container/mpeg/reader/MpegSectionInfo.java: -------------------------------------------------------------------------------- 1 | package com.sedmelluq.discord.lavaplayer.container.mpeg.reader; 2 | 3 | /** 4 | * Information for one MP4 section (aka box) 5 | */ 6 | public class MpegSectionInfo { 7 | /** 8 | * Absolute offset of the section 9 | */ 10 | public final long offset; 11 | /** 12 | * Length of the section 13 | */ 14 | public final long length; 15 | /** 16 | * Type (fourCC) of the section 17 | */ 18 | public final String type; 19 | 20 | /** 21 | * @param offset Absolute offset of the section 22 | * @param length Length of the section 23 | * @param type Type (fourCC) of the section 24 | */ 25 | public MpegSectionInfo(long offset, long length, String type) { 26 | this.offset = offset; 27 | this.length = length; 28 | this.type = type; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /main/src/main/java/com/sedmelluq/discord/lavaplayer/container/mpeg/reader/MpegVersionedSectionHandler.java: -------------------------------------------------------------------------------- 1 | package com.sedmelluq.discord.lavaplayer.container.mpeg.reader; 2 | 3 | import java.io.IOException; 4 | 5 | /** 6 | * Handles one MPEG section which has version info 7 | */ 8 | public interface MpegVersionedSectionHandler { 9 | /** 10 | * @param child The versioned section 11 | * @throws IOException On read error 12 | */ 13 | void handle(MpegVersionedSectionInfo child) throws IOException; 14 | } 15 | -------------------------------------------------------------------------------- /main/src/main/java/com/sedmelluq/discord/lavaplayer/container/mpeg/reader/MpegVersionedSectionInfo.java: -------------------------------------------------------------------------------- 1 | package com.sedmelluq.discord.lavaplayer.container.mpeg.reader; 2 | 3 | /** 4 | * Information for one MP4 section (aka box) including version and flags 5 | */ 6 | public class MpegVersionedSectionInfo extends MpegSectionInfo { 7 | /** 8 | * Version of the section 9 | */ 10 | public final int version; 11 | /** 12 | * Flags of the section 13 | */ 14 | public final int flags; 15 | 16 | /** 17 | * @param sectionInfo Basic info for the section 18 | * @param version Version of the section 19 | * @param flags Flags of the section 20 | */ 21 | public MpegVersionedSectionInfo(MpegSectionInfo sectionInfo, int version, int flags) { 22 | super(sectionInfo.offset, sectionInfo.length, sectionInfo.type); 23 | 24 | this.version = version; 25 | this.flags = flags; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /main/src/main/java/com/sedmelluq/discord/lavaplayer/container/mpeg/reader/fragmented/MpegGlobalSeekInfo.java: -------------------------------------------------------------------------------- 1 | package com.sedmelluq.discord.lavaplayer.container.mpeg.reader.fragmented; 2 | 3 | /** 4 | * Describes the seek info for a fragmented MP4 file 5 | */ 6 | public class MpegGlobalSeekInfo { 7 | /** 8 | * The value of the internal timecodes that corresponds to one second 9 | */ 10 | public final int timescale; 11 | /** 12 | * Size and duration information for each segment 13 | */ 14 | public final MpegSegmentEntry[] entries; 15 | /** 16 | * Absolute timecode offset of each segment 17 | */ 18 | public final long[] timeOffsets; 19 | /** 20 | * Absolute file offset of each segment 21 | */ 22 | public final long[] fileOffsets; 23 | 24 | /** 25 | * @param timescale The value of the internal timecodes that corresponds to one second 26 | * @param baseOffset The file offset of the first segment 27 | * @param entries Size and duration information for each segment 28 | */ 29 | public MpegGlobalSeekInfo(int timescale, long baseOffset, MpegSegmentEntry[] entries) { 30 | this.timescale = timescale; 31 | this.entries = entries; 32 | 33 | timeOffsets = new long[entries.length]; 34 | fileOffsets = new long[entries.length]; 35 | fileOffsets[0] = baseOffset; 36 | 37 | for (int i = 1; i < entries.length; i++) { 38 | timeOffsets[i] = timeOffsets[i - 1] + entries[i - 1].duration; 39 | fileOffsets[i] = fileOffsets[i - 1] + entries[i - 1].size; 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /main/src/main/java/com/sedmelluq/discord/lavaplayer/container/mpeg/reader/fragmented/MpegSegmentEntry.java: -------------------------------------------------------------------------------- 1 | package com.sedmelluq.discord.lavaplayer.container.mpeg.reader.fragmented; 2 | 3 | /** 4 | * Information about one MP4 segment aka fragment 5 | */ 6 | public class MpegSegmentEntry { 7 | /** 8 | * Type of the segment 9 | */ 10 | public final int type; 11 | /** 12 | * Size in bytes 13 | */ 14 | public final int size; 15 | /** 16 | * Duration using the timescale of the file 17 | */ 18 | public final int duration; 19 | 20 | /** 21 | * @param type Type of the segment 22 | * @param size Size in bytes 23 | * @param duration Duration using the timescale of the file 24 | */ 25 | public MpegSegmentEntry(int type, int size, int duration) { 26 | this.type = type; 27 | this.size = size; 28 | this.duration = duration; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /main/src/main/java/com/sedmelluq/discord/lavaplayer/container/mpegts/MpegAdtsAudioTrack.java: -------------------------------------------------------------------------------- 1 | package com.sedmelluq.discord.lavaplayer.container.mpegts; 2 | 3 | import com.sedmelluq.discord.lavaplayer.container.adts.AdtsAudioTrack; 4 | import com.sedmelluq.discord.lavaplayer.track.AudioTrackInfo; 5 | import com.sedmelluq.discord.lavaplayer.track.DelegatedAudioTrack; 6 | import com.sedmelluq.discord.lavaplayer.track.playback.LocalAudioTrackExecutor; 7 | 8 | import java.io.InputStream; 9 | 10 | import static com.sedmelluq.discord.lavaplayer.container.mpegts.MpegTsElementaryInputStream.ADTS_ELEMENTARY_STREAM; 11 | 12 | public class MpegAdtsAudioTrack extends DelegatedAudioTrack { 13 | private final InputStream inputStream; 14 | 15 | /** 16 | * @param trackInfo Track info 17 | */ 18 | public MpegAdtsAudioTrack(AudioTrackInfo trackInfo, InputStream inputStream) { 19 | super(trackInfo); 20 | 21 | this.inputStream = inputStream; 22 | } 23 | 24 | @Override 25 | public void process(LocalAudioTrackExecutor executor) throws Exception { 26 | MpegTsElementaryInputStream elementaryInputStream = new MpegTsElementaryInputStream(inputStream, ADTS_ELEMENTARY_STREAM); 27 | PesPacketInputStream pesPacketInputStream = new PesPacketInputStream(elementaryInputStream); 28 | processDelegate(new AdtsAudioTrack(trackInfo, pesPacketInputStream), executor); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /main/src/main/java/com/sedmelluq/discord/lavaplayer/container/ogg/OggCodecHandler.java: -------------------------------------------------------------------------------- 1 | package com.sedmelluq.discord.lavaplayer.container.ogg; 2 | 3 | import com.sedmelluq.discord.lavaplayer.tools.io.DirectBufferStreamBroker; 4 | 5 | import java.io.IOException; 6 | 7 | public interface OggCodecHandler { 8 | boolean isMatchingIdentifier(int identifier); 9 | 10 | int getMaximumFirstPacketLength(); 11 | 12 | OggTrackBlueprint loadBlueprint(OggPacketInputStream stream, DirectBufferStreamBroker broker) throws IOException; 13 | 14 | OggMetadata loadMetadata(OggPacketInputStream stream, DirectBufferStreamBroker broker) throws IOException; 15 | } 16 | -------------------------------------------------------------------------------- /main/src/main/java/com/sedmelluq/discord/lavaplayer/container/ogg/OggMetadata.java: -------------------------------------------------------------------------------- 1 | package com.sedmelluq.discord.lavaplayer.container.ogg; 2 | 3 | import com.sedmelluq.discord.lavaplayer.tools.Units; 4 | import com.sedmelluq.discord.lavaplayer.track.info.AudioTrackInfoProvider; 5 | 6 | import java.util.Collections; 7 | import java.util.Map; 8 | 9 | /** 10 | * Audio track info provider based on OGG metadata map. 11 | */ 12 | public class OggMetadata implements AudioTrackInfoProvider { 13 | public static final OggMetadata EMPTY = new OggMetadata(Collections.emptyMap(), Units.DURATION_MS_UNKNOWN); 14 | 15 | private static final String TITLE_FIELD = "TITLE"; 16 | private static final String ARTIST_FIELD = "ARTIST"; 17 | private static final String ISRC_FIELD = "ISRC"; 18 | 19 | private final Map tags; 20 | private final Long length; 21 | 22 | /** 23 | * @param tags Map of OGG metadata with OGG-specific keys. 24 | */ 25 | public OggMetadata(Map tags, Long length) { 26 | this.tags = tags; 27 | this.length = length; 28 | } 29 | 30 | @Override 31 | public String getTitle() { 32 | return tags.get(TITLE_FIELD); 33 | } 34 | 35 | @Override 36 | public String getAuthor() { 37 | return tags.get(ARTIST_FIELD); 38 | } 39 | 40 | @Override 41 | public Long getLength() { 42 | return length; 43 | } 44 | 45 | @Override 46 | public String getIdentifier() { 47 | return null; 48 | } 49 | 50 | @Override 51 | public String getUri() { 52 | return null; 53 | } 54 | 55 | @Override 56 | public String getArtworkUrl() { 57 | return null; 58 | } 59 | 60 | @Override 61 | public String getISRC() { 62 | return tags.get(ISRC_FIELD); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /main/src/main/java/com/sedmelluq/discord/lavaplayer/container/ogg/OggSeekPoint.java: -------------------------------------------------------------------------------- 1 | package com.sedmelluq.discord.lavaplayer.container.ogg; 2 | 3 | public class OggSeekPoint { 4 | private final long position; 5 | private final long granulePosition; 6 | private final long timecode; 7 | private final long pageSequence; 8 | 9 | /** 10 | * @param position The position of the seek point in the stream, in bytes. 11 | * @param granulePosition The granule position of the seek point in the stream. 12 | * @param timecode The time of the seek point in the stream, in milliseconds. 13 | * @param pageSequence The page to what this seek point belong. 14 | */ 15 | public OggSeekPoint(long position, long granulePosition, long timecode, long pageSequence) { 16 | this.position = position; 17 | this.granulePosition = granulePosition; 18 | this.timecode = timecode; 19 | this.pageSequence = pageSequence; 20 | } 21 | 22 | /** 23 | * @return The position of the seek point in the stream, in bytes. 24 | */ 25 | public long getPosition() { 26 | return position; 27 | } 28 | 29 | /** 30 | * @return The granule position of the seek point in the stream. 31 | */ 32 | public long getGranulePosition() { 33 | return granulePosition; 34 | } 35 | 36 | /** 37 | * @return The timecode of the seek point in the stream, in milliseconds. 38 | */ 39 | public long getTimecode() { 40 | return timecode; 41 | } 42 | 43 | /** 44 | * @return The page to what this seek point belong. 45 | */ 46 | public long getPageSequence() { 47 | return pageSequence; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /main/src/main/java/com/sedmelluq/discord/lavaplayer/container/ogg/OggStreamSizeInfo.java: -------------------------------------------------------------------------------- 1 | package com.sedmelluq.discord.lavaplayer.container.ogg; 2 | 3 | /** 4 | * Describes the size information of an OGG stream. 5 | */ 6 | public class OggStreamSizeInfo { 7 | /** 8 | * Total number of bytes in the stream. 9 | */ 10 | public final long totalBytes; 11 | /** 12 | * Total number of samples in the stream. 13 | */ 14 | public final long totalSamples; 15 | /** 16 | * Absolute offset of the first page in the stream. 17 | */ 18 | public final long firstPageOffset; 19 | /** 20 | * Absolute offset of the last page in the stream. 21 | */ 22 | public final long lastPageOffset; 23 | /** 24 | * Sample rate of the track in this stream, useful for calculating duration in milliseconds. 25 | */ 26 | public final int sampleRate; 27 | 28 | /** 29 | * @param totalBytes See {@link #totalBytes}. 30 | * @param totalSamples See {@link #totalSamples}. 31 | * @param firstPageOffset See {@link #firstPageOffset}. 32 | * @param lastPageOffset See {@link #lastPageOffset}. 33 | * @param sampleRate See {@link #sampleRate}. 34 | */ 35 | public OggStreamSizeInfo(long totalBytes, long totalSamples, long firstPageOffset, long lastPageOffset, 36 | int sampleRate) { 37 | 38 | this.totalBytes = totalBytes; 39 | this.totalSamples = totalSamples; 40 | this.firstPageOffset = firstPageOffset; 41 | this.lastPageOffset = lastPageOffset; 42 | this.sampleRate = sampleRate; 43 | } 44 | 45 | /** 46 | * @return Duration calculated from size info in milliseconds (rounded down). 47 | */ 48 | public long getDuration() { 49 | return totalSamples * 1000 / sampleRate; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /main/src/main/java/com/sedmelluq/discord/lavaplayer/container/ogg/OggTrackBlueprint.java: -------------------------------------------------------------------------------- 1 | package com.sedmelluq.discord.lavaplayer.container.ogg; 2 | 3 | public interface OggTrackBlueprint { 4 | OggTrackHandler loadTrackHandler(OggPacketInputStream stream); 5 | 6 | int getSampleRate(); 7 | } 8 | -------------------------------------------------------------------------------- /main/src/main/java/com/sedmelluq/discord/lavaplayer/container/ogg/OggTrackHandler.java: -------------------------------------------------------------------------------- 1 | package com.sedmelluq.discord.lavaplayer.container.ogg; 2 | 3 | import com.sedmelluq.discord.lavaplayer.track.playback.AudioProcessingContext; 4 | 5 | import java.io.Closeable; 6 | import java.io.IOException; 7 | 8 | /** 9 | * A handler for a specific codec for an OGG stream. 10 | */ 11 | public interface OggTrackHandler extends Closeable { 12 | /** 13 | * Initialises the track stream. 14 | * 15 | * @param context Configuration and output information for processing 16 | * @throws IOException On read error. 17 | */ 18 | void initialise(AudioProcessingContext context, long timecode, long desiredTimecode) throws IOException; 19 | 20 | /** 21 | * Decodes audio frames and sends them to frame consumer. 22 | * 23 | * @throws InterruptedException When interrupted externally (or for seek/stop). 24 | */ 25 | void provideFrames() throws InterruptedException; 26 | 27 | /** 28 | * Seeks to the specified timecode. 29 | * 30 | * @param timecode The timecode in milliseconds 31 | */ 32 | void seekToTimecode(long timecode); 33 | } 34 | -------------------------------------------------------------------------------- /main/src/main/java/com/sedmelluq/discord/lavaplayer/container/playlists/HlsStreamSegment.java: -------------------------------------------------------------------------------- 1 | package com.sedmelluq.discord.lavaplayer.container.playlists; 2 | 3 | public class HlsStreamSegment { 4 | /** 5 | * URL of the segment. 6 | */ 7 | public final String url; 8 | /** 9 | * Duration of the segment in milliseconds. null if unknown. 10 | */ 11 | public final Long duration; 12 | /** 13 | * Name of the segment. null if unknown. 14 | */ 15 | public final String name; 16 | 17 | public HlsStreamSegment(String url, Long duration, String name) { 18 | this.url = url; 19 | this.duration = duration; 20 | this.name = name; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /main/src/main/java/com/sedmelluq/discord/lavaplayer/container/playlists/HlsStreamTrack.java: -------------------------------------------------------------------------------- 1 | package com.sedmelluq.discord.lavaplayer.container.playlists; 2 | 3 | import com.sedmelluq.discord.lavaplayer.source.stream.M3uStreamSegmentUrlProvider; 4 | import com.sedmelluq.discord.lavaplayer.source.stream.MpegTsM3uStreamAudioTrack; 5 | import com.sedmelluq.discord.lavaplayer.tools.io.HttpInterface; 6 | import com.sedmelluq.discord.lavaplayer.tools.io.HttpInterfaceManager; 7 | import com.sedmelluq.discord.lavaplayer.track.AudioTrackInfo; 8 | 9 | public class HlsStreamTrack extends MpegTsM3uStreamAudioTrack { 10 | private final HlsStreamSegmentUrlProvider segmentUrlProvider; 11 | private final HttpInterfaceManager httpInterfaceManager; 12 | 13 | /** 14 | * @param trackInfo Track info 15 | * @param httpInterfaceManager 16 | */ 17 | public HlsStreamTrack(AudioTrackInfo trackInfo, String streamUrl, HttpInterfaceManager httpInterfaceManager, 18 | boolean isInnerUrl) { 19 | 20 | super(trackInfo); 21 | 22 | segmentUrlProvider = isInnerUrl ? 23 | new HlsStreamSegmentUrlProvider(null, streamUrl) : 24 | new HlsStreamSegmentUrlProvider(streamUrl, null); 25 | 26 | this.httpInterfaceManager = httpInterfaceManager; 27 | } 28 | 29 | @Override 30 | protected M3uStreamSegmentUrlProvider getSegmentUrlProvider() { 31 | return segmentUrlProvider; 32 | } 33 | 34 | @Override 35 | protected HttpInterface getHttpInterface() { 36 | return httpInterfaceManager.getInterface(); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /main/src/main/java/com/sedmelluq/discord/lavaplayer/container/wav/WavAudioTrack.java: -------------------------------------------------------------------------------- 1 | package com.sedmelluq.discord.lavaplayer.container.wav; 2 | 3 | import com.sedmelluq.discord.lavaplayer.tools.io.SeekableInputStream; 4 | import com.sedmelluq.discord.lavaplayer.track.AudioTrackInfo; 5 | import com.sedmelluq.discord.lavaplayer.track.BaseAudioTrack; 6 | import com.sedmelluq.discord.lavaplayer.track.playback.LocalAudioTrackExecutor; 7 | import org.slf4j.Logger; 8 | import org.slf4j.LoggerFactory; 9 | 10 | /** 11 | * Audio track that handles a WAV stream 12 | */ 13 | public class WavAudioTrack extends BaseAudioTrack { 14 | private static final Logger log = LoggerFactory.getLogger(WavAudioTrack.class); 15 | 16 | private final SeekableInputStream inputStream; 17 | 18 | /** 19 | * @param trackInfo Track info 20 | * @param inputStream Input stream for the WAV file 21 | */ 22 | public WavAudioTrack(AudioTrackInfo trackInfo, SeekableInputStream inputStream) { 23 | super(trackInfo); 24 | 25 | this.inputStream = inputStream; 26 | } 27 | 28 | @Override 29 | public void process(LocalAudioTrackExecutor localExecutor) throws Exception { 30 | WavTrackProvider trackProvider = new WavFileLoader(inputStream).loadTrack(localExecutor.getProcessingContext()); 31 | 32 | try { 33 | log.debug("Starting to play WAV track {}", getIdentifier()); 34 | localExecutor.executeProcessingLoop(trackProvider::provideFrames, trackProvider::seekToTimecode); 35 | } finally { 36 | trackProvider.close(); 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /main/src/main/java/com/sedmelluq/discord/lavaplayer/container/wav/WaveFormatType.java: -------------------------------------------------------------------------------- 1 | package com.sedmelluq.discord.lavaplayer.container.wav; 2 | 3 | import java.util.Arrays; 4 | 5 | public enum WaveFormatType { 6 | // https://www.mmsp.ece.mcgill.ca/Documents/AudioFormats/WAVE/Docs/Pages%20from%20mmreg.h.pdf 7 | WAVE_FORMAT_UNKNOWN(0x0000), 8 | WAVE_FORMAT_PCM(0x0001), 9 | WAVE_FORMAT_EXTENSIBLE(0xFFFE); 10 | 11 | final int code; 12 | 13 | WaveFormatType(int code) { 14 | this.code = code; 15 | } 16 | 17 | static WaveFormatType getByCode(int code) { 18 | return Arrays.stream(values()).filter(type -> type.code == code).findFirst() 19 | .orElse(WAVE_FORMAT_UNKNOWN); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /main/src/main/java/com/sedmelluq/discord/lavaplayer/filter/AudioFilter.java: -------------------------------------------------------------------------------- 1 | package com.sedmelluq.discord.lavaplayer.filter; 2 | 3 | /** 4 | * A filter for audio samples 5 | */ 6 | public interface AudioFilter { 7 | /** 8 | * Indicates that the next samples are not a continuation from the previous ones and gives the timecode for the 9 | * next incoming sample. 10 | * 11 | * @param requestedTime Timecode in milliseconds to which the seek was requested to 12 | * @param providedTime Timecode in milliseconds to which the seek was actually performed to 13 | */ 14 | void seekPerformed(long requestedTime, long providedTime); 15 | 16 | /** 17 | * Flush everything to output. 18 | * 19 | * @throws InterruptedException When interrupted externally (or for seek/stop). 20 | */ 21 | void flush() throws InterruptedException; 22 | 23 | /** 24 | * Free all resources. No more input is expected. 25 | */ 26 | void close(); 27 | } 28 | -------------------------------------------------------------------------------- /main/src/main/java/com/sedmelluq/discord/lavaplayer/filter/AudioFilterChain.java: -------------------------------------------------------------------------------- 1 | package com.sedmelluq.discord.lavaplayer.filter; 2 | 3 | import java.util.List; 4 | 5 | /** 6 | * A chain of audio filters. 7 | */ 8 | public class AudioFilterChain { 9 | /** 10 | * The first filter in the stream. Separate field as unlike other filters, this must be an instance of 11 | * {@link UniversalPcmAudioFilter} as the input data may be in any representation. 12 | */ 13 | public final UniversalPcmAudioFilter input; 14 | 15 | /** 16 | * All filters in this chain. 17 | */ 18 | public final List filters; 19 | 20 | /** 21 | * Immutable context/configuration instance that this filter was generated from. May be null. 22 | */ 23 | public final Object context; 24 | 25 | /** 26 | * @param input See {@link #input}. 27 | * @param filters See {@link #filters}. 28 | * @param context See {@link #context}. 29 | */ 30 | public AudioFilterChain(UniversalPcmAudioFilter input, List filters, Object context) { 31 | this.input = input; 32 | this.filters = filters; 33 | this.context = context; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /main/src/main/java/com/sedmelluq/discord/lavaplayer/filter/AudioPipeline.java: -------------------------------------------------------------------------------- 1 | package com.sedmelluq.discord.lavaplayer.filter; 2 | 3 | import java.nio.ShortBuffer; 4 | import java.util.List; 5 | 6 | /** 7 | * Represents an audio pipeline (top-level audio filter chain). 8 | */ 9 | public class AudioPipeline extends CompositeAudioFilter { 10 | private final List filters; 11 | private final UniversalPcmAudioFilter first; 12 | 13 | /** 14 | * @param chain The top-level filter chain. 15 | */ 16 | public AudioPipeline(AudioFilterChain chain) { 17 | this.filters = chain.filters; 18 | this.first = chain.input; 19 | } 20 | 21 | @Override 22 | public void process(float[][] input, int offset, int length) throws InterruptedException { 23 | first.process(input, offset, length); 24 | } 25 | 26 | @Override 27 | public void process(short[] input, int offset, int length) throws InterruptedException { 28 | first.process(input, offset, length); 29 | } 30 | 31 | @Override 32 | public void process(ShortBuffer buffer) throws InterruptedException { 33 | first.process(buffer); 34 | } 35 | 36 | @Override 37 | public void process(short[][] input, int offset, int length) throws InterruptedException { 38 | first.process(input, offset, length); 39 | } 40 | 41 | @Override 42 | protected List getFilters() { 43 | return filters; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /main/src/main/java/com/sedmelluq/discord/lavaplayer/filter/AudioPostProcessor.java: -------------------------------------------------------------------------------- 1 | package com.sedmelluq.discord.lavaplayer.filter; 2 | 3 | import java.nio.ShortBuffer; 4 | 5 | /** 6 | * Audio chunk post processor. 7 | */ 8 | public interface AudioPostProcessor { 9 | /** 10 | * Receives chunk buffer in its final PCM format with the sample count, sample rate and channel count matching that of 11 | * the output format. 12 | * 13 | * @param timecode Absolute starting timecode of the chunk in milliseconds 14 | * @param buffer PCM buffer of samples in the chunk 15 | * @throws InterruptedException When interrupted externally (or for seek/stop). 16 | */ 17 | void process(long timecode, ShortBuffer buffer) throws InterruptedException; 18 | 19 | /** 20 | * Frees up all resources this processor is holding internally. 21 | */ 22 | void close(); 23 | } 24 | -------------------------------------------------------------------------------- /main/src/main/java/com/sedmelluq/discord/lavaplayer/filter/CompositeAudioFilter.java: -------------------------------------------------------------------------------- 1 | package com.sedmelluq.discord.lavaplayer.filter; 2 | 3 | import org.slf4j.Logger; 4 | import org.slf4j.LoggerFactory; 5 | 6 | import java.util.List; 7 | 8 | /** 9 | * An audio filter which may consist of a number of other filters. 10 | */ 11 | public abstract class CompositeAudioFilter implements UniversalPcmAudioFilter { 12 | private static final Logger log = LoggerFactory.getLogger(CompositeAudioFilter.class); 13 | 14 | @Override 15 | public void seekPerformed(long requestedTime, long providedTime) { 16 | for (AudioFilter filter : getFilters()) { 17 | try { 18 | filter.seekPerformed(requestedTime, providedTime); 19 | } catch (Exception e) { 20 | log.error("Notifying filter {} of seek failed with exception.", filter.getClass(), e); 21 | } 22 | } 23 | } 24 | 25 | @Override 26 | public void flush() throws InterruptedException { 27 | for (AudioFilter filter : getFilters()) { 28 | try { 29 | filter.flush(); 30 | } catch (Exception e) { 31 | log.error("Flushing filter {} failed with exception.", filter.getClass(), e); 32 | } 33 | } 34 | } 35 | 36 | @Override 37 | public void close() { 38 | for (AudioFilter filter : getFilters()) { 39 | try { 40 | filter.close(); 41 | } catch (Exception e) { 42 | log.error("Closing filter {} failed with exception.", filter.getClass(), e); 43 | } 44 | } 45 | } 46 | 47 | protected abstract List getFilters(); 48 | } 49 | -------------------------------------------------------------------------------- /main/src/main/java/com/sedmelluq/discord/lavaplayer/filter/FloatPcmAudioFilter.java: -------------------------------------------------------------------------------- 1 | package com.sedmelluq.discord.lavaplayer.filter; 2 | 3 | /** 4 | * Audio filter which accepts floating point PCM samples. 5 | */ 6 | public interface FloatPcmAudioFilter extends AudioFilter { 7 | /** 8 | * @param input An array of samples for each channel 9 | * @param offset Offset in the arrays to start at 10 | * @param length Length of the target sequence in arrays 11 | * @throws InterruptedException When interrupted externally (or for seek/stop). 12 | */ 13 | void process(float[][] input, int offset, int length) throws InterruptedException; 14 | } 15 | -------------------------------------------------------------------------------- /main/src/main/java/com/sedmelluq/discord/lavaplayer/filter/PcmFilterFactory.java: -------------------------------------------------------------------------------- 1 | package com.sedmelluq.discord.lavaplayer.filter; 2 | 3 | import com.sedmelluq.discord.lavaplayer.format.AudioDataFormat; 4 | import com.sedmelluq.discord.lavaplayer.track.AudioTrack; 5 | 6 | import java.util.List; 7 | 8 | /** 9 | * Factory for custom PCM filters. 10 | */ 11 | public interface PcmFilterFactory { 12 | /** 13 | * Builds a filter chain for processing a track. Note that this may be called several times during the playback of a 14 | * single track. All filters should send the output from the filter either to the next filter in the list, or to the 15 | * output filter if it is the last one in the list. Only the process and flush methods should call the next filter, 16 | * all other methods are called individually for each filter anyway. 17 | * 18 | * @param track The track that this chain is built for. 19 | * @param format The output format of the track. At the point where these filters are called, the number of channels 20 | * and the sample rate already matches that of the output format. 21 | * @param output The filter that the last filter in this chain should send its data to. 22 | * @return The list of filters in the built chain. May be empty, but not null. 23 | */ 24 | List buildChain(AudioTrack track, AudioDataFormat format, UniversalPcmAudioFilter output); 25 | } 26 | -------------------------------------------------------------------------------- /main/src/main/java/com/sedmelluq/discord/lavaplayer/filter/PcmFormat.java: -------------------------------------------------------------------------------- 1 | package com.sedmelluq.discord.lavaplayer.filter; 2 | 3 | /** 4 | * Describes the properties of PCM data. 5 | */ 6 | public class PcmFormat { 7 | /** 8 | * Number of channels. 9 | */ 10 | public final int channelCount; 11 | /** 12 | * Sample rate (frequency). 13 | */ 14 | public final int sampleRate; 15 | 16 | /** 17 | * @param channelCount See {@link #channelCount}. 18 | * @param sampleRate See {@link #sampleRate}. 19 | */ 20 | public PcmFormat(int channelCount, int sampleRate) { 21 | this.channelCount = channelCount; 22 | this.sampleRate = sampleRate; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /main/src/main/java/com/sedmelluq/discord/lavaplayer/filter/ShortPcmAudioFilter.java: -------------------------------------------------------------------------------- 1 | package com.sedmelluq.discord.lavaplayer.filter; 2 | 3 | import java.nio.ShortBuffer; 4 | 5 | /** 6 | * Audio filter which accepts 16-bit signed PCM samples. 7 | */ 8 | public interface ShortPcmAudioFilter extends AudioFilter { 9 | /** 10 | * @param input Array of samples 11 | * @param offset Offset in the array 12 | * @param length Length of the sequence in the array 13 | * @throws InterruptedException When interrupted externally (or for seek/stop). 14 | */ 15 | void process(short[] input, int offset, int length) throws InterruptedException; 16 | 17 | /** 18 | * @param buffer The buffer of samples 19 | * @throws InterruptedException When interrupted externally (or for seek/stop). 20 | */ 21 | void process(ShortBuffer buffer) throws InterruptedException; 22 | } 23 | -------------------------------------------------------------------------------- /main/src/main/java/com/sedmelluq/discord/lavaplayer/filter/SplitShortPcmAudioFilter.java: -------------------------------------------------------------------------------- 1 | package com.sedmelluq.discord.lavaplayer.filter; 2 | 3 | /** 4 | * Audio filter which accepts 16-bit signed PCM samples, with an array per . 5 | */ 6 | public interface SplitShortPcmAudioFilter extends AudioFilter { 7 | /** 8 | * @param input An array of samples for each channel 9 | * @param offset Offset in the array 10 | * @param length Length of the sequence in the array 11 | * @throws InterruptedException When interrupted externally (or for seek/stop). 12 | */ 13 | void process(short[][] input, int offset, int length) throws InterruptedException; 14 | } 15 | -------------------------------------------------------------------------------- /main/src/main/java/com/sedmelluq/discord/lavaplayer/filter/UniversalPcmAudioFilter.java: -------------------------------------------------------------------------------- 1 | package com.sedmelluq.discord.lavaplayer.filter; 2 | 3 | /** 4 | * A PCM filter which must be able to correctly process all representations of PCM input data, as specified by the 5 | * methods of the interfaces it extends. 6 | */ 7 | public interface UniversalPcmAudioFilter extends ShortPcmAudioFilter, SplitShortPcmAudioFilter, FloatPcmAudioFilter { 8 | } 9 | -------------------------------------------------------------------------------- /main/src/main/java/com/sedmelluq/discord/lavaplayer/filter/converter/ConverterAudioFilter.java: -------------------------------------------------------------------------------- 1 | package com.sedmelluq.discord.lavaplayer.filter.converter; 2 | 3 | import com.sedmelluq.discord.lavaplayer.filter.UniversalPcmAudioFilter; 4 | 5 | /** 6 | * Base class for converter filters which have no internal state. 7 | */ 8 | public abstract class ConverterAudioFilter implements UniversalPcmAudioFilter { 9 | protected static final int BUFFER_SIZE = 4096; 10 | 11 | @Override 12 | public void seekPerformed(long requestedTime, long providedTime) { 13 | // Nothing to do. 14 | } 15 | 16 | @Override 17 | public void flush() throws InterruptedException { 18 | // Nothing to do. 19 | } 20 | 21 | @Override 22 | public void close() { 23 | // Nothing to do. 24 | } 25 | 26 | protected static short floatToShort(float value) { 27 | return (short) (value * 32768.0f); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /main/src/main/java/com/sedmelluq/discord/lavaplayer/filter/equalizer/EqualizerConfiguration.java: -------------------------------------------------------------------------------- 1 | package com.sedmelluq.discord.lavaplayer.filter.equalizer; 2 | 3 | /** 4 | * Holder of equalizer configuration. 5 | */ 6 | public class EqualizerConfiguration { 7 | protected final float[] bandMultipliers; 8 | 9 | /** 10 | * @param bandMultipliers The band multiplier values. Keeps using this array internally, so the values can be changed 11 | * externally. 12 | */ 13 | public EqualizerConfiguration(float[] bandMultipliers) { 14 | this.bandMultipliers = bandMultipliers; 15 | } 16 | 17 | /** 18 | * @param band The index of the band. If this is not a valid band index, the method has no effect. 19 | * @param value The multiplier for this band. Default value is 0. Valid values are from -0.25 to 1. -0.25 means that 20 | * the given frequency is completely muted and 0.25 means it is doubled. Note that this may change the 21 | * volume of the output. 22 | */ 23 | public void setGain(int band, float value) { 24 | if (isValidBand(band)) { 25 | bandMultipliers[band] = Math.max(Math.min(value, 1.0f), -0.25f); 26 | } 27 | } 28 | 29 | /** 30 | * @param band The index of the band. 31 | * @return The multiplier for this band. Default value is 0. 32 | */ 33 | public float getGain(int band) { 34 | if (isValidBand(band)) { 35 | return bandMultipliers[band]; 36 | } else { 37 | return 0.0f; 38 | } 39 | } 40 | 41 | private boolean isValidBand(int band) { 42 | return band >= 0 && band < bandMultipliers.length; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /main/src/main/java/com/sedmelluq/discord/lavaplayer/filter/equalizer/EqualizerFactory.java: -------------------------------------------------------------------------------- 1 | package com.sedmelluq.discord.lavaplayer.filter.equalizer; 2 | 3 | import com.sedmelluq.discord.lavaplayer.filter.AudioFilter; 4 | import com.sedmelluq.discord.lavaplayer.filter.PcmFilterFactory; 5 | import com.sedmelluq.discord.lavaplayer.filter.UniversalPcmAudioFilter; 6 | import com.sedmelluq.discord.lavaplayer.format.AudioDataFormat; 7 | import com.sedmelluq.discord.lavaplayer.track.AudioTrack; 8 | 9 | import java.util.Collections; 10 | import java.util.List; 11 | 12 | /** 13 | * PCM filter factory which creates a single {@link Equalizer} filter for every track. Useful in case the equalizer is 14 | * the only custom filter used. 15 | */ 16 | public class EqualizerFactory extends EqualizerConfiguration implements PcmFilterFactory { 17 | /** 18 | * Creates a new instance no gains applied initially. 19 | */ 20 | public EqualizerFactory() { 21 | super(new float[Equalizer.BAND_COUNT]); 22 | } 23 | 24 | @Override 25 | public List buildChain(AudioTrack track, AudioDataFormat format, UniversalPcmAudioFilter output) { 26 | if (Equalizer.isCompatible(format)) { 27 | return Collections.singletonList(new Equalizer(format.channelCount, output, bandMultipliers)); 28 | } else { 29 | return Collections.emptyList(); 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /main/src/main/java/com/sedmelluq/discord/lavaplayer/filter/volume/VolumePostProcessor.java: -------------------------------------------------------------------------------- 1 | package com.sedmelluq.discord.lavaplayer.filter.volume; 2 | 3 | import com.sedmelluq.discord.lavaplayer.filter.AudioPostProcessor; 4 | import com.sedmelluq.discord.lavaplayer.track.playback.AudioProcessingContext; 5 | 6 | import java.nio.ShortBuffer; 7 | 8 | /** 9 | * Audio chunk post processor to apply selected volume. 10 | */ 11 | public class VolumePostProcessor implements AudioPostProcessor { 12 | private final PcmVolumeProcessor volumeProcessor; 13 | private final AudioProcessingContext context; 14 | 15 | /** 16 | * @param context Configuration and output information for processing 17 | */ 18 | public VolumePostProcessor(AudioProcessingContext context) { 19 | this.context = context; 20 | this.volumeProcessor = new PcmVolumeProcessor(context.playerOptions.volumeLevel.get()); 21 | } 22 | 23 | @Override 24 | public void process(long timecode, ShortBuffer buffer) throws InterruptedException { 25 | int currentVolume = context.playerOptions.volumeLevel.get(); 26 | 27 | if (currentVolume != volumeProcessor.getLastVolume()) { 28 | AudioFrameVolumeChanger.apply(context); 29 | } 30 | 31 | // Volume 0 is stored in the frame with volume 100 buffer 32 | if (currentVolume != 0) { 33 | volumeProcessor.applyVolume(100, currentVolume, buffer); 34 | } else { 35 | volumeProcessor.setLastVolume(0); 36 | } 37 | } 38 | 39 | @Override 40 | public void close() { 41 | // Nothing to close here 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /main/src/main/java/com/sedmelluq/discord/lavaplayer/format/AudioDataFormatTools.java: -------------------------------------------------------------------------------- 1 | package com.sedmelluq.discord.lavaplayer.format; 2 | 3 | import javax.sound.sampled.AudioFormat; 4 | 5 | import static javax.sound.sampled.AudioFormat.Encoding.PCM_SIGNED; 6 | 7 | /** 8 | * Tools to deal with audio data formats. 9 | */ 10 | public class AudioDataFormatTools { 11 | 12 | /** 13 | * @param format Audio data format to convert to JDK audio format 14 | * @return JDK audio format for the specified format. 15 | */ 16 | public static AudioFormat toAudioFormat(AudioDataFormat format) { 17 | if (format instanceof Pcm16AudioDataFormat) { 18 | return new AudioFormat( 19 | PCM_SIGNED, 20 | format.sampleRate, 21 | 16, 22 | format.channelCount, 23 | format.channelCount * 2, 24 | format.sampleRate, 25 | format.codecName().equals(Pcm16AudioDataFormat.CODEC_NAME_BE) 26 | ); 27 | } else { 28 | throw new IllegalStateException("Only PCM is currently supported."); 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /main/src/main/java/com/sedmelluq/discord/lavaplayer/format/StandardAudioDataFormats.java: -------------------------------------------------------------------------------- 1 | package com.sedmelluq.discord.lavaplayer.format; 2 | 3 | /** 4 | * Standard output formats compatible with Discord. 5 | */ 6 | public class StandardAudioDataFormats { 7 | /** 8 | * The Opus configuration used by both Discord and YouTube. Default. 9 | */ 10 | public static final AudioDataFormat DISCORD_OPUS = new OpusAudioDataFormat(2, 48000, 960); 11 | /** 12 | * Signed 16-bit big-endian PCM format matching the parameters used by Discord. 13 | */ 14 | public static final AudioDataFormat DISCORD_PCM_S16_BE = new Pcm16AudioDataFormat(2, 48000, 960, true); 15 | /** 16 | * Signed 16-bit little-endian PCM format matching the parameters used by Discord. 17 | */ 18 | public static final AudioDataFormat DISCORD_PCM_S16_LE = new Pcm16AudioDataFormat(2, 48000, 960, false); 19 | /** 20 | * Signed 16-bit big-endian PCM format matching with the most common sample rate. 21 | */ 22 | public static final AudioDataFormat COMMON_PCM_S16_BE = new Pcm16AudioDataFormat(2, 44100, 960, true); 23 | /** 24 | * Signed 16-bit little-endian PCM format matching with the most common sample rate. 25 | */ 26 | public static final AudioDataFormat COMMON_PCM_S16_LE = new Pcm16AudioDataFormat(2, 44100, 960, false); 27 | } 28 | -------------------------------------------------------------------------------- /main/src/main/java/com/sedmelluq/discord/lavaplayer/format/transcoder/AudioChunkDecoder.java: -------------------------------------------------------------------------------- 1 | package com.sedmelluq.discord.lavaplayer.format.transcoder; 2 | 3 | import java.nio.ShortBuffer; 4 | 5 | /** 6 | * Decodes one chunk of audio into internal PCM format. 7 | */ 8 | public interface AudioChunkDecoder { 9 | /** 10 | * @param encoded Encoded bytes 11 | * @param buffer Output buffer for the PCM data 12 | */ 13 | void decode(byte[] encoded, ShortBuffer buffer); 14 | 15 | /** 16 | * Frees up all held resources. 17 | */ 18 | void close(); 19 | } 20 | -------------------------------------------------------------------------------- /main/src/main/java/com/sedmelluq/discord/lavaplayer/format/transcoder/AudioChunkEncoder.java: -------------------------------------------------------------------------------- 1 | package com.sedmelluq.discord.lavaplayer.format.transcoder; 2 | 3 | import java.nio.ByteBuffer; 4 | import java.nio.ShortBuffer; 5 | 6 | /** 7 | * Encodes one chunk of audio from internal PCM format. 8 | */ 9 | public interface AudioChunkEncoder { 10 | /** 11 | * @param buffer Input buffer containing the PCM samples. 12 | * @return Encoded bytes 13 | */ 14 | byte[] encode(ShortBuffer buffer); 15 | 16 | /** 17 | * @param buffer Input buffer containing the PCM samples. 18 | * @param out Output buffer to store the encoded bytes in 19 | */ 20 | void encode(ShortBuffer buffer, ByteBuffer out); 21 | 22 | /** 23 | * Frees up all held resources. 24 | */ 25 | void close(); 26 | } 27 | -------------------------------------------------------------------------------- /main/src/main/java/com/sedmelluq/discord/lavaplayer/format/transcoder/OpusChunkDecoder.java: -------------------------------------------------------------------------------- 1 | package com.sedmelluq.discord.lavaplayer.format.transcoder; 2 | 3 | import com.sedmelluq.discord.lavaplayer.format.AudioDataFormat; 4 | import com.sedmelluq.discord.lavaplayer.natives.opus.OpusDecoder; 5 | 6 | import java.nio.ByteBuffer; 7 | import java.nio.ShortBuffer; 8 | 9 | /** 10 | * Audio chunk decoder for Opus codec. 11 | */ 12 | public class OpusChunkDecoder implements AudioChunkDecoder { 13 | private final OpusDecoder decoder; 14 | private final ByteBuffer encodedBuffer; 15 | 16 | /** 17 | * @param format Source audio format. 18 | */ 19 | public OpusChunkDecoder(AudioDataFormat format) { 20 | encodedBuffer = ByteBuffer.allocateDirect(4096); 21 | decoder = new OpusDecoder(format.sampleRate, format.channelCount); 22 | } 23 | 24 | @Override 25 | public void decode(byte[] encoded, ShortBuffer buffer) { 26 | encodedBuffer.clear(); 27 | encodedBuffer.put(encoded); 28 | encodedBuffer.flip(); 29 | 30 | buffer.clear(); 31 | decoder.decode(encodedBuffer, buffer); 32 | } 33 | 34 | @Override 35 | public void close() { 36 | decoder.close(); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /main/src/main/java/com/sedmelluq/discord/lavaplayer/format/transcoder/PcmChunkDecoder.java: -------------------------------------------------------------------------------- 1 | package com.sedmelluq.discord.lavaplayer.format.transcoder; 2 | 3 | import com.sedmelluq.discord.lavaplayer.format.AudioDataFormat; 4 | 5 | import java.nio.ByteBuffer; 6 | import java.nio.ByteOrder; 7 | import java.nio.ShortBuffer; 8 | 9 | /** 10 | * Audio chunk decoder for PCM data. 11 | */ 12 | public class PcmChunkDecoder implements AudioChunkDecoder { 13 | private final ByteBuffer encodedAsByte; 14 | private final ShortBuffer encodedAsShort; 15 | 16 | /** 17 | * @param format Source audio format. 18 | * @param bigEndian Whether the samples are in big-endian format (as opposed to little-endian). 19 | */ 20 | public PcmChunkDecoder(AudioDataFormat format, boolean bigEndian) { 21 | this.encodedAsByte = ByteBuffer.allocate(format.maximumChunkSize()); 22 | encodedAsByte.order(bigEndian ? ByteOrder.BIG_ENDIAN : ByteOrder.LITTLE_ENDIAN); 23 | this.encodedAsShort = encodedAsByte.asShortBuffer(); 24 | } 25 | 26 | @Override 27 | public void decode(byte[] encoded, ShortBuffer buffer) { 28 | buffer.clear(); 29 | 30 | encodedAsByte.clear(); 31 | encodedAsByte.put(encoded); 32 | 33 | encodedAsShort.clear(); 34 | encodedAsShort.limit(encodedAsByte.position() / 2); 35 | 36 | buffer.put(encodedAsShort); 37 | buffer.rewind(); 38 | } 39 | 40 | @Override 41 | public void close() { 42 | // Nothing to close here 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /main/src/main/java/com/sedmelluq/discord/lavaplayer/natives/ConnectorNativeLibLoader.java: -------------------------------------------------------------------------------- 1 | package com.sedmelluq.discord.lavaplayer.natives; 2 | 3 | import com.sedmelluq.lava.common.natives.NativeLibraryLoader; 4 | import com.sedmelluq.lava.common.natives.architecture.DefaultOperatingSystemTypes; 5 | 6 | /** 7 | * Methods for loading the connector library. 8 | */ 9 | public class ConnectorNativeLibLoader { 10 | private static final NativeLibraryLoader[] loaders = new NativeLibraryLoader[]{ 11 | NativeLibraryLoader.createFiltered(ConnectorNativeLibLoader.class, "libmpg123-0", 12 | it -> it.osType == DefaultOperatingSystemTypes.WINDOWS), 13 | NativeLibraryLoader.create(ConnectorNativeLibLoader.class, "connector") 14 | }; 15 | 16 | /** 17 | * Loads the connector library with its dependencies for the current system 18 | */ 19 | public static void loadConnectorLibrary() { 20 | for (NativeLibraryLoader loader : loaders) { 21 | loader.load(); 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /main/src/main/java/com/sedmelluq/discord/lavaplayer/natives/aac/AacDecoderLibrary.java: -------------------------------------------------------------------------------- 1 | package com.sedmelluq.discord.lavaplayer.natives.aac; 2 | 3 | import com.sedmelluq.discord.lavaplayer.natives.ConnectorNativeLibLoader; 4 | 5 | import java.nio.ByteBuffer; 6 | import java.nio.ShortBuffer; 7 | 8 | class AacDecoderLibrary { 9 | private AacDecoderLibrary() { 10 | 11 | } 12 | 13 | static AacDecoderLibrary getInstance() { 14 | ConnectorNativeLibLoader.loadConnectorLibrary(); 15 | return new AacDecoderLibrary(); 16 | } 17 | 18 | native long create(int transportType); 19 | 20 | native void destroy(long instance); 21 | 22 | native int configure(long instance, long bufferData); 23 | 24 | native int fill(long instance, ByteBuffer directBuffer, int offset, int length); 25 | 26 | native int decode(long instance, ShortBuffer directBuffer, int length, boolean flush); 27 | 28 | native long getStreamInfo(long instance); 29 | } 30 | -------------------------------------------------------------------------------- /main/src/main/java/com/sedmelluq/discord/lavaplayer/natives/mp3/Mp3DecoderLibrary.java: -------------------------------------------------------------------------------- 1 | package com.sedmelluq.discord.lavaplayer.natives.mp3; 2 | 3 | import com.sedmelluq.discord.lavaplayer.natives.ConnectorNativeLibLoader; 4 | 5 | import java.nio.ByteBuffer; 6 | import java.nio.ShortBuffer; 7 | 8 | class Mp3DecoderLibrary { 9 | private Mp3DecoderLibrary() { 10 | 11 | } 12 | 13 | static Mp3DecoderLibrary getInstance() { 14 | ConnectorNativeLibLoader.loadConnectorLibrary(); 15 | return new Mp3DecoderLibrary(); 16 | } 17 | 18 | native long create(); 19 | 20 | native void destroy(long instance); 21 | 22 | native int decode(long instance, ByteBuffer directInput, int inputLength, ShortBuffer directOutput, int outputLengthInBytes); 23 | } 24 | -------------------------------------------------------------------------------- /main/src/main/java/com/sedmelluq/discord/lavaplayer/natives/opus/OpusDecoderLibrary.java: -------------------------------------------------------------------------------- 1 | package com.sedmelluq.discord.lavaplayer.natives.opus; 2 | 3 | import com.sedmelluq.discord.lavaplayer.natives.ConnectorNativeLibLoader; 4 | 5 | import java.nio.ByteBuffer; 6 | import java.nio.ShortBuffer; 7 | 8 | class OpusDecoderLibrary { 9 | private OpusDecoderLibrary() { 10 | 11 | } 12 | 13 | static OpusDecoderLibrary getInstance() { 14 | ConnectorNativeLibLoader.loadConnectorLibrary(); 15 | return new OpusDecoderLibrary(); 16 | } 17 | 18 | native long create(int sampleRate, int channels); 19 | 20 | native void destroy(long instance); 21 | 22 | native int decode(long instance, ByteBuffer directInput, int inputSize, ShortBuffer directOutput, int frameSize); 23 | } 24 | -------------------------------------------------------------------------------- /main/src/main/java/com/sedmelluq/discord/lavaplayer/natives/opus/OpusEncoderLibrary.java: -------------------------------------------------------------------------------- 1 | package com.sedmelluq.discord.lavaplayer.natives.opus; 2 | 3 | import com.sedmelluq.discord.lavaplayer.natives.ConnectorNativeLibLoader; 4 | 5 | import java.nio.ByteBuffer; 6 | import java.nio.ShortBuffer; 7 | 8 | class OpusEncoderLibrary { 9 | static final int APPLICATION_AUDIO = 2049; 10 | 11 | private OpusEncoderLibrary() { 12 | 13 | } 14 | 15 | static OpusEncoderLibrary getInstance() { 16 | ConnectorNativeLibLoader.loadConnectorLibrary(); 17 | return new OpusEncoderLibrary(); 18 | } 19 | 20 | native long create(int sampleRate, int channels, int application, int quality); 21 | 22 | native void destroy(long instance); 23 | 24 | native int encode(long instance, ShortBuffer directInput, int frameSize, ByteBuffer directOutput, int outputCapacity); 25 | } 26 | -------------------------------------------------------------------------------- /main/src/main/java/com/sedmelluq/discord/lavaplayer/natives/samplerate/SampleRateLibrary.java: -------------------------------------------------------------------------------- 1 | package com.sedmelluq.discord.lavaplayer.natives.samplerate; 2 | 3 | import com.sedmelluq.discord.lavaplayer.natives.ConnectorNativeLibLoader; 4 | 5 | class SampleRateLibrary { 6 | private SampleRateLibrary() { 7 | 8 | } 9 | 10 | static SampleRateLibrary getInstance() { 11 | ConnectorNativeLibLoader.loadConnectorLibrary(); 12 | return new SampleRateLibrary(); 13 | } 14 | 15 | native long create(int type, int channels); 16 | 17 | native void destroy(long instance); 18 | 19 | native void reset(long instance); 20 | 21 | native int process(long instance, float[] in, int inOffset, int inLength, float[] out, int outOffset, int outLength, boolean endOfInput, double sourceRatio, int[] progress); 22 | } 23 | -------------------------------------------------------------------------------- /main/src/main/java/com/sedmelluq/discord/lavaplayer/natives/statistics/CpuStatisticsLibrary.java: -------------------------------------------------------------------------------- 1 | package com.sedmelluq.discord.lavaplayer.natives.statistics; 2 | 3 | import com.sedmelluq.discord.lavaplayer.natives.ConnectorNativeLibLoader; 4 | 5 | class CpuStatisticsLibrary { 6 | private CpuStatisticsLibrary() { 7 | 8 | } 9 | 10 | static CpuStatisticsLibrary getInstance() { 11 | ConnectorNativeLibLoader.loadConnectorLibrary(); 12 | return new CpuStatisticsLibrary(); 13 | } 14 | 15 | native void getSystemTimes(long[] timingArray); 16 | 17 | enum Timings { 18 | SYSTEM_TOTAL, 19 | SYSTEM_USER, 20 | SYSTEM_KERNEL, 21 | PROCESS_USER, 22 | PROCESS_KERNEL; 23 | 24 | int id() { 25 | return ordinal(); 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /main/src/main/java/com/sedmelluq/discord/lavaplayer/natives/vorbis/VorbisDecoderLibrary.java: -------------------------------------------------------------------------------- 1 | package com.sedmelluq.discord.lavaplayer.natives.vorbis; 2 | 3 | import com.sedmelluq.discord.lavaplayer.natives.ConnectorNativeLibLoader; 4 | 5 | import java.nio.ByteBuffer; 6 | 7 | class VorbisDecoderLibrary { 8 | private VorbisDecoderLibrary() { 9 | 10 | } 11 | 12 | static VorbisDecoderLibrary getInstance() { 13 | ConnectorNativeLibLoader.loadConnectorLibrary(); 14 | return new VorbisDecoderLibrary(); 15 | } 16 | 17 | native long create(); 18 | 19 | native void destroy(long instance); 20 | 21 | native boolean initialise(long instance, ByteBuffer infoBuffer, int infoOffset, int infoLength, 22 | ByteBuffer setupBuffer, int setupOffset, int setupLength); 23 | 24 | native int getChannelCount(long instance); 25 | 26 | native int input(long instance, ByteBuffer directBuffer, int offset, int length); 27 | 28 | native int output(long instance, float[][] channels, int length); 29 | } 30 | -------------------------------------------------------------------------------- /main/src/main/java/com/sedmelluq/discord/lavaplayer/player/AudioLoadResultHandler.java: -------------------------------------------------------------------------------- 1 | package com.sedmelluq.discord.lavaplayer.player; 2 | 3 | import com.sedmelluq.discord.lavaplayer.tools.FriendlyException; 4 | import com.sedmelluq.discord.lavaplayer.track.AudioPlaylist; 5 | import com.sedmelluq.discord.lavaplayer.track.AudioTrack; 6 | 7 | /** 8 | * Handles the result of loading an item from an audio player manager. 9 | */ 10 | public interface AudioLoadResultHandler { 11 | /** 12 | * Called when the requested item is a track and it was successfully loaded. 13 | * 14 | * @param track The loaded track 15 | */ 16 | void trackLoaded(AudioTrack track); 17 | 18 | /** 19 | * Called when the requested item is a playlist and it was successfully loaded. 20 | * 21 | * @param playlist The loaded playlist 22 | */ 23 | void playlistLoaded(AudioPlaylist playlist); 24 | 25 | /** 26 | * Called when there were no items found by the specified identifier. 27 | */ 28 | void noMatches(); 29 | 30 | /** 31 | * Called when loading an item failed with an exception. 32 | * 33 | * @param exception The exception that was thrown 34 | */ 35 | void loadFailed(FriendlyException exception); 36 | } 37 | -------------------------------------------------------------------------------- /main/src/main/java/com/sedmelluq/discord/lavaplayer/player/AudioPlayerOptions.java: -------------------------------------------------------------------------------- 1 | package com.sedmelluq.discord.lavaplayer.player; 2 | 3 | import com.sedmelluq.discord.lavaplayer.filter.PcmFilterFactory; 4 | 5 | import java.util.concurrent.atomic.AtomicInteger; 6 | import java.util.concurrent.atomic.AtomicReference; 7 | 8 | /** 9 | * Mutable options of an audio player which may be applied in real-time. 10 | */ 11 | public class AudioPlayerOptions { 12 | /** 13 | * Volume level of the audio, see {@link AudioPlayer#setVolume(int)}. Applied in real-time. 14 | */ 15 | public final AtomicInteger volumeLevel; 16 | /** 17 | * Current PCM filter factory. Applied in real-time. 18 | */ 19 | public final AtomicReference filterFactory; 20 | /** 21 | * Current frame buffer size. If not set, the global default is used. Changing this only affects the next track that 22 | * is started. 23 | */ 24 | public final AtomicReference frameBufferDuration; 25 | 26 | /** 27 | * New instance of player options. By default, frame buffer duration is not set, hence taken from global settings. 28 | */ 29 | public AudioPlayerOptions() { 30 | this.volumeLevel = new AtomicInteger(100); 31 | this.filterFactory = new AtomicReference<>(); 32 | this.frameBufferDuration = new AtomicReference<>(); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /main/src/main/java/com/sedmelluq/discord/lavaplayer/player/event/AudioEvent.java: -------------------------------------------------------------------------------- 1 | package com.sedmelluq.discord.lavaplayer.player.event; 2 | 3 | import com.sedmelluq.discord.lavaplayer.player.AudioPlayer; 4 | 5 | /** 6 | * An event related to an audio player. 7 | */ 8 | public abstract class AudioEvent { 9 | /** 10 | * The related audio player. 11 | */ 12 | public final AudioPlayer player; 13 | 14 | /** 15 | * @param player The related audio player. 16 | */ 17 | public AudioEvent(AudioPlayer player) { 18 | this.player = player; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /main/src/main/java/com/sedmelluq/discord/lavaplayer/player/event/AudioEventListener.java: -------------------------------------------------------------------------------- 1 | package com.sedmelluq.discord.lavaplayer.player.event; 2 | 3 | /** 4 | * Listener of audio events. 5 | */ 6 | public interface AudioEventListener { 7 | /** 8 | * @param event The event 9 | */ 10 | void onEvent(AudioEvent event); 11 | } 12 | -------------------------------------------------------------------------------- /main/src/main/java/com/sedmelluq/discord/lavaplayer/player/event/PlayerPauseEvent.java: -------------------------------------------------------------------------------- 1 | package com.sedmelluq.discord.lavaplayer.player.event; 2 | 3 | import com.sedmelluq.discord.lavaplayer.player.AudioPlayer; 4 | 5 | /** 6 | * Event that is fired when a player is paused. 7 | */ 8 | public class PlayerPauseEvent extends AudioEvent { 9 | /** 10 | * @param player Audio player 11 | */ 12 | public PlayerPauseEvent(AudioPlayer player) { 13 | super(player); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /main/src/main/java/com/sedmelluq/discord/lavaplayer/player/event/PlayerResumeEvent.java: -------------------------------------------------------------------------------- 1 | package com.sedmelluq.discord.lavaplayer.player.event; 2 | 3 | import com.sedmelluq.discord.lavaplayer.player.AudioPlayer; 4 | 5 | /** 6 | * Event that is fired when a player is resumed. 7 | */ 8 | public class PlayerResumeEvent extends AudioEvent { 9 | /** 10 | * @param player Audio player 11 | */ 12 | public PlayerResumeEvent(AudioPlayer player) { 13 | super(player); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /main/src/main/java/com/sedmelluq/discord/lavaplayer/player/event/TrackEndEvent.java: -------------------------------------------------------------------------------- 1 | package com.sedmelluq.discord.lavaplayer.player.event; 2 | 3 | import com.sedmelluq.discord.lavaplayer.player.AudioPlayer; 4 | import com.sedmelluq.discord.lavaplayer.track.AudioTrack; 5 | import com.sedmelluq.discord.lavaplayer.track.AudioTrackEndReason; 6 | 7 | /** 8 | * Event that is fired when an audio track ends in an audio player, either by interruption, exception or reaching the end. 9 | */ 10 | public class TrackEndEvent extends AudioEvent { 11 | /** 12 | * Audio track that ended 13 | */ 14 | public final AudioTrack track; 15 | /** 16 | * The reason why the track stopped playing 17 | */ 18 | public final AudioTrackEndReason endReason; 19 | 20 | /** 21 | * @param player Audio player 22 | * @param track Audio track that ended 23 | * @param endReason The reason why the track stopped playing 24 | */ 25 | public TrackEndEvent(AudioPlayer player, AudioTrack track, AudioTrackEndReason endReason) { 26 | super(player); 27 | this.track = track; 28 | this.endReason = endReason; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /main/src/main/java/com/sedmelluq/discord/lavaplayer/player/event/TrackExceptionEvent.java: -------------------------------------------------------------------------------- 1 | package com.sedmelluq.discord.lavaplayer.player.event; 2 | 3 | import com.sedmelluq.discord.lavaplayer.player.AudioPlayer; 4 | import com.sedmelluq.discord.lavaplayer.tools.FriendlyException; 5 | import com.sedmelluq.discord.lavaplayer.track.AudioTrack; 6 | 7 | /** 8 | * Event that is fired when an exception occurs in an audio track that causes it to halt or not start. 9 | */ 10 | public class TrackExceptionEvent extends AudioEvent { 11 | /** 12 | * Audio track where the exception occurred 13 | */ 14 | public final AudioTrack track; 15 | /** 16 | * The exception that occurred 17 | */ 18 | public final FriendlyException exception; 19 | 20 | /** 21 | * @param player Audio player 22 | * @param track Audio track where the exception occurred 23 | * @param exception The exception that occurred 24 | */ 25 | public TrackExceptionEvent(AudioPlayer player, AudioTrack track, FriendlyException exception) { 26 | super(player); 27 | this.track = track; 28 | this.exception = exception; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /main/src/main/java/com/sedmelluq/discord/lavaplayer/player/event/TrackStartEvent.java: -------------------------------------------------------------------------------- 1 | package com.sedmelluq.discord.lavaplayer.player.event; 2 | 3 | import com.sedmelluq.discord.lavaplayer.player.AudioPlayer; 4 | import com.sedmelluq.discord.lavaplayer.track.AudioTrack; 5 | 6 | /** 7 | * Event that is fired when a track starts playing. 8 | */ 9 | public class TrackStartEvent extends AudioEvent { 10 | /** 11 | * Audio track that started 12 | */ 13 | public final AudioTrack track; 14 | 15 | /** 16 | * @param player Audio player 17 | * @param track Audio track that started 18 | */ 19 | public TrackStartEvent(AudioPlayer player, AudioTrack track) { 20 | super(player); 21 | this.track = track; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /main/src/main/java/com/sedmelluq/discord/lavaplayer/player/event/TrackStuckEvent.java: -------------------------------------------------------------------------------- 1 | package com.sedmelluq.discord.lavaplayer.player.event; 2 | 3 | import com.sedmelluq.discord.lavaplayer.player.AudioPlayer; 4 | import com.sedmelluq.discord.lavaplayer.track.AudioTrack; 5 | 6 | /** 7 | * Event that is fired when a track was started, but no audio frames from it have arrived in a long time, specified 8 | * by the threshold set via AudioPlayerManager.setTrackStuckThreshold(). 9 | */ 10 | public class TrackStuckEvent extends AudioEvent { 11 | /** 12 | * Audio track where the exception occurred 13 | */ 14 | public final AudioTrack track; 15 | /** 16 | * The wait threshold that was exceeded for this event to trigger 17 | */ 18 | public final long thresholdMs; 19 | 20 | public final StackTraceElement[] stackTrace; 21 | 22 | /** 23 | * @param player Audio player 24 | * @param track Audio track where the exception occurred 25 | * @param thresholdMs The wait threshold that was exceeded for this event to trigger 26 | */ 27 | public TrackStuckEvent(AudioPlayer player, AudioTrack track, long thresholdMs, StackTraceElement[] stackTrace) { 28 | super(player); 29 | this.track = track; 30 | this.thresholdMs = thresholdMs; 31 | this.stackTrace = stackTrace; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /main/src/main/java/com/sedmelluq/discord/lavaplayer/player/hook/AudioOutputHook.java: -------------------------------------------------------------------------------- 1 | package com.sedmelluq.discord.lavaplayer.player.hook; 2 | 3 | import com.sedmelluq.discord.lavaplayer.player.AudioPlayer; 4 | import com.sedmelluq.discord.lavaplayer.track.playback.AudioFrame; 5 | 6 | /** 7 | * Hook for intercepting outgoing audio frames from AudioPlayer. 8 | */ 9 | public interface AudioOutputHook { 10 | /** 11 | * @param player Audio player where the frame is coming from 12 | * @param frame Audio frame 13 | * @return The frame to pass onto the actual caller 14 | */ 15 | AudioFrame outgoingFrame(AudioPlayer player, AudioFrame frame); 16 | } 17 | -------------------------------------------------------------------------------- /main/src/main/java/com/sedmelluq/discord/lavaplayer/player/hook/AudioOutputHookFactory.java: -------------------------------------------------------------------------------- 1 | package com.sedmelluq.discord.lavaplayer.player.hook; 2 | 3 | /** 4 | * Factory for audio output hook instances. 5 | */ 6 | public interface AudioOutputHookFactory { 7 | /** 8 | * @return New instance of an audio output hook 9 | */ 10 | AudioOutputHook createOutputHook(); 11 | } 12 | -------------------------------------------------------------------------------- /main/src/main/java/com/sedmelluq/discord/lavaplayer/source/soundcloud/DefaultSoundCloudDataLoader.java: -------------------------------------------------------------------------------- 1 | package com.sedmelluq.discord.lavaplayer.source.soundcloud; 2 | 3 | import com.sedmelluq.discord.lavaplayer.tools.JsonBrowser; 4 | import com.sedmelluq.discord.lavaplayer.tools.io.HttpClientTools; 5 | import com.sedmelluq.discord.lavaplayer.tools.io.HttpInterface; 6 | import org.apache.http.HttpStatus; 7 | import org.apache.http.client.methods.CloseableHttpResponse; 8 | import org.apache.http.client.methods.HttpGet; 9 | import org.apache.http.client.utils.URIBuilder; 10 | import org.apache.http.util.EntityUtils; 11 | 12 | import java.io.IOException; 13 | import java.net.URI; 14 | import java.net.URISyntaxException; 15 | import java.nio.charset.StandardCharsets; 16 | 17 | public class DefaultSoundCloudDataLoader implements SoundCloudDataLoader { 18 | @Override 19 | public JsonBrowser load(HttpInterface httpInterface, String url) throws IOException { 20 | try (CloseableHttpResponse response = httpInterface.execute(new HttpGet(buildUri(url)))) { 21 | if (response.getStatusLine().getStatusCode() == HttpStatus.SC_NOT_FOUND) { 22 | return JsonBrowser.NULL_BROWSER; 23 | } 24 | 25 | HttpClientTools.assertSuccessWithContent(response, "video page response"); 26 | 27 | String json = EntityUtils.toString(response.getEntity(), StandardCharsets.UTF_8); 28 | 29 | return JsonBrowser.parse(json); 30 | } 31 | } 32 | 33 | private URI buildUri(String url) { 34 | try { 35 | return new URIBuilder("https://api-v2.soundcloud.com/resolve") 36 | .addParameter("url", url) 37 | .build(); 38 | } catch (URISyntaxException e) { 39 | throw new RuntimeException(e); 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /main/src/main/java/com/sedmelluq/discord/lavaplayer/source/soundcloud/DefaultSoundCloudTrackFormat.java: -------------------------------------------------------------------------------- 1 | package com.sedmelluq.discord.lavaplayer.source.soundcloud; 2 | 3 | public class DefaultSoundCloudTrackFormat implements SoundCloudTrackFormat { 4 | private final String trackId; 5 | private final String protocol; 6 | private final String mimeType; 7 | private final String lookupUrl; 8 | 9 | public DefaultSoundCloudTrackFormat(String trackId, String protocol, String mimeType, String lookupUrl) { 10 | this.trackId = trackId; 11 | this.protocol = protocol; 12 | this.mimeType = mimeType; 13 | this.lookupUrl = lookupUrl; 14 | } 15 | 16 | @Override 17 | public String getTrackId() { 18 | return trackId; 19 | } 20 | 21 | @Override 22 | public String getProtocol() { 23 | return protocol; 24 | } 25 | 26 | @Override 27 | public String getMimeType() { 28 | return mimeType; 29 | } 30 | 31 | @Override 32 | public String getLookupUrl() { 33 | return lookupUrl; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /main/src/main/java/com/sedmelluq/discord/lavaplayer/source/soundcloud/SoundCloudDataLoader.java: -------------------------------------------------------------------------------- 1 | package com.sedmelluq.discord.lavaplayer.source.soundcloud; 2 | 3 | import com.sedmelluq.discord.lavaplayer.tools.JsonBrowser; 4 | import com.sedmelluq.discord.lavaplayer.tools.io.HttpInterface; 5 | 6 | import java.io.IOException; 7 | 8 | public interface SoundCloudDataLoader { 9 | JsonBrowser load(HttpInterface httpInterface, String url) throws IOException; 10 | } 11 | -------------------------------------------------------------------------------- /main/src/main/java/com/sedmelluq/discord/lavaplayer/source/soundcloud/SoundCloudDataReader.java: -------------------------------------------------------------------------------- 1 | package com.sedmelluq.discord.lavaplayer.source.soundcloud; 2 | 3 | import com.sedmelluq.discord.lavaplayer.tools.JsonBrowser; 4 | import com.sedmelluq.discord.lavaplayer.track.AudioTrackInfo; 5 | 6 | import java.util.List; 7 | 8 | public interface SoundCloudDataReader { 9 | JsonBrowser findTrackData(JsonBrowser rootData); 10 | 11 | String readTrackId(JsonBrowser trackData); 12 | 13 | boolean isTrackBlocked(JsonBrowser trackData); 14 | 15 | AudioTrackInfo readTrackInfo(JsonBrowser trackData, String identifier); 16 | 17 | List readTrackFormats(JsonBrowser trackData); 18 | 19 | JsonBrowser findPlaylistData(JsonBrowser rootData, String kind); 20 | 21 | String readPlaylistName(JsonBrowser playlistData); 22 | 23 | String readPlaylistIdentifier(JsonBrowser playlistData); 24 | 25 | List readPlaylistTracks(JsonBrowser playlistData); 26 | } 27 | -------------------------------------------------------------------------------- /main/src/main/java/com/sedmelluq/discord/lavaplayer/source/soundcloud/SoundCloudFormatHandler.java: -------------------------------------------------------------------------------- 1 | package com.sedmelluq.discord.lavaplayer.source.soundcloud; 2 | 3 | import java.util.List; 4 | 5 | public interface SoundCloudFormatHandler { 6 | SoundCloudTrackFormat chooseBestFormat(List formats); 7 | 8 | String buildFormatIdentifier(SoundCloudTrackFormat format); 9 | 10 | SoundCloudM3uInfo getM3uInfo(String identifier); 11 | 12 | String getMp3LookupUrl(String identifier); 13 | } 14 | -------------------------------------------------------------------------------- /main/src/main/java/com/sedmelluq/discord/lavaplayer/source/soundcloud/SoundCloudM3uInfo.java: -------------------------------------------------------------------------------- 1 | package com.sedmelluq.discord.lavaplayer.source.soundcloud; 2 | 3 | public class SoundCloudM3uInfo { 4 | public final String lookupUrl; 5 | public final SoundCloudSegmentDecoder.Factory decoderFactory; 6 | 7 | public SoundCloudM3uInfo(String lookupUrl, SoundCloudSegmentDecoder.Factory decoderFactory) { 8 | this.lookupUrl = lookupUrl; 9 | this.decoderFactory = decoderFactory; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /main/src/main/java/com/sedmelluq/discord/lavaplayer/source/soundcloud/SoundCloudMp3SegmentDecoder.java: -------------------------------------------------------------------------------- 1 | package com.sedmelluq.discord.lavaplayer.source.soundcloud; 2 | 3 | import com.sedmelluq.discord.lavaplayer.container.mp3.Mp3TrackProvider; 4 | import com.sedmelluq.discord.lavaplayer.tools.io.SeekableInputStream; 5 | import com.sedmelluq.discord.lavaplayer.track.playback.AudioProcessingContext; 6 | 7 | import java.io.IOException; 8 | import java.util.function.Supplier; 9 | 10 | public class SoundCloudMp3SegmentDecoder implements SoundCloudSegmentDecoder { 11 | private final Supplier nextStreamProvider; 12 | 13 | public SoundCloudMp3SegmentDecoder(Supplier nextStreamProvider) { 14 | this.nextStreamProvider = nextStreamProvider; 15 | } 16 | 17 | @Override 18 | public void prepareStream(boolean beginning) { 19 | // Nothing to do. 20 | } 21 | 22 | @Override 23 | public void resetStream() { 24 | // Nothing to do. 25 | } 26 | 27 | @Override 28 | public void playStream( 29 | AudioProcessingContext context, 30 | long startPosition, 31 | long desiredPosition 32 | ) throws InterruptedException, IOException { 33 | try (SeekableInputStream stream = nextStreamProvider.get()) { 34 | Mp3TrackProvider trackProvider = new Mp3TrackProvider(context, stream); 35 | 36 | try { 37 | trackProvider.parseHeaders(); 38 | 39 | // Persist the position to seek to 40 | trackProvider.recordSeek(desiredPosition, startPosition); 41 | 42 | trackProvider.provideFrames(); 43 | } finally { 44 | trackProvider.close(); 45 | } 46 | } 47 | } 48 | 49 | @Override 50 | public void close() { 51 | // Nothing to do. 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /main/src/main/java/com/sedmelluq/discord/lavaplayer/source/soundcloud/SoundCloudPlaylistLoader.java: -------------------------------------------------------------------------------- 1 | package com.sedmelluq.discord.lavaplayer.source.soundcloud; 2 | 3 | import com.sedmelluq.discord.lavaplayer.tools.io.HttpInterfaceManager; 4 | import com.sedmelluq.discord.lavaplayer.track.AudioPlaylist; 5 | import com.sedmelluq.discord.lavaplayer.track.AudioTrack; 6 | import com.sedmelluq.discord.lavaplayer.track.AudioTrackInfo; 7 | 8 | import java.util.function.Function; 9 | 10 | public interface SoundCloudPlaylistLoader { 11 | AudioPlaylist load( 12 | String identifier, 13 | HttpInterfaceManager httpInterfaceManager, 14 | Function trackFactory 15 | ); 16 | } 17 | -------------------------------------------------------------------------------- /main/src/main/java/com/sedmelluq/discord/lavaplayer/source/soundcloud/SoundCloudSegmentDecoder.java: -------------------------------------------------------------------------------- 1 | package com.sedmelluq.discord.lavaplayer.source.soundcloud; 2 | 3 | import com.sedmelluq.discord.lavaplayer.tools.io.SeekableInputStream; 4 | import com.sedmelluq.discord.lavaplayer.track.playback.AudioProcessingContext; 5 | 6 | import java.io.IOException; 7 | import java.util.function.Supplier; 8 | 9 | public interface SoundCloudSegmentDecoder extends AutoCloseable { 10 | void prepareStream(boolean beginning) throws IOException; 11 | 12 | void resetStream() throws IOException; 13 | 14 | void playStream( 15 | AudioProcessingContext context, 16 | long startPosition, 17 | long desiredPosition 18 | ) throws InterruptedException, IOException; 19 | 20 | interface Factory { 21 | SoundCloudSegmentDecoder create(Supplier nextStreamProvider); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /main/src/main/java/com/sedmelluq/discord/lavaplayer/source/soundcloud/SoundCloudTrackFormat.java: -------------------------------------------------------------------------------- 1 | package com.sedmelluq.discord.lavaplayer.source.soundcloud; 2 | 3 | public interface SoundCloudTrackFormat { 4 | String getTrackId(); 5 | 6 | String getProtocol(); 7 | 8 | String getMimeType(); 9 | 10 | String getLookupUrl(); 11 | } 12 | -------------------------------------------------------------------------------- /main/src/main/java/com/sedmelluq/discord/lavaplayer/source/stream/M3uStreamAudioTrack.java: -------------------------------------------------------------------------------- 1 | package com.sedmelluq.discord.lavaplayer.source.stream; 2 | 3 | import com.sedmelluq.discord.lavaplayer.tools.io.ChainedInputStream; 4 | import com.sedmelluq.discord.lavaplayer.tools.io.HttpInterface; 5 | import com.sedmelluq.discord.lavaplayer.track.AudioTrackInfo; 6 | import com.sedmelluq.discord.lavaplayer.track.DelegatedAudioTrack; 7 | import com.sedmelluq.discord.lavaplayer.track.playback.LocalAudioTrackExecutor; 8 | 9 | import java.io.InputStream; 10 | 11 | /** 12 | * Audio track that handles processing M3U segment streams which using MPEG-TS wrapped ADTS codec. 13 | */ 14 | public abstract class M3uStreamAudioTrack extends DelegatedAudioTrack { 15 | /** 16 | * @param trackInfo Track info 17 | */ 18 | public M3uStreamAudioTrack(AudioTrackInfo trackInfo) { 19 | super(trackInfo); 20 | } 21 | 22 | protected abstract M3uStreamSegmentUrlProvider getSegmentUrlProvider(); 23 | 24 | protected abstract HttpInterface getHttpInterface(); 25 | 26 | protected abstract void processJoinedStream( 27 | LocalAudioTrackExecutor localExecutor, 28 | InputStream stream 29 | ) throws Exception; 30 | 31 | @Override 32 | public void process(LocalAudioTrackExecutor localExecutor) throws Exception { 33 | try (final HttpInterface httpInterface = getHttpInterface()) { 34 | try (ChainedInputStream chainedInputStream = new ChainedInputStream(() -> getSegmentUrlProvider().getNextSegmentStream(httpInterface))) { 35 | processJoinedStream(localExecutor, chainedInputStream); 36 | } 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /main/src/main/java/com/sedmelluq/discord/lavaplayer/source/stream/MpegTsM3uStreamAudioTrack.java: -------------------------------------------------------------------------------- 1 | package com.sedmelluq.discord.lavaplayer.source.stream; 2 | 3 | import com.sedmelluq.discord.lavaplayer.container.adts.AdtsAudioTrack; 4 | import com.sedmelluq.discord.lavaplayer.container.mpegts.MpegTsElementaryInputStream; 5 | import com.sedmelluq.discord.lavaplayer.container.mpegts.PesPacketInputStream; 6 | import com.sedmelluq.discord.lavaplayer.track.AudioTrackInfo; 7 | import com.sedmelluq.discord.lavaplayer.track.playback.LocalAudioTrackExecutor; 8 | 9 | import java.io.InputStream; 10 | 11 | import static com.sedmelluq.discord.lavaplayer.container.mpegts.MpegTsElementaryInputStream.ADTS_ELEMENTARY_STREAM; 12 | 13 | public abstract class MpegTsM3uStreamAudioTrack extends M3uStreamAudioTrack { 14 | /** 15 | * @param trackInfo Track info 16 | */ 17 | public MpegTsM3uStreamAudioTrack(AudioTrackInfo trackInfo) { 18 | super(trackInfo); 19 | } 20 | 21 | @Override 22 | protected void processJoinedStream(LocalAudioTrackExecutor localExecutor, InputStream stream) throws Exception { 23 | MpegTsElementaryInputStream elementaryInputStream = new MpegTsElementaryInputStream(stream, ADTS_ELEMENTARY_STREAM); 24 | PesPacketInputStream pesPacketInputStream = new PesPacketInputStream(elementaryInputStream); 25 | 26 | processDelegate(new AdtsAudioTrack(trackInfo, pesPacketInputStream), localExecutor); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /main/src/main/java/com/sedmelluq/discord/lavaplayer/source/twitch/TwitchConstants.java: -------------------------------------------------------------------------------- 1 | package com.sedmelluq.discord.lavaplayer.source.twitch; 2 | 3 | public class TwitchConstants { 4 | static final String TWITCH_GRAPHQL_BASE_URL = "https://gql.twitch.tv/gql"; 5 | static final String TWITCH_URL = "https://www.twitch.tv"; 6 | static final String TWITCH_IMAGE_PREVIEW_URL = "https://static-cdn.jtvnw.net/previews-ttv/live_user_%s-440x248.jpg"; 7 | static final String METADATA_PAYLOAD = "{\"operationName\":\"StreamMetadata\",\"variables\":{\"channelLogin\":\"%s\"},\"extensions\":{\"persistedQuery\":{\"version\":1,\"sha256Hash\":\"1c719a40e481453e5c48d9bb585d971b8b372f8ebb105b17076722264dfa5b3e\"}}}"; 8 | static final String ACCESS_TOKEN_PAYLOAD = "{\"operationName\":\"PlaybackAccessToken_Template\",\"query\":\"query PlaybackAccessToken_Template($login: String!,$isLive:Boolean!,$vodID:ID!,$isVod:Boolean!,$playerType:String!){streamPlaybackAccessToken(channelName:$login,params:{platform:\\\"web\\\",playerBackend:\\\"mediaplayer\\\",playerType:$playerType})@include(if:$isLive){value signature __typename}videoPlaybackAccessToken(id:$vodID,params:{platform:\\\"web\\\",playerBackend:\\\"mediaplayer\\\",playerType:$playerType})@include(if:$isVod){value signature __typename}}\",\"variables\":{\"isLive\":true,\"login\":\"%s\",\"isVod\":false,\"vodID\":\"\",\"playerType\":\"site\"}}"; 9 | } 10 | -------------------------------------------------------------------------------- /main/src/main/java/com/sedmelluq/discord/lavaplayer/source/yamusic/DefaultYandexMusicTrackLoader.java: -------------------------------------------------------------------------------- 1 | package com.sedmelluq.discord.lavaplayer.source.yamusic; 2 | 3 | import com.sedmelluq.discord.lavaplayer.tools.JsonBrowser; 4 | import com.sedmelluq.discord.lavaplayer.track.AudioItem; 5 | import com.sedmelluq.discord.lavaplayer.track.AudioReference; 6 | import com.sedmelluq.discord.lavaplayer.track.AudioTrack; 7 | import com.sedmelluq.discord.lavaplayer.track.AudioTrackInfo; 8 | 9 | import java.util.function.Function; 10 | 11 | public class DefaultYandexMusicTrackLoader extends AbstractYandexMusicApiLoader implements YandexMusicTrackLoader { 12 | 13 | private static final String TRACKS_INFO_FORMAT = "https://api.music.yandex.net/tracks?trackIds="; 14 | 15 | @Override 16 | public AudioItem loadTrack(String albumId, String trackId, Function trackFactory) { 17 | StringBuilder id = new StringBuilder(trackId); 18 | if (!albumId.isEmpty()) id.append(":").append(albumId); 19 | 20 | return extractFromApi(TRACKS_INFO_FORMAT + id, (httpClient, result) -> { 21 | JsonBrowser entry = result.index(0); 22 | if (DefaultYandexMusicPlaylistLoader.hasError(entry)) return AudioReference.NO_TRACK; 23 | return YandexMusicUtils.extractTrack(entry, trackFactory); 24 | }); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /main/src/main/java/com/sedmelluq/discord/lavaplayer/source/yamusic/YandexMusicApiLoader.java: -------------------------------------------------------------------------------- 1 | package com.sedmelluq.discord.lavaplayer.source.yamusic; 2 | 3 | import com.sedmelluq.discord.lavaplayer.tools.http.ExtendedHttpConfigurable; 4 | 5 | public interface YandexMusicApiLoader { 6 | ExtendedHttpConfigurable getHttpConfiguration(); 7 | 8 | void shutdown(); 9 | } 10 | -------------------------------------------------------------------------------- /main/src/main/java/com/sedmelluq/discord/lavaplayer/source/yamusic/YandexMusicDirectUrlLoader.java: -------------------------------------------------------------------------------- 1 | package com.sedmelluq.discord.lavaplayer.source.yamusic; 2 | 3 | public interface YandexMusicDirectUrlLoader extends YandexMusicApiLoader { 4 | String getDirectUrl(String trackId, String codec); 5 | } 6 | -------------------------------------------------------------------------------- /main/src/main/java/com/sedmelluq/discord/lavaplayer/source/yamusic/YandexMusicPlaylistLoader.java: -------------------------------------------------------------------------------- 1 | package com.sedmelluq.discord.lavaplayer.source.yamusic; 2 | 3 | import com.sedmelluq.discord.lavaplayer.track.AudioItem; 4 | import com.sedmelluq.discord.lavaplayer.track.AudioTrack; 5 | import com.sedmelluq.discord.lavaplayer.track.AudioTrackInfo; 6 | 7 | import java.util.function.Function; 8 | 9 | public interface YandexMusicPlaylistLoader extends YandexMusicApiLoader { 10 | AudioItem loadPlaylist(String login, String id, String trackProperty, Function trackFactory); 11 | 12 | AudioItem loadPlaylist(String album, String trackProperty, Function trackFactory); 13 | } 14 | -------------------------------------------------------------------------------- /main/src/main/java/com/sedmelluq/discord/lavaplayer/source/yamusic/YandexMusicSearchResultLoader.java: -------------------------------------------------------------------------------- 1 | package com.sedmelluq.discord.lavaplayer.source.yamusic; 2 | 3 | import com.sedmelluq.discord.lavaplayer.track.AudioItem; 4 | import com.sedmelluq.discord.lavaplayer.track.AudioTrack; 5 | import com.sedmelluq.discord.lavaplayer.track.AudioTrackInfo; 6 | 7 | import java.util.function.Function; 8 | 9 | public interface YandexMusicSearchResultLoader extends YandexMusicApiLoader { 10 | AudioItem loadSearchResult(String query, 11 | YandexMusicPlaylistLoader playlistLoader, 12 | Function trackFactory); 13 | } 14 | -------------------------------------------------------------------------------- /main/src/main/java/com/sedmelluq/discord/lavaplayer/source/yamusic/YandexMusicTrackLoader.java: -------------------------------------------------------------------------------- 1 | package com.sedmelluq.discord.lavaplayer.source.yamusic; 2 | 3 | import com.sedmelluq.discord.lavaplayer.track.AudioItem; 4 | import com.sedmelluq.discord.lavaplayer.track.AudioTrack; 5 | import com.sedmelluq.discord.lavaplayer.track.AudioTrackInfo; 6 | 7 | import java.util.function.Function; 8 | 9 | public interface YandexMusicTrackLoader extends YandexMusicApiLoader { 10 | AudioItem loadTrack(String albumId, String trackId, Function trackFactory); 11 | } 12 | -------------------------------------------------------------------------------- /main/src/main/java/com/sedmelluq/discord/lavaplayer/source/youtube/YoutubeCipherOperation.java: -------------------------------------------------------------------------------- 1 | package com.sedmelluq.discord.lavaplayer.source.youtube; 2 | 3 | /** 4 | * One cipher operation definition. 5 | */ 6 | public class YoutubeCipherOperation { 7 | /** 8 | * The type of the operation. 9 | */ 10 | public final YoutubeCipherOperationType type; 11 | /** 12 | * The parameter for the operation. 13 | */ 14 | public final int parameter; 15 | 16 | /** 17 | * @param type The type of the operation. 18 | * @param parameter The parameter for the operation. 19 | */ 20 | public YoutubeCipherOperation(YoutubeCipherOperationType type, int parameter) { 21 | this.type = type; 22 | this.parameter = parameter; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /main/src/main/java/com/sedmelluq/discord/lavaplayer/source/youtube/YoutubeCipherOperationType.java: -------------------------------------------------------------------------------- 1 | package com.sedmelluq.discord.lavaplayer.source.youtube; 2 | 3 | /** 4 | * Type of a signature cipher operation. 5 | */ 6 | public enum YoutubeCipherOperationType { 7 | SWAP, 8 | REVERSE, 9 | SLICE, 10 | SPLICE 11 | } 12 | -------------------------------------------------------------------------------- /main/src/main/java/com/sedmelluq/discord/lavaplayer/source/youtube/YoutubeLinkRouter.java: -------------------------------------------------------------------------------- 1 | package com.sedmelluq.discord.lavaplayer.source.youtube; 2 | 3 | public interface YoutubeLinkRouter { 4 | T route(String link, Routes routes); 5 | 6 | interface Routes { 7 | T track(String videoId); 8 | 9 | T playlist(String playlistId, String selectedVideoId); 10 | 11 | T mix(String mixId, String selectedVideoId); 12 | 13 | T search(String query); 14 | 15 | T searchMusic(String query); 16 | 17 | T anonymous(String videoIds); 18 | 19 | T none(); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /main/src/main/java/com/sedmelluq/discord/lavaplayer/source/youtube/YoutubeMixLoader.java: -------------------------------------------------------------------------------- 1 | package com.sedmelluq.discord.lavaplayer.source.youtube; 2 | 3 | import com.sedmelluq.discord.lavaplayer.tools.io.HttpInterface; 4 | import com.sedmelluq.discord.lavaplayer.track.AudioPlaylist; 5 | import com.sedmelluq.discord.lavaplayer.track.AudioTrack; 6 | import com.sedmelluq.discord.lavaplayer.track.AudioTrackInfo; 7 | 8 | import java.util.function.Function; 9 | 10 | public interface YoutubeMixLoader { 11 | AudioPlaylist load( 12 | HttpInterface httpInterface, 13 | String mixId, 14 | String selectedVideoId, 15 | Function trackFactory 16 | ); 17 | } 18 | -------------------------------------------------------------------------------- /main/src/main/java/com/sedmelluq/discord/lavaplayer/source/youtube/YoutubePayloadHelper.java: -------------------------------------------------------------------------------- 1 | package com.sedmelluq.discord.lavaplayer.source.youtube; 2 | 3 | import org.json.JSONObject; 4 | 5 | public class YoutubePayloadHelper { 6 | 7 | public static JSONObject putOnceAndJoin(JSONObject json, String key) { 8 | if (key != null) { 9 | if (json.opt(key) != null) { 10 | return json.getJSONObject(key); 11 | } 12 | return json.put(key, new JSONObject()).getJSONObject(key); 13 | } 14 | return json.getJSONObject(null); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /main/src/main/java/com/sedmelluq/discord/lavaplayer/source/youtube/YoutubePlaylistLoader.java: -------------------------------------------------------------------------------- 1 | package com.sedmelluq.discord.lavaplayer.source.youtube; 2 | 3 | import com.sedmelluq.discord.lavaplayer.tools.io.HttpInterface; 4 | import com.sedmelluq.discord.lavaplayer.track.AudioPlaylist; 5 | import com.sedmelluq.discord.lavaplayer.track.AudioTrack; 6 | import com.sedmelluq.discord.lavaplayer.track.AudioTrackInfo; 7 | 8 | import java.util.function.Function; 9 | 10 | public interface YoutubePlaylistLoader { 11 | void setPlaylistPageCount(int playlistPageCount); 12 | 13 | AudioPlaylist load(HttpInterface httpInterface, String playlistId, String selectedVideoId, 14 | Function trackFactory); 15 | } 16 | -------------------------------------------------------------------------------- /main/src/main/java/com/sedmelluq/discord/lavaplayer/source/youtube/YoutubeSearchMusicResultLoader.java: -------------------------------------------------------------------------------- 1 | package com.sedmelluq.discord.lavaplayer.source.youtube; 2 | 3 | import com.sedmelluq.discord.lavaplayer.tools.http.ExtendedHttpConfigurable; 4 | import com.sedmelluq.discord.lavaplayer.track.AudioItem; 5 | import com.sedmelluq.discord.lavaplayer.track.AudioTrack; 6 | import com.sedmelluq.discord.lavaplayer.track.AudioTrackInfo; 7 | 8 | import java.util.function.Function; 9 | 10 | public interface YoutubeSearchMusicResultLoader { 11 | AudioItem loadSearchMusicResult(String query, Function trackFactory); 12 | 13 | ExtendedHttpConfigurable getHttpConfiguration(); 14 | } 15 | -------------------------------------------------------------------------------- /main/src/main/java/com/sedmelluq/discord/lavaplayer/source/youtube/YoutubeSearchResultLoader.java: -------------------------------------------------------------------------------- 1 | package com.sedmelluq.discord.lavaplayer.source.youtube; 2 | 3 | import com.sedmelluq.discord.lavaplayer.tools.http.ExtendedHttpConfigurable; 4 | import com.sedmelluq.discord.lavaplayer.track.AudioItem; 5 | import com.sedmelluq.discord.lavaplayer.track.AudioTrack; 6 | import com.sedmelluq.discord.lavaplayer.track.AudioTrackInfo; 7 | 8 | import java.util.function.Function; 9 | 10 | public interface YoutubeSearchResultLoader { 11 | AudioItem loadSearchResult(String query, Function trackFactory); 12 | 13 | ExtendedHttpConfigurable getHttpConfiguration(); 14 | } 15 | -------------------------------------------------------------------------------- /main/src/main/java/com/sedmelluq/discord/lavaplayer/source/youtube/YoutubeSignatureResolver.java: -------------------------------------------------------------------------------- 1 | package com.sedmelluq.discord.lavaplayer.source.youtube; 2 | 3 | import com.sedmelluq.discord.lavaplayer.tools.io.HttpInterface; 4 | 5 | import java.io.IOException; 6 | import java.net.URI; 7 | 8 | public interface YoutubeSignatureResolver { 9 | YoutubeSignatureCipher getExtractedScript(HttpInterface httpInterface, String playerScript) throws IOException; 10 | 11 | URI resolveFormatUrl(HttpInterface httpInterface, String playerScript, YoutubeTrackFormat format) throws Exception; 12 | 13 | String resolveDashUrl(HttpInterface httpInterface, String playerScript, String dashUrl) throws Exception; 14 | } 15 | -------------------------------------------------------------------------------- /main/src/main/java/com/sedmelluq/discord/lavaplayer/source/youtube/YoutubeTrackDetails.java: -------------------------------------------------------------------------------- 1 | package com.sedmelluq.discord.lavaplayer.source.youtube; 2 | 3 | import com.sedmelluq.discord.lavaplayer.tools.io.HttpInterface; 4 | import com.sedmelluq.discord.lavaplayer.track.AudioTrackInfo; 5 | 6 | import java.util.List; 7 | 8 | public interface YoutubeTrackDetails { 9 | AudioTrackInfo getTrackInfo(); 10 | 11 | List getFormats(HttpInterface httpInterface, YoutubeSignatureResolver signatureResolver); 12 | 13 | String getPlayerScript(); 14 | } 15 | -------------------------------------------------------------------------------- /main/src/main/java/com/sedmelluq/discord/lavaplayer/source/youtube/YoutubeTrackDetailsLoader.java: -------------------------------------------------------------------------------- 1 | package com.sedmelluq.discord.lavaplayer.source.youtube; 2 | 3 | import com.sedmelluq.discord.lavaplayer.tools.io.HttpInterface; 4 | 5 | public interface YoutubeTrackDetailsLoader { 6 | YoutubeTrackDetails loadDetails(HttpInterface httpInterface, String videoId, boolean requireFormats, YoutubeAudioSourceManager sourceManager); 7 | } 8 | -------------------------------------------------------------------------------- /main/src/main/java/com/sedmelluq/discord/lavaplayer/source/youtube/format/OfflineYoutubeTrackFormatExtractor.java: -------------------------------------------------------------------------------- 1 | package com.sedmelluq.discord.lavaplayer.source.youtube.format; 2 | 3 | import com.sedmelluq.discord.lavaplayer.source.youtube.YoutubeSignatureResolver; 4 | import com.sedmelluq.discord.lavaplayer.source.youtube.YoutubeTrackFormat; 5 | import com.sedmelluq.discord.lavaplayer.source.youtube.YoutubeTrackJsonData; 6 | import com.sedmelluq.discord.lavaplayer.tools.io.HttpInterface; 7 | 8 | import java.util.List; 9 | 10 | public interface OfflineYoutubeTrackFormatExtractor extends YoutubeTrackFormatExtractor { 11 | List extract(YoutubeTrackJsonData data); 12 | 13 | @Override 14 | default List extract( 15 | YoutubeTrackJsonData data, 16 | HttpInterface httpInterface, 17 | YoutubeSignatureResolver signatureResolver 18 | ) { 19 | return extract(data); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /main/src/main/java/com/sedmelluq/discord/lavaplayer/source/youtube/format/YoutubeTrackFormatExtractor.java: -------------------------------------------------------------------------------- 1 | package com.sedmelluq.discord.lavaplayer.source.youtube.format; 2 | 3 | import com.sedmelluq.discord.lavaplayer.source.youtube.YoutubeSignatureResolver; 4 | import com.sedmelluq.discord.lavaplayer.source.youtube.YoutubeTrackFormat; 5 | import com.sedmelluq.discord.lavaplayer.source.youtube.YoutubeTrackJsonData; 6 | import com.sedmelluq.discord.lavaplayer.tools.io.HttpInterface; 7 | 8 | import java.util.List; 9 | 10 | public interface YoutubeTrackFormatExtractor { 11 | String DEFAULT_SIGNATURE_KEY = "signature"; 12 | 13 | List extract( 14 | YoutubeTrackJsonData response, 15 | HttpInterface httpInterface, 16 | YoutubeSignatureResolver signatureResolver 17 | ); 18 | } 19 | -------------------------------------------------------------------------------- /main/src/main/java/com/sedmelluq/discord/lavaplayer/tools/CopyOnUpdateIdentityList.java: -------------------------------------------------------------------------------- 1 | package com.sedmelluq.discord.lavaplayer.tools; 2 | 3 | import java.util.ArrayList; 4 | import java.util.Collections; 5 | import java.util.List; 6 | 7 | /** 8 | * Wrapper for keeping a list which can be updated from the same thread while being iterated. The list can contain only 9 | * one of each item (based on identity). Not backed by a set as that would be overkill for the use cases this class is 10 | * intended for. Not thread-safe. 11 | */ 12 | public class CopyOnUpdateIdentityList { 13 | public List items = Collections.emptyList(); 14 | 15 | public void add(T item) { 16 | for (T existingItem : items) { 17 | if (existingItem == item) { 18 | // No duplicates, do not add again. 19 | return; 20 | } 21 | } 22 | 23 | List updated = new ArrayList<>(items.size() + 1); 24 | updated.addAll(items); 25 | updated.add(item); 26 | items = updated; 27 | } 28 | 29 | public void remove(T item) { 30 | List updated = new ArrayList<>(items.size()); 31 | 32 | for (T existingItem : items) { 33 | if (existingItem != item) { 34 | updated.add(existingItem); 35 | } 36 | } 37 | 38 | items = updated; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /main/src/main/java/com/sedmelluq/discord/lavaplayer/tools/DecodedException.java: -------------------------------------------------------------------------------- 1 | package com.sedmelluq.discord.lavaplayer.tools; 2 | 3 | /** 4 | * Decoded serialized exception. The original exception class is not restored, instead all exceptions will be instances 5 | * of this class and contain the original class name and message as fields and as the message. 6 | */ 7 | public class DecodedException extends Exception { 8 | /** 9 | * Original exception class name 10 | */ 11 | public final String className; 12 | /** 13 | * Original exception message 14 | */ 15 | public final String originalMessage; 16 | 17 | /** 18 | * @param className Original exception class name 19 | * @param originalMessage Original exception message 20 | * @param cause Cause of this exception 21 | */ 22 | public DecodedException(String className, String originalMessage, DecodedException cause) { 23 | super(className + ": " + originalMessage, cause, true, true); 24 | 25 | this.className = className; 26 | this.originalMessage = originalMessage; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /main/src/main/java/com/sedmelluq/discord/lavaplayer/tools/FriendlyException.java: -------------------------------------------------------------------------------- 1 | package com.sedmelluq.discord.lavaplayer.tools; 2 | 3 | /** 4 | * An exception with a friendly message. 5 | */ 6 | public class FriendlyException extends RuntimeException { 7 | /** 8 | * Severity of the exception 9 | */ 10 | public final Severity severity; 11 | 12 | /** 13 | * @param friendlyMessage A message which is understandable to end-users 14 | * @param severity Severity of the exception 15 | * @param cause The cause of the exception with technical details 16 | */ 17 | public FriendlyException(String friendlyMessage, Severity severity, Throwable cause) { 18 | super(friendlyMessage, cause); 19 | 20 | this.severity = severity; 21 | } 22 | 23 | /** 24 | * Severity levels for FriendlyException 25 | */ 26 | public enum Severity { 27 | /** 28 | * The cause is known and expected, indicates that there is nothing wrong with the library itself. 29 | */ 30 | COMMON, 31 | /** 32 | * The cause might not be exactly known, but is possibly caused by outside factors. For example when an outside 33 | * service responds in a format that we do not expect. 34 | */ 35 | SUSPICIOUS, 36 | /** 37 | * If the probable cause is an issue with the library or when there is no way to tell what the cause might be. 38 | * This is the default level and other levels are used in cases where the thrower has more in-depth knowledge 39 | * about the error. 40 | */ 41 | FAULT 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /main/src/main/java/com/sedmelluq/discord/lavaplayer/tools/PlayerLibrary.java: -------------------------------------------------------------------------------- 1 | package com.sedmelluq.discord.lavaplayer.tools; 2 | 3 | import org.apache.commons.io.IOUtils; 4 | 5 | import java.io.InputStream; 6 | import java.nio.charset.StandardCharsets; 7 | 8 | /** 9 | * Contains constants with metadata about the library. 10 | */ 11 | public class PlayerLibrary { 12 | /** 13 | * The currently loaded version of the library. 14 | */ 15 | public static final String VERSION = readVersion(); 16 | 17 | private static String readVersion() { 18 | InputStream stream = PlayerLibrary.class.getResourceAsStream("version.txt"); 19 | 20 | try { 21 | if (stream != null) { 22 | return IOUtils.toString(stream, StandardCharsets.UTF_8); 23 | } 24 | } catch (Exception e) { 25 | // Something went wrong. 26 | } 27 | 28 | return "UNKNOWN"; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /main/src/main/java/com/sedmelluq/discord/lavaplayer/tools/RingBufferMath.java: -------------------------------------------------------------------------------- 1 | package com.sedmelluq.discord.lavaplayer.tools; 2 | 3 | import java.util.function.Function; 4 | 5 | /** 6 | * Utility class for calculating averages on the last N values, with input and output transformers. 7 | */ 8 | public class RingBufferMath { 9 | private final double[] values; 10 | private final Function inputProcessor; 11 | private final Function outputProcessor; 12 | private double sum; 13 | private int position; 14 | private int size; 15 | 16 | /** 17 | * @param size Maximum number of values to remember. 18 | * @param inputProcessor Input transformer. 19 | * @param outputProcessor Output transformer. 20 | */ 21 | public RingBufferMath(int size, Function inputProcessor, Function outputProcessor) { 22 | this.values = new double[size]; 23 | this.inputProcessor = inputProcessor; 24 | this.outputProcessor = outputProcessor; 25 | } 26 | 27 | /** 28 | * @param value Original value to add (before transformation) 29 | */ 30 | public void add(double value) { 31 | value = inputProcessor.apply(value); 32 | 33 | sum -= values[position]; 34 | values[position] = value; 35 | sum += values[position]; 36 | 37 | position = (position + 1) == values.length ? 0 : position + 1; 38 | size = Math.min(values.length, size + 1); 39 | } 40 | 41 | /** 42 | * @return Transformed mean of the internal values. 43 | */ 44 | public double mean() { 45 | if (size == 0) { 46 | return outputProcessor.apply(0.0); 47 | } else { 48 | return outputProcessor.apply(sum / size); 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /main/src/main/java/com/sedmelluq/discord/lavaplayer/tools/ThumbnailTools.java: -------------------------------------------------------------------------------- 1 | package com.sedmelluq.discord.lavaplayer.tools; 2 | 3 | import java.util.List; 4 | 5 | public class ThumbnailTools { 6 | 7 | public static String getYouTubeMusicThumbnail(JsonBrowser videoData, String videoId) { 8 | JsonBrowser thumbnails = videoData.get("thumbnail").get("thumbnails").index(0); 9 | 10 | if (!thumbnails.isNull()) { 11 | return thumbnails.get("url").text().replaceFirst("=.*", "=w1000-h1000"); 12 | } 13 | 14 | return String.format("https://i.ytimg.com/vi/%s/mqdefault.jpg", videoId); 15 | } 16 | 17 | public static String getYouTubeThumbnail(JsonBrowser videoData, String videoId) { 18 | List thumbnails = videoData.get("thumbnail").get("thumbnails").values(); 19 | 20 | if (!thumbnails.isEmpty()) { 21 | String lastThumbnail = thumbnails.get(thumbnails.size() - 1).get("url").text(); 22 | 23 | if (lastThumbnail.contains("maxresdefault")) { 24 | return lastThumbnail; 25 | } 26 | } 27 | 28 | return String.format("https://i.ytimg.com/vi/%s/mqdefault.jpg", videoId); 29 | } 30 | 31 | public static String getSoundCloudThumbnail(JsonBrowser trackData) { 32 | JsonBrowser thumbnail = trackData.get("artwork_url"); 33 | 34 | if (!thumbnail.isNull()) { 35 | return thumbnail.text().replace("large.jpg", "original.jpg"); 36 | } 37 | 38 | JsonBrowser avatar = trackData.get("user").get("avatar_url"); 39 | return avatar.text().replace("large.jpg", "original.jpg"); 40 | } 41 | } -------------------------------------------------------------------------------- /main/src/main/java/com/sedmelluq/discord/lavaplayer/tools/Units.java: -------------------------------------------------------------------------------- 1 | package com.sedmelluq.discord.lavaplayer.tools; 2 | 3 | public class Units { 4 | /** 5 | * Not a negative number, so that we would not need to test for it separately when comparing. 6 | */ 7 | public static final long CONTENT_LENGTH_UNKNOWN = Long.MAX_VALUE; 8 | public static final long DURATION_MS_UNKNOWN = Long.MAX_VALUE; 9 | public static final long DURATION_SEC_UNKNOWN = Long.MAX_VALUE; 10 | 11 | public static final long BITRATE_UNKNOWN = -1; 12 | 13 | private static final long SECONDS_MAXIMUM = DURATION_SEC_UNKNOWN / 1000; 14 | 15 | public static long secondsToMillis(long seconds) { 16 | if (seconds == DURATION_SEC_UNKNOWN) { 17 | return DURATION_MS_UNKNOWN; 18 | } else if (seconds > SECONDS_MAXIMUM) { 19 | throw new RuntimeException("Cannot convert " + seconds + " to millis - would overflow."); 20 | } else { 21 | return seconds * 1000; 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /main/src/main/java/com/sedmelluq/discord/lavaplayer/tools/http/AbstractHttpContextFilter.java: -------------------------------------------------------------------------------- 1 | package com.sedmelluq.discord.lavaplayer.tools.http; 2 | 3 | import org.apache.http.HttpResponse; 4 | import org.apache.http.client.methods.HttpUriRequest; 5 | import org.apache.http.client.protocol.HttpClientContext; 6 | 7 | public abstract class AbstractHttpContextFilter implements HttpContextFilter { 8 | private final HttpContextFilter delegate; 9 | 10 | protected AbstractHttpContextFilter(HttpContextFilter delegate) { 11 | this.delegate = delegate; 12 | } 13 | 14 | @Override 15 | public void onContextOpen(HttpClientContext context) { 16 | if (delegate != null) { 17 | delegate.onContextOpen(context); 18 | } 19 | } 20 | 21 | @Override 22 | public void onContextClose(HttpClientContext context) { 23 | if (delegate != null) { 24 | delegate.onContextClose(context); 25 | } 26 | } 27 | 28 | @Override 29 | public void onRequest(HttpClientContext context, HttpUriRequest request, boolean isRepetition) { 30 | if (delegate != null) { 31 | delegate.onRequest(context, request, isRepetition); 32 | } 33 | } 34 | 35 | @Override 36 | public boolean onRequestResponse(HttpClientContext context, HttpUriRequest request, HttpResponse response) { 37 | if (delegate != null) { 38 | return delegate.onRequestResponse(context, request, response); 39 | } 40 | 41 | return false; 42 | } 43 | 44 | @Override 45 | public boolean onRequestException(HttpClientContext context, HttpUriRequest request, Throwable error) { 46 | if (delegate != null) { 47 | return delegate.onRequestException(context, request, error); 48 | } 49 | 50 | return false; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /main/src/main/java/com/sedmelluq/discord/lavaplayer/tools/http/ExtendedHttpConfigurable.java: -------------------------------------------------------------------------------- 1 | package com.sedmelluq.discord.lavaplayer.tools.http; 2 | 3 | import com.sedmelluq.discord.lavaplayer.tools.io.HttpConfigurable; 4 | 5 | public interface ExtendedHttpConfigurable extends HttpConfigurable { 6 | void setHttpContextFilter(HttpContextFilter filter); 7 | } 8 | -------------------------------------------------------------------------------- /main/src/main/java/com/sedmelluq/discord/lavaplayer/tools/http/HttpContextFilter.java: -------------------------------------------------------------------------------- 1 | package com.sedmelluq.discord.lavaplayer.tools.http; 2 | 3 | import org.apache.http.HttpResponse; 4 | import org.apache.http.client.methods.HttpUriRequest; 5 | import org.apache.http.client.protocol.HttpClientContext; 6 | 7 | public interface HttpContextFilter { 8 | void onContextOpen(HttpClientContext context); 9 | 10 | void onContextClose(HttpClientContext context); 11 | 12 | void onRequest(HttpClientContext context, HttpUriRequest request, boolean isRepetition); 13 | 14 | boolean onRequestResponse(HttpClientContext context, HttpUriRequest request, HttpResponse response); 15 | 16 | boolean onRequestException(HttpClientContext context, HttpUriRequest request, Throwable error); 17 | } 18 | -------------------------------------------------------------------------------- /main/src/main/java/com/sedmelluq/discord/lavaplayer/tools/http/HttpContextRetryCounter.java: -------------------------------------------------------------------------------- 1 | package com.sedmelluq.discord.lavaplayer.tools.http; 2 | 3 | import org.apache.http.client.protocol.HttpClientContext; 4 | 5 | public class HttpContextRetryCounter { 6 | private final String attributeName; 7 | 8 | public HttpContextRetryCounter(String attributeName) { 9 | this.attributeName = attributeName; 10 | } 11 | 12 | public void handleUpdate(HttpClientContext context, boolean isRepetition) { 13 | if (isRepetition) { 14 | setRetryCount(context, getRetryCount(context) + 1); 15 | } else { 16 | setRetryCount(context, 0); 17 | } 18 | } 19 | 20 | public void setRetryCount(HttpClientContext context, int value) { 21 | RetryCount count = context.getAttribute(attributeName, RetryCount.class); 22 | 23 | if (count == null) { 24 | count = new RetryCount(); 25 | context.setAttribute(attributeName, count); 26 | } 27 | 28 | count.value = value; 29 | } 30 | 31 | public int getRetryCount(HttpClientContext context) { 32 | RetryCount count = context.getAttribute(attributeName, RetryCount.class); 33 | return count != null ? count.value : 0; 34 | } 35 | 36 | private static class RetryCount { 37 | private int value; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /main/src/main/java/com/sedmelluq/discord/lavaplayer/tools/http/HttpStreamTools.java: -------------------------------------------------------------------------------- 1 | package com.sedmelluq.discord.lavaplayer.tools.http; 2 | 3 | import com.sedmelluq.discord.lavaplayer.tools.ExceptionTools; 4 | import com.sedmelluq.discord.lavaplayer.tools.io.HttpClientTools; 5 | import com.sedmelluq.discord.lavaplayer.tools.io.HttpInterface; 6 | import org.apache.http.client.methods.CloseableHttpResponse; 7 | import org.apache.http.client.methods.HttpUriRequest; 8 | 9 | import java.io.IOException; 10 | import java.io.InputStream; 11 | 12 | public class HttpStreamTools { 13 | public static InputStream streamContent(HttpInterface httpInterface, HttpUriRequest request) { 14 | CloseableHttpResponse response = null; 15 | boolean success = false; 16 | 17 | try { 18 | response = httpInterface.execute(request); 19 | int statusCode = response.getStatusLine().getStatusCode(); 20 | 21 | if (!HttpClientTools.isSuccessWithContent(statusCode)) { 22 | throw new IOException("Invalid status code from " + request.getURI() + " URL: " + statusCode); 23 | } 24 | 25 | success = true; 26 | return response.getEntity().getContent(); 27 | } catch (IOException e) { 28 | throw new RuntimeException(e); 29 | } finally { 30 | if (response != null && !success) { 31 | ExceptionTools.closeWithWarnings(response); 32 | } 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /main/src/main/java/com/sedmelluq/discord/lavaplayer/tools/http/MultiHttpConfigurable.java: -------------------------------------------------------------------------------- 1 | package com.sedmelluq.discord.lavaplayer.tools.http; 2 | 3 | import org.apache.http.client.config.RequestConfig; 4 | import org.apache.http.impl.client.HttpClientBuilder; 5 | 6 | import java.util.Collection; 7 | import java.util.function.Consumer; 8 | import java.util.function.Function; 9 | 10 | public class MultiHttpConfigurable implements ExtendedHttpConfigurable { 11 | private final Collection configurables; 12 | 13 | public MultiHttpConfigurable(Collection configurables) { 14 | this.configurables = configurables; 15 | } 16 | 17 | @Override 18 | public void setHttpContextFilter(HttpContextFilter filter) { 19 | for (ExtendedHttpConfigurable configurable : configurables) { 20 | configurable.setHttpContextFilter(filter); 21 | } 22 | } 23 | 24 | @Override 25 | public void configureRequests(Function configurator) { 26 | for (ExtendedHttpConfigurable configurable : configurables) { 27 | configurable.configureRequests(configurator); 28 | } 29 | } 30 | 31 | @Override 32 | public void configureBuilder(Consumer configurator) { 33 | for (ExtendedHttpConfigurable configurable : configurables) { 34 | configurable.configureBuilder(configurator); 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /main/src/main/java/com/sedmelluq/discord/lavaplayer/tools/io/BitBufferReader.java: -------------------------------------------------------------------------------- 1 | package com.sedmelluq.discord.lavaplayer.tools.io; 2 | 3 | import java.io.IOException; 4 | import java.nio.ByteBuffer; 5 | 6 | /** 7 | * Helper for reading a specific number of bits at a time from a byte buffer. 8 | */ 9 | public class BitBufferReader extends BitStreamReader { 10 | private final ByteBuffer buffer; 11 | 12 | /** 13 | * @param buffer Byte buffer to read bytes from 14 | */ 15 | public BitBufferReader(ByteBuffer buffer) { 16 | super(null); 17 | 18 | this.buffer = buffer; 19 | } 20 | 21 | @Override 22 | public long asLong(int bitsNeeded) { 23 | try { 24 | return super.asLong(bitsNeeded); 25 | } catch (IOException e) { 26 | throw new RuntimeException(e); 27 | } 28 | } 29 | 30 | @Override 31 | public int asInteger(int bitsNeeded) { 32 | try { 33 | return super.asInteger(bitsNeeded); 34 | } catch (IOException e) { 35 | throw new RuntimeException(e); 36 | } 37 | } 38 | 39 | @Override 40 | protected int readByte() throws IOException { 41 | return buffer.get() & 0xFF; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /main/src/main/java/com/sedmelluq/discord/lavaplayer/tools/io/ByteBufferInputStream.java: -------------------------------------------------------------------------------- 1 | package com.sedmelluq.discord.lavaplayer.tools.io; 2 | 3 | import java.io.IOException; 4 | import java.io.InputStream; 5 | import java.nio.ByteBuffer; 6 | 7 | /** 8 | * A byte buffer exposed as an input stream. 9 | */ 10 | public class ByteBufferInputStream extends InputStream { 11 | private final ByteBuffer buffer; 12 | 13 | /** 14 | * @param buffer The buffer to read from. 15 | */ 16 | public ByteBufferInputStream(ByteBuffer buffer) { 17 | this.buffer = buffer; 18 | } 19 | 20 | @Override 21 | public int read() throws IOException { 22 | if (buffer.hasRemaining()) { 23 | return buffer.get() & 0xFF; 24 | } else { 25 | return -1; 26 | } 27 | } 28 | 29 | @Override 30 | public int read(byte[] array, int offset, int length) throws IOException { 31 | if (buffer.hasRemaining()) { 32 | int chunk = Math.min(buffer.remaining(), length); 33 | buffer.get(array, offset, length); 34 | return chunk; 35 | } else { 36 | return -1; 37 | } 38 | } 39 | 40 | @Override 41 | public int available() throws IOException { 42 | return buffer.remaining(); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /main/src/main/java/com/sedmelluq/discord/lavaplayer/tools/io/ByteBufferOutputStream.java: -------------------------------------------------------------------------------- 1 | package com.sedmelluq.discord.lavaplayer.tools.io; 2 | 3 | import java.io.IOException; 4 | import java.io.OutputStream; 5 | import java.nio.ByteBuffer; 6 | 7 | /** 8 | * A byte buffer wrapped in an output stream. 9 | */ 10 | public class ByteBufferOutputStream extends OutputStream { 11 | private final ByteBuffer buffer; 12 | 13 | /** 14 | * @param buffer The underlying byte buffer 15 | */ 16 | public ByteBufferOutputStream(ByteBuffer buffer) { 17 | this.buffer = buffer; 18 | } 19 | 20 | @Override 21 | public void write(int b) throws IOException { 22 | buffer.put((byte) b); 23 | } 24 | 25 | @Override 26 | public void write(byte[] b, int off, int len) throws IOException { 27 | buffer.put(b, off, len); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /main/src/main/java/com/sedmelluq/discord/lavaplayer/tools/io/DetachedByteChannel.java: -------------------------------------------------------------------------------- 1 | package com.sedmelluq.discord.lavaplayer.tools.io; 2 | 3 | import java.io.IOException; 4 | import java.nio.ByteBuffer; 5 | import java.nio.channels.ClosedChannelException; 6 | import java.nio.channels.ReadableByteChannel; 7 | 8 | /** 9 | * Creates a readable byte channel which can be closed without closing the underlying channel. 10 | */ 11 | public class DetachedByteChannel implements ReadableByteChannel { 12 | private final ReadableByteChannel delegate; 13 | private boolean closed; 14 | 15 | /** 16 | * @param delegate The underlying channel 17 | */ 18 | public DetachedByteChannel(ReadableByteChannel delegate) { 19 | this.delegate = delegate; 20 | } 21 | 22 | @Override 23 | public int read(ByteBuffer output) throws IOException { 24 | if (closed) { 25 | throw new ClosedChannelException(); 26 | } 27 | 28 | return delegate.read(output); 29 | } 30 | 31 | @Override 32 | public boolean isOpen() { 33 | return !closed && delegate.isOpen(); 34 | } 35 | 36 | @Override 37 | public void close() throws IOException { 38 | closed = true; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /main/src/main/java/com/sedmelluq/discord/lavaplayer/tools/io/EmptyInputStream.java: -------------------------------------------------------------------------------- 1 | package com.sedmelluq.discord.lavaplayer.tools.io; 2 | 3 | import java.io.InputStream; 4 | 5 | /** 6 | * Represents an empty input stream. 7 | */ 8 | public class EmptyInputStream extends InputStream { 9 | public static final EmptyInputStream INSTANCE = new EmptyInputStream(); 10 | 11 | @Override 12 | public int available() { 13 | return 0; 14 | } 15 | 16 | @Override 17 | public int read() { 18 | return -1; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /main/src/main/java/com/sedmelluq/discord/lavaplayer/tools/io/ExtendedBufferedInputStream.java: -------------------------------------------------------------------------------- 1 | package com.sedmelluq.discord.lavaplayer.tools.io; 2 | 3 | import java.io.BufferedInputStream; 4 | import java.io.InputStream; 5 | 6 | /** 7 | * A buffered input stream that gives with the ability to get the number of bytes left in the buffer and a method for 8 | * discarding the buffer. 9 | */ 10 | public class ExtendedBufferedInputStream extends BufferedInputStream { 11 | /** 12 | * @param in Underlying input stream 13 | */ 14 | public ExtendedBufferedInputStream(InputStream in) { 15 | super(in); 16 | } 17 | 18 | /** 19 | * @param in Underlying input stream 20 | * @param size Size of the buffer 21 | */ 22 | public ExtendedBufferedInputStream(InputStream in, int size) { 23 | super(in, size); 24 | } 25 | 26 | /** 27 | * @return The number of bytes left in the buffer. This is useful for calculating the actual position in the buffer 28 | * if the position in the underlying buffer is known. 29 | */ 30 | public int getBufferedByteCount() { 31 | return count - pos; 32 | } 33 | 34 | /** 35 | * Discard the remaining buffer. This should be called after seek has been performed on the underlying stream. 36 | */ 37 | public void discardBuffer() { 38 | pos = count; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /main/src/main/java/com/sedmelluq/discord/lavaplayer/tools/io/GreedyInputStream.java: -------------------------------------------------------------------------------- 1 | package com.sedmelluq.discord.lavaplayer.tools.io; 2 | 3 | import java.io.FilterInputStream; 4 | import java.io.IOException; 5 | import java.io.InputStream; 6 | 7 | /** 8 | * Input stream wrapper which reads or skips until EOF or requested length. 9 | */ 10 | public class GreedyInputStream extends FilterInputStream { 11 | /** 12 | * @param in Underlying input stream. 13 | */ 14 | public GreedyInputStream(InputStream in) { 15 | super(in); 16 | } 17 | 18 | @Override 19 | public int read(byte[] buffer, int offset, int length) throws IOException { 20 | int read = 0; 21 | 22 | while (read < length) { 23 | int chunk = in.read(buffer, offset + read, length - read); 24 | if (chunk == -1) { 25 | return read == 0 ? -1 : read; 26 | } 27 | read += chunk; 28 | } 29 | 30 | return read; 31 | } 32 | 33 | @Override 34 | public long skip(long maximum) throws IOException { 35 | long skipped = 0; 36 | 37 | while (skipped < maximum) { 38 | long chunk = in.skip(maximum - skipped); 39 | if (chunk == 0) { 40 | if (in.read() == -1) { 41 | break; 42 | } else { 43 | chunk = 1; 44 | } 45 | } 46 | skipped += chunk; 47 | } 48 | 49 | return skipped; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /main/src/main/java/com/sedmelluq/discord/lavaplayer/tools/io/HttpConfigurable.java: -------------------------------------------------------------------------------- 1 | package com.sedmelluq.discord.lavaplayer.tools.io; 2 | 3 | import org.apache.http.client.config.RequestConfig; 4 | import org.apache.http.impl.client.HttpClientBuilder; 5 | 6 | import java.util.function.Consumer; 7 | import java.util.function.Function; 8 | 9 | /** 10 | * Represents a class where HTTP request configuration can be changed. 11 | */ 12 | public interface HttpConfigurable { 13 | /** 14 | * @param configurator Function to reconfigure request config. 15 | */ 16 | void configureRequests(Function configurator); 17 | 18 | /** 19 | * @param configurator Function to reconfigure HTTP builder. 20 | */ 21 | void configureBuilder(Consumer configurator); 22 | } 23 | -------------------------------------------------------------------------------- /main/src/main/java/com/sedmelluq/discord/lavaplayer/tools/io/HttpInterfaceManager.java: -------------------------------------------------------------------------------- 1 | package com.sedmelluq.discord.lavaplayer.tools.io; 2 | 3 | import com.sedmelluq.discord.lavaplayer.tools.http.ExtendedHttpConfigurable; 4 | 5 | import java.io.Closeable; 6 | 7 | /** 8 | * A thread-safe manager for HTTP interfaces. 9 | */ 10 | public interface HttpInterfaceManager extends ExtendedHttpConfigurable, Closeable { 11 | /** 12 | * @return An HTTP interface for use by the current thread. 13 | */ 14 | HttpInterface getInterface(); 15 | } 16 | -------------------------------------------------------------------------------- /main/src/main/java/com/sedmelluq/discord/lavaplayer/tools/io/NonSeekableInputStream.java: -------------------------------------------------------------------------------- 1 | package com.sedmelluq.discord.lavaplayer.tools.io; 2 | 3 | import com.sedmelluq.discord.lavaplayer.tools.Units; 4 | import com.sedmelluq.discord.lavaplayer.track.info.AudioTrackInfoProvider; 5 | import org.apache.commons.io.input.CountingInputStream; 6 | 7 | import java.io.IOException; 8 | import java.io.InputStream; 9 | import java.util.Collections; 10 | import java.util.List; 11 | 12 | public class NonSeekableInputStream extends SeekableInputStream { 13 | private final CountingInputStream delegate; 14 | 15 | public NonSeekableInputStream(InputStream delegate) { 16 | super(Units.CONTENT_LENGTH_UNKNOWN, 0); 17 | this.delegate = new CountingInputStream(delegate); 18 | } 19 | 20 | @Override 21 | public long getPosition() { 22 | return delegate.getByteCount(); 23 | } 24 | 25 | @Override 26 | protected void seekHard(long position) { 27 | throw new UnsupportedOperationException(); 28 | } 29 | 30 | @Override 31 | public boolean canSeekHard() { 32 | return false; 33 | } 34 | 35 | @Override 36 | public List getTrackInfoProviders() { 37 | return Collections.emptyList(); 38 | } 39 | 40 | @Override 41 | public int read() throws IOException { 42 | return delegate.read(); 43 | } 44 | 45 | @Override 46 | public int read(byte[] buffer, int offset, int length) throws IOException { 47 | return delegate.read(buffer, offset, length); 48 | } 49 | 50 | @Override 51 | public void close() throws IOException { 52 | delegate.close(); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /main/src/main/java/com/sedmelluq/discord/lavaplayer/tools/io/SimpleHttpInterfaceManager.java: -------------------------------------------------------------------------------- 1 | package com.sedmelluq.discord.lavaplayer.tools.io; 2 | 3 | import com.sedmelluq.discord.lavaplayer.tools.http.HttpContextFilter; 4 | import com.sedmelluq.discord.lavaplayer.tools.http.SettableHttpRequestFilter; 5 | import org.apache.http.client.config.RequestConfig; 6 | import org.apache.http.client.protocol.HttpClientContext; 7 | import org.apache.http.impl.client.HttpClientBuilder; 8 | 9 | /** 10 | * HTTP interface manager which creates a new HTTP context for each interface. 11 | */ 12 | public class SimpleHttpInterfaceManager extends AbstractHttpInterfaceManager { 13 | private final SettableHttpRequestFilter filterHolder; 14 | 15 | /** 16 | * @param clientBuilder HTTP client builder to use for creating the client instance. 17 | * @param requestConfig Request config used by the client builder 18 | */ 19 | public SimpleHttpInterfaceManager(HttpClientBuilder clientBuilder, RequestConfig requestConfig) { 20 | super(clientBuilder, requestConfig); 21 | this.filterHolder = new SettableHttpRequestFilter(); 22 | } 23 | 24 | @Override 25 | public HttpInterface getInterface() { 26 | HttpInterface httpInterface = new HttpInterface(getSharedClient(), HttpClientContext.create(), false, filterHolder); 27 | httpInterface.acquire(); 28 | return httpInterface; 29 | } 30 | 31 | @Override 32 | public void setHttpContextFilter(HttpContextFilter filter) { 33 | filterHolder.set(filter); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /main/src/main/java/com/sedmelluq/discord/lavaplayer/tools/io/StreamTools.java: -------------------------------------------------------------------------------- 1 | package com.sedmelluq.discord.lavaplayer.tools.io; 2 | 3 | import java.io.IOException; 4 | import java.io.InputStream; 5 | 6 | /** 7 | * Utility methods for streams. 8 | */ 9 | public class StreamTools { 10 | /** 11 | * Reads from the stream until either the length number of bytes is read, or the stream ends. Note that neither case 12 | * throws an exception. 13 | * 14 | * @param in The stream to read from. 15 | * @param buffer Buffer to write the data that is read from the stream. 16 | * @param offset Offset in the buffer to start writing from. 17 | * @param length Maximum number of bytes to read from the stream. 18 | * @return The number of bytes read from the stream. 19 | * @throws IOException On read error. 20 | */ 21 | public static int readUntilEnd(InputStream in, byte[] buffer, int offset, int length) throws IOException { 22 | int position = 0; 23 | 24 | while (position < length) { 25 | int count = in.read(buffer, offset + position, length - position); 26 | if (count < 0) { 27 | break; 28 | } 29 | 30 | position += count; 31 | } 32 | 33 | return position; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /main/src/main/java/com/sedmelluq/discord/lavaplayer/track/AudioItem.java: -------------------------------------------------------------------------------- 1 | package com.sedmelluq.discord.lavaplayer.track; 2 | 3 | /** 4 | * Marker interface for all loadable items 5 | */ 6 | public interface AudioItem { 7 | } 8 | -------------------------------------------------------------------------------- /main/src/main/java/com/sedmelluq/discord/lavaplayer/track/AudioPlaylist.java: -------------------------------------------------------------------------------- 1 | package com.sedmelluq.discord.lavaplayer.track; 2 | 3 | import java.util.List; 4 | 5 | /** 6 | * Playlist of audio tracks 7 | */ 8 | public interface AudioPlaylist extends AudioItem { 9 | /** 10 | * @return Name of the playlist 11 | */ 12 | String getName(); 13 | 14 | /** 15 | * @return List of tracks in the playlist 16 | */ 17 | List getTracks(); 18 | 19 | /** 20 | * @return Track that is explicitly selected, may be null. This same instance occurs in the track list. 21 | */ 22 | AudioTrack getSelectedTrack(); 23 | 24 | /** 25 | * @return True if the playlist was created from search results. 26 | */ 27 | boolean isSearchResult(); 28 | } 29 | -------------------------------------------------------------------------------- /main/src/main/java/com/sedmelluq/discord/lavaplayer/track/AudioTrackEndReason.java: -------------------------------------------------------------------------------- 1 | package com.sedmelluq.discord.lavaplayer.track; 2 | 3 | /** 4 | * Reason why a track stopped playing. 5 | */ 6 | public enum AudioTrackEndReason { 7 | /** 8 | * This means that the track itself emitted a terminator. This is usually caused by the track reaching the end, 9 | * however it will also be used when it ends due to an exception. 10 | */ 11 | FINISHED(true), 12 | /** 13 | * This means that the track failed to start, throwing an exception before providing any audio. 14 | */ 15 | LOAD_FAILED(true), 16 | /** 17 | * The track was stopped due to the player being stopped by either calling stop() or playTrack(null). 18 | */ 19 | STOPPED(false), 20 | /** 21 | * The track stopped playing because a new track started playing. Note that with this reason, the old track will still 22 | * play until either its buffer runs out or audio from the new track is available. 23 | */ 24 | REPLACED(false), 25 | /** 26 | * The track was stopped because the cleanup threshold for the audio player was reached. This triggers when the amount 27 | * of time passed since the last call to AudioPlayer#provide() has reached the threshold specified in player manager 28 | * configuration. This may also indicate either a leaked audio player which was discarded, but not stopped. 29 | */ 30 | CLEANUP(false); 31 | 32 | /** 33 | * Indicates whether a new track should be started on receiving this event. If this is false, either this event is 34 | * already triggered because another track started (REPLACED) or because the player is stopped (STOPPED, CLEANUP). 35 | */ 36 | public final boolean mayStartNext; 37 | 38 | AudioTrackEndReason(boolean mayStartNext) { 39 | this.mayStartNext = mayStartNext; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /main/src/main/java/com/sedmelluq/discord/lavaplayer/track/AudioTrackState.java: -------------------------------------------------------------------------------- 1 | package com.sedmelluq.discord.lavaplayer.track; 2 | 3 | /** 4 | * The execution state of an audio track 5 | */ 6 | public enum AudioTrackState { 7 | INACTIVE, 8 | LOADING, 9 | PLAYING, 10 | SEEKING, 11 | STOPPING, 12 | FINISHED 13 | } 14 | -------------------------------------------------------------------------------- /main/src/main/java/com/sedmelluq/discord/lavaplayer/track/BasicAudioPlaylist.java: -------------------------------------------------------------------------------- 1 | package com.sedmelluq.discord.lavaplayer.track; 2 | 3 | import java.util.List; 4 | 5 | /** 6 | * The basic implementation of AudioPlaylist 7 | */ 8 | public class BasicAudioPlaylist implements AudioPlaylist { 9 | private final String name; 10 | private final List tracks; 11 | private final AudioTrack selectedTrack; 12 | private final boolean isSearchResult; 13 | 14 | /** 15 | * @param name Name of the playlist 16 | * @param tracks List of tracks in the playlist 17 | * @param selectedTrack Track that is explicitly selected 18 | * @param isSearchResult True if the playlist was created from search results 19 | */ 20 | public BasicAudioPlaylist(String name, List tracks, AudioTrack selectedTrack, boolean isSearchResult) { 21 | this.name = name; 22 | this.tracks = tracks; 23 | this.selectedTrack = selectedTrack; 24 | this.isSearchResult = isSearchResult; 25 | } 26 | 27 | @Override 28 | public String getName() { 29 | return name; 30 | } 31 | 32 | @Override 33 | public List getTracks() { 34 | return tracks; 35 | } 36 | 37 | @Override 38 | public AudioTrack getSelectedTrack() { 39 | return selectedTrack; 40 | } 41 | 42 | @Override 43 | public boolean isSearchResult() { 44 | return isSearchResult; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /main/src/main/java/com/sedmelluq/discord/lavaplayer/track/DecodedTrackHolder.java: -------------------------------------------------------------------------------- 1 | package com.sedmelluq.discord.lavaplayer.track; 2 | 3 | /** 4 | * The result of decoding a track. 5 | */ 6 | public class DecodedTrackHolder { 7 | /** 8 | * The decoded track. This may be null if there was a track to decode, but the decoding could not be performed because 9 | * of an older serialization version or because the track source it used is not loaded. 10 | */ 11 | public final AudioTrack decodedTrack; 12 | 13 | /** 14 | * @param decodedTrack The decoded track 15 | */ 16 | public DecodedTrackHolder(AudioTrack decodedTrack) { 17 | this.decodedTrack = decodedTrack; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /main/src/main/java/com/sedmelluq/discord/lavaplayer/track/InternalAudioTrack.java: -------------------------------------------------------------------------------- 1 | package com.sedmelluq.discord.lavaplayer.track; 2 | 3 | import com.sedmelluq.discord.lavaplayer.player.AudioPlayerManager; 4 | import com.sedmelluq.discord.lavaplayer.track.playback.AudioFrameProvider; 5 | import com.sedmelluq.discord.lavaplayer.track.playback.AudioTrackExecutor; 6 | import com.sedmelluq.discord.lavaplayer.track.playback.LocalAudioTrackExecutor; 7 | 8 | /** 9 | * Methods of an audio track that should not be visible outside of the library 10 | */ 11 | public interface InternalAudioTrack extends AudioTrack, AudioFrameProvider { 12 | /** 13 | * @param executor Executor to assign to the track 14 | * @param applyPrimordialState True if the state previously applied to this track should be copied to new executor. 15 | */ 16 | void assignExecutor(AudioTrackExecutor executor, boolean applyPrimordialState); 17 | 18 | /** 19 | * @return Get the active track executor 20 | */ 21 | AudioTrackExecutor getActiveExecutor(); 22 | 23 | /** 24 | * Perform any necessary loading and then enter the read/seek loop 25 | * 26 | * @param executor The local executor which processes this track 27 | * @throws Exception In case anything explodes. 28 | */ 29 | void process(LocalAudioTrackExecutor executor) throws Exception; 30 | 31 | /** 32 | * @param playerManager The player manager which is executing this track 33 | * @return A custom local executor for this track. Unless this track requires a special executor, this should return 34 | * null as the default one will be used in that case. 35 | */ 36 | AudioTrackExecutor createLocalExecutor(AudioPlayerManager playerManager); 37 | } 38 | -------------------------------------------------------------------------------- /main/src/main/java/com/sedmelluq/discord/lavaplayer/track/TrackMarker.java: -------------------------------------------------------------------------------- 1 | package com.sedmelluq.discord.lavaplayer.track; 2 | 3 | /** 4 | * A track position marker. This makes the specified handler get called when the specified position is reached or 5 | * reaching that position has become impossible. This guarantees that whenever a marker is set and the track is played, 6 | * its handler will always be called. 7 | */ 8 | public class TrackMarker { 9 | /** 10 | * The position of the track in milliseconds when this marker should trigger. 11 | */ 12 | public final long timecode; 13 | /** 14 | * The handler for the marker. The handler is guaranteed to be never called more than once, and guaranteed to be 15 | * called at least once if the track is started on a player. 16 | */ 17 | public final TrackMarkerHandler handler; 18 | 19 | /** 20 | * @param timecode The position of the track in milliseconds when this marker should trigger. 21 | * @param handler The handler for the marker. 22 | */ 23 | public TrackMarker(long timecode, TrackMarkerHandler handler) { 24 | this.timecode = timecode; 25 | this.handler = handler; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /main/src/main/java/com/sedmelluq/discord/lavaplayer/track/TrackMarkerHandler.java: -------------------------------------------------------------------------------- 1 | package com.sedmelluq.discord.lavaplayer.track; 2 | 3 | /** 4 | * A track marker handler. 5 | */ 6 | public interface TrackMarkerHandler { 7 | /** 8 | * @param state The state of the marker when it is triggered. 9 | */ 10 | void handle(MarkerState state); 11 | 12 | /** 13 | * The state of the marker at the moment the handle method is called. 14 | */ 15 | enum MarkerState { 16 | /** 17 | * The specified position has been reached with normal playback. 18 | */ 19 | REACHED, 20 | /** 21 | * The marker has been removed by setting the marker of the track to null. 22 | */ 23 | REMOVED, 24 | /** 25 | * The marker has been overwritten by setting the marker of the track to another non-null marker. 26 | */ 27 | OVERWRITTEN, 28 | /** 29 | * A seek was performed which jumped over the marked position. 30 | */ 31 | BYPASSED, 32 | /** 33 | * The track was stopped before it ended, before the marked position was reached. 34 | */ 35 | STOPPED, 36 | /** 37 | * The playback position was already beyond the marked position when the marker was placed. 38 | */ 39 | LATE, 40 | /** 41 | * The track ended without the marker being triggered (either due to an exception or because the track duration was 42 | * smaller than the marked position). 43 | */ 44 | ENDED 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /main/src/main/java/com/sedmelluq/discord/lavaplayer/track/TrackStateListener.java: -------------------------------------------------------------------------------- 1 | package com.sedmelluq.discord.lavaplayer.track; 2 | 3 | import com.sedmelluq.discord.lavaplayer.tools.FriendlyException; 4 | 5 | /** 6 | * Listener of track execution events. 7 | */ 8 | public interface TrackStateListener { 9 | /** 10 | * Called when an exception occurs while a track is playing or loading. This is always fatal, but it may have left 11 | * some data in the audio buffer which can still play until the buffer clears out. 12 | * 13 | * @param track The audio track for which the exception occurred 14 | * @param exception The exception that occurred 15 | */ 16 | void onTrackException(AudioTrack track, FriendlyException exception); 17 | 18 | /** 19 | * Called when an exception occurs while a track is playing or loading. This is always fatal, but it may have left 20 | * some data in the audio buffer which can still play until the buffer clears out. 21 | * 22 | * @param track The audio track for which the exception occurred 23 | * @param thresholdMs The wait threshold that was exceeded for this event to trigger 24 | */ 25 | void onTrackStuck(AudioTrack track, long thresholdMs); 26 | } 27 | -------------------------------------------------------------------------------- /main/src/main/java/com/sedmelluq/discord/lavaplayer/track/info/AudioTrackInfoProvider.java: -------------------------------------------------------------------------------- 1 | package com.sedmelluq.discord.lavaplayer.track.info; 2 | 3 | /** 4 | * Provider for audio track info. 5 | */ 6 | public interface AudioTrackInfoProvider { 7 | /** 8 | * @return Track title, or null if this provider does not know it. 9 | */ 10 | String getTitle(); 11 | 12 | /** 13 | * @return Track author, or null if this provider does not know it. 14 | */ 15 | String getAuthor(); 16 | 17 | /** 18 | * @return Track length in milliseconds, or null if this provider does not know it. 19 | */ 20 | Long getLength(); 21 | 22 | /** 23 | * @return Track identifier, or null if this provider does not know it. 24 | */ 25 | String getIdentifier(); 26 | 27 | /** 28 | * @return Track URI, or null if this provider does not know it. 29 | */ 30 | String getUri(); 31 | 32 | String getArtworkUrl(); 33 | 34 | String getISRC(); 35 | } 36 | -------------------------------------------------------------------------------- /main/src/main/java/com/sedmelluq/discord/lavaplayer/track/playback/AbstractMutableAudioFrame.java: -------------------------------------------------------------------------------- 1 | package com.sedmelluq.discord.lavaplayer.track.playback; 2 | 3 | import com.sedmelluq.discord.lavaplayer.format.AudioDataFormat; 4 | 5 | /** 6 | * Base class for mutable audio frames. 7 | */ 8 | public abstract class AbstractMutableAudioFrame implements AudioFrame { 9 | private long timecode; 10 | private int volume; 11 | private AudioDataFormat format; 12 | private boolean terminator; 13 | 14 | @Override 15 | public long getTimecode() { 16 | return timecode; 17 | } 18 | 19 | public void setTimecode(long timecode) { 20 | this.timecode = timecode; 21 | } 22 | 23 | @Override 24 | public int getVolume() { 25 | return volume; 26 | } 27 | 28 | public void setVolume(int volume) { 29 | this.volume = volume; 30 | } 31 | 32 | @Override 33 | public AudioDataFormat getFormat() { 34 | return format; 35 | } 36 | 37 | public void setFormat(AudioDataFormat format) { 38 | this.format = format; 39 | } 40 | 41 | @Override 42 | public boolean isTerminator() { 43 | return terminator; 44 | } 45 | 46 | public void setTerminator(boolean terminator) { 47 | this.terminator = terminator; 48 | } 49 | 50 | /** 51 | * @return An immutable instance created from this mutable audio frame. In an ideal flow, this should never be called. 52 | */ 53 | public ImmutableAudioFrame freeze() { 54 | return new ImmutableAudioFrame(timecode, getData(), volume, format); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /main/src/main/java/com/sedmelluq/discord/lavaplayer/track/playback/AudioFrame.java: -------------------------------------------------------------------------------- 1 | package com.sedmelluq.discord.lavaplayer.track.playback; 2 | 3 | import com.sedmelluq.discord.lavaplayer.format.AudioDataFormat; 4 | 5 | /** 6 | * Represents an audio frame. 7 | */ 8 | public interface AudioFrame { 9 | /** 10 | * @return Absolute timecode of the frame in milliseconds. 11 | */ 12 | long getTimecode(); 13 | 14 | /** 15 | * @return Volume of the current frame. 16 | */ 17 | int getVolume(); 18 | 19 | /** 20 | * @return Length of the data of this frame. 21 | */ 22 | int getDataLength(); 23 | 24 | /** 25 | * @return Byte array with the frame data. 26 | */ 27 | byte[] getData(); 28 | 29 | /** 30 | * Before calling this method, the caller should verify that the data fits in the buffer using 31 | * {@link #getDataLength()}. 32 | * 33 | * @param buffer Buffer to write the frame data to. 34 | * @param offset Offset in the buffer to start writing at. 35 | */ 36 | void getData(byte[] buffer, int offset); 37 | 38 | /** 39 | * @return The data format of this buffer. 40 | */ 41 | AudioDataFormat getFormat(); 42 | 43 | /** 44 | * @return Whether this frame is a terminator. This is an internal concept of the player and should never be 45 | * true in any frames received by the user. 46 | */ 47 | boolean isTerminator(); 48 | } 49 | -------------------------------------------------------------------------------- /main/src/main/java/com/sedmelluq/discord/lavaplayer/track/playback/AudioFrameBufferFactory.java: -------------------------------------------------------------------------------- 1 | package com.sedmelluq.discord.lavaplayer.track.playback; 2 | 3 | import com.sedmelluq.discord.lavaplayer.format.AudioDataFormat; 4 | 5 | import java.util.concurrent.atomic.AtomicBoolean; 6 | 7 | /** 8 | * Factory for audio frame buffers. 9 | */ 10 | public interface AudioFrameBufferFactory { 11 | /** 12 | * @param bufferDuration Maximum duration of the buffer. The buffer may actually hold less in case the average size of 13 | * frames exceeds {@link AudioDataFormat#expectedChunkSize()}. 14 | * @param format The format of the frames held in this buffer. 15 | * @param stopping Atomic boolean which has true value when the track is in a state of pending stop. 16 | * @return A new frame buffer instance. 17 | */ 18 | AudioFrameBuffer create(int bufferDuration, AudioDataFormat format, AtomicBoolean stopping); 19 | } 20 | -------------------------------------------------------------------------------- /main/src/main/java/com/sedmelluq/discord/lavaplayer/track/playback/AudioFrameConsumer.java: -------------------------------------------------------------------------------- 1 | package com.sedmelluq.discord.lavaplayer.track.playback; 2 | 3 | /** 4 | * A consumer for audio frames 5 | */ 6 | public interface AudioFrameConsumer { 7 | /** 8 | * Consumes the frame, may block 9 | * 10 | * @param frame The frame to consume 11 | * @throws InterruptedException When interrupted externally (or for seek/stop). 12 | */ 13 | void consume(AudioFrame frame) throws InterruptedException; 14 | 15 | /** 16 | * Rebuild all caches frames 17 | * 18 | * @param rebuilder The rebuilder to use 19 | */ 20 | void rebuild(AudioFrameRebuilder rebuilder); 21 | } 22 | -------------------------------------------------------------------------------- /main/src/main/java/com/sedmelluq/discord/lavaplayer/track/playback/AudioFrameProviderTools.java: -------------------------------------------------------------------------------- 1 | package com.sedmelluq.discord.lavaplayer.track.playback; 2 | 3 | import com.sedmelluq.discord.lavaplayer.tools.ExceptionTools; 4 | 5 | import java.util.concurrent.TimeUnit; 6 | import java.util.concurrent.TimeoutException; 7 | 8 | /** 9 | * Encapsulates common behavior shared by different audio frame providers. 10 | */ 11 | public class AudioFrameProviderTools { 12 | /** 13 | * @param provider Delegates a call to frame provide without timeout to the timed version of it. 14 | * @return The audio frame from provide method. 15 | */ 16 | public static AudioFrame delegateToTimedProvide(AudioFrameProvider provider) { 17 | try { 18 | return provider.provide(0, TimeUnit.MILLISECONDS); 19 | } catch (TimeoutException | InterruptedException e) { 20 | ExceptionTools.keepInterrupted(e); 21 | throw new RuntimeException(e); 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /main/src/main/java/com/sedmelluq/discord/lavaplayer/track/playback/AudioFrameRebuilder.java: -------------------------------------------------------------------------------- 1 | package com.sedmelluq.discord.lavaplayer.track.playback; 2 | 3 | /** 4 | * Interface for classes which can rebuild audio frames. 5 | */ 6 | public interface AudioFrameRebuilder { 7 | /** 8 | * Rebuilds a frame (for example by reencoding) 9 | * 10 | * @param frame The audio frame 11 | * @return The new frame (may be the same as input) 12 | */ 13 | AudioFrame rebuild(AudioFrame frame); 14 | } 15 | -------------------------------------------------------------------------------- /main/src/main/java/com/sedmelluq/discord/lavaplayer/track/playback/ReferenceMutableAudioFrame.java: -------------------------------------------------------------------------------- 1 | package com.sedmelluq.discord.lavaplayer.track.playback; 2 | 3 | /** 4 | * Mutable audio frame which contains no dedicated buffer, but refers to a segment in a specified byte buffer. 5 | */ 6 | public class ReferenceMutableAudioFrame extends AbstractMutableAudioFrame { 7 | private byte[] frameBuffer; 8 | private int frameOffset; 9 | private int frameLength; 10 | 11 | /** 12 | * @return The underlying byte buffer. 13 | */ 14 | public byte[] getFrameBuffer() { 15 | return frameBuffer; 16 | } 17 | 18 | /** 19 | * @return Offset of the frame data in the underlying byte buffer. 20 | */ 21 | public int getFrameOffset() { 22 | return frameOffset; 23 | } 24 | 25 | /** 26 | * @return Offset of the end of frame data in the underlying byte buffer. 27 | */ 28 | public int getFrameEndOffset() { 29 | return frameOffset + frameLength; 30 | } 31 | 32 | @Override 33 | public int getDataLength() { 34 | return frameLength; 35 | } 36 | 37 | @Override 38 | public byte[] getData() { 39 | byte[] data = new byte[frameLength]; 40 | getData(data, 0); 41 | return data; 42 | } 43 | 44 | @Override 45 | public void getData(byte[] buffer, int offset) { 46 | System.arraycopy(frameBuffer, frameOffset, buffer, offset, frameLength); 47 | } 48 | 49 | /** 50 | * @param frameBuffer See {@link #getFrameBuffer()}. 51 | * @param frameOffset See {@link #getFrameOffset()}. 52 | * @param frameLength See {@link #getDataLength()}. 53 | */ 54 | public void setDataReference(byte[] frameBuffer, int frameOffset, int frameLength) { 55 | this.frameBuffer = frameBuffer; 56 | this.frameOffset = frameOffset; 57 | this.frameLength = frameLength; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /main/src/main/java/com/sedmelluq/discord/lavaplayer/track/playback/TerminatorAudioFrame.java: -------------------------------------------------------------------------------- 1 | package com.sedmelluq.discord.lavaplayer.track.playback; 2 | 3 | import com.sedmelluq.discord.lavaplayer.format.AudioDataFormat; 4 | 5 | /** 6 | * Audio frame where {@link #isTerminator()} is true. 7 | */ 8 | public class TerminatorAudioFrame implements AudioFrame { 9 | public static final TerminatorAudioFrame INSTANCE = new TerminatorAudioFrame(); 10 | 11 | @Override 12 | public long getTimecode() { 13 | throw new UnsupportedOperationException(); 14 | } 15 | 16 | @Override 17 | public int getVolume() { 18 | throw new UnsupportedOperationException(); 19 | } 20 | 21 | @Override 22 | public int getDataLength() { 23 | throw new UnsupportedOperationException(); 24 | } 25 | 26 | @Override 27 | public byte[] getData() { 28 | throw new UnsupportedOperationException(); 29 | } 30 | 31 | @Override 32 | public void getData(byte[] buffer, int offset) { 33 | throw new UnsupportedOperationException(); 34 | } 35 | 36 | @Override 37 | public AudioDataFormat getFormat() { 38 | throw new UnsupportedOperationException(); 39 | } 40 | 41 | @Override 42 | public boolean isTerminator() { 43 | return true; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /main/src/main/resources/certificates/bundled.txt: -------------------------------------------------------------------------------- 1 | dst-root-ca-x3.jks -------------------------------------------------------------------------------- /main/src/main/resources/certificates/dst-root-ca-x3.jks: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lavalink-devs/lavaplayer/fbcc546cb6cb64ea2a7062259e67d612e20e7d1e/main/src/main/resources/certificates/dst-root-ca-x3.jks -------------------------------------------------------------------------------- /natives-publish/build.gradle.kts: -------------------------------------------------------------------------------- 1 | import com.vanniktech.maven.publish.JavaLibrary 2 | import com.vanniktech.maven.publish.JavadocJar 3 | 4 | plugins { 5 | java 6 | alias(libs.plugins.maven.publish.base) 7 | } 8 | 9 | base { 10 | archivesName = "lavaplayer-natives" 11 | } 12 | 13 | mavenPublishing { 14 | configure(JavaLibrary(JavadocJar.Javadoc())) 15 | } 16 | -------------------------------------------------------------------------------- /natives-publish/src/main/resources/natives/darwin/libconnector.dylib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lavalink-devs/lavaplayer/fbcc546cb6cb64ea2a7062259e67d612e20e7d1e/natives-publish/src/main/resources/natives/darwin/libconnector.dylib -------------------------------------------------------------------------------- /natives-publish/src/main/resources/natives/linux-aarch32/libconnector.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lavalink-devs/lavaplayer/fbcc546cb6cb64ea2a7062259e67d612e20e7d1e/natives-publish/src/main/resources/natives/linux-aarch32/libconnector.so -------------------------------------------------------------------------------- /natives-publish/src/main/resources/natives/linux-aarch64/libconnector.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lavalink-devs/lavaplayer/fbcc546cb6cb64ea2a7062259e67d612e20e7d1e/natives-publish/src/main/resources/natives/linux-aarch64/libconnector.so -------------------------------------------------------------------------------- /natives-publish/src/main/resources/natives/linux-arm/libconnector.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lavalink-devs/lavaplayer/fbcc546cb6cb64ea2a7062259e67d612e20e7d1e/natives-publish/src/main/resources/natives/linux-arm/libconnector.so -------------------------------------------------------------------------------- /natives-publish/src/main/resources/natives/linux-armhf/libconnector.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lavalink-devs/lavaplayer/fbcc546cb6cb64ea2a7062259e67d612e20e7d1e/natives-publish/src/main/resources/natives/linux-armhf/libconnector.so -------------------------------------------------------------------------------- /natives-publish/src/main/resources/natives/linux-musl-aarch64/libconnector.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lavalink-devs/lavaplayer/fbcc546cb6cb64ea2a7062259e67d612e20e7d1e/natives-publish/src/main/resources/natives/linux-musl-aarch64/libconnector.so -------------------------------------------------------------------------------- /natives-publish/src/main/resources/natives/linux-musl-x86-64/libconnector.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lavalink-devs/lavaplayer/fbcc546cb6cb64ea2a7062259e67d612e20e7d1e/natives-publish/src/main/resources/natives/linux-musl-x86-64/libconnector.so -------------------------------------------------------------------------------- /natives-publish/src/main/resources/natives/linux-x86-64/libconnector.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lavalink-devs/lavaplayer/fbcc546cb6cb64ea2a7062259e67d612e20e7d1e/natives-publish/src/main/resources/natives/linux-x86-64/libconnector.so -------------------------------------------------------------------------------- /natives-publish/src/main/resources/natives/linux-x86/libconnector.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lavalink-devs/lavaplayer/fbcc546cb6cb64ea2a7062259e67d612e20e7d1e/natives-publish/src/main/resources/natives/linux-x86/libconnector.so -------------------------------------------------------------------------------- /natives-publish/src/main/resources/natives/win-x86-64/connector.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lavalink-devs/lavaplayer/fbcc546cb6cb64ea2a7062259e67d612e20e7d1e/natives-publish/src/main/resources/natives/win-x86-64/connector.dll -------------------------------------------------------------------------------- /natives-publish/src/main/resources/natives/win-x86-64/libmpg123-0.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lavalink-devs/lavaplayer/fbcc546cb6cb64ea2a7062259e67d612e20e7d1e/natives-publish/src/main/resources/natives/win-x86-64/libmpg123-0.dll -------------------------------------------------------------------------------- /natives-publish/src/main/resources/natives/win-x86/connector.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lavalink-devs/lavaplayer/fbcc546cb6cb64ea2a7062259e67d612e20e7d1e/natives-publish/src/main/resources/natives/win-x86/connector.dll -------------------------------------------------------------------------------- /natives-publish/src/main/resources/natives/win-x86/libmpg123-0.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lavalink-devs/lavaplayer/fbcc546cb6cb64ea2a7062259e67d612e20e7d1e/natives-publish/src/main/resources/natives/win-x86/libmpg123-0.dll -------------------------------------------------------------------------------- /natives/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.0) 2 | 3 | add_subdirectory(samplerate) 4 | add_subdirectory(fdk-aac) 5 | add_subdirectory(vorbis) 6 | add_subdirectory(connector) 7 | -------------------------------------------------------------------------------- /natives/connector/connector.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #ifdef __GNUC__ 6 | #define CONNECTOR_EXPORT __attribute__ ((visibility("default"))) JNIEXPORT 7 | #else 8 | #define CONNECTOR_EXPORT JNIEXPORT 9 | #endif 10 | 11 | #ifdef MSC_VER 12 | #define CONNECTOR_IMPORT __declspec(dllimport) 13 | #else 14 | #define CONNECTOR_IMPORT 15 | #endif 16 | -------------------------------------------------------------------------------- /natives/connector/win/statistics.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | enum { 5 | SYSTEM_TOTAL, 6 | SYSTEM_USER, 7 | SYSTEM_KERNEL, 8 | PROCESS_USER, 9 | PROCESS_KERNEL 10 | }; 11 | 12 | JNIEXPORT void JNICALL Java_com_sedmelluq_discord_lavaplayer_natives_statistics_CpuStatisticsLibrary_getSystemTimes(JNIEnv *jni, jobject me, jlongArray valueArray) { 13 | jlong values[5], idle; 14 | FILETIME unused; 15 | 16 | GetSystemTimes((FILETIME*) &idle, (FILETIME*) &values[SYSTEM_KERNEL], (FILETIME*) &values[SYSTEM_USER]); 17 | values[SYSTEM_TOTAL] = values[SYSTEM_KERNEL] + values[SYSTEM_USER]; 18 | values[SYSTEM_KERNEL] -= idle; 19 | 20 | GetProcessTimes(GetCurrentProcess(), &unused, &unused, (FILETIME*) &values[PROCESS_KERNEL], (FILETIME*) &values[PROCESS_USER]); 21 | 22 | (*jni)->SetLongArrayRegion(jni, valueArray, 0, sizeof(values) / sizeof(*values), values); 23 | } 24 | -------------------------------------------------------------------------------- /natives/fdk-aac/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.0) 2 | 3 | project(fdk-aac CXX) 4 | 5 | get_filename_component(ROOT_DIR "." ABSOLUTE) 6 | 7 | set(MAIN_DIRS "${ROOT_DIR}/libAACdec" "${ROOT_DIR}/libFDK" "${ROOT_DIR}/libSYS" "${ROOT_DIR}/libMpegTPDec" "${ROOT_DIR}/libSBRdec" "${ROOT_DIR}/libPCMutils" "${ROOT_DIR}/libArithCoding" "${ROOT_DIR}/libDRCdec" "${ROOT_DIR}/libSACdec") 8 | set(MAIN_SOURCES "") 9 | 10 | message(STATUS "${MAIN_DIRS}") 11 | 12 | foreach(subdir ${MAIN_DIRS}) 13 | file (GLOB dir_sources "${subdir}/src/*.cpp" "${subdir}/src/*.h" "${subdir}/include/*.h") 14 | set (MAIN_SOURCES ${MAIN_SOURCES} ${dir_sources}) 15 | 16 | include_directories("${subdir}/include") 17 | endforeach() 18 | 19 | if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") 20 | add_definitions(-Dinline=__inline) 21 | set(CMAKE_CXX_FLAGS_RELEASE "/MT") 22 | else() 23 | add_definitions(-Wno-narrowing) 24 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fPIC -fdata-sections -ffunction-sections") 25 | endif() 26 | 27 | add_library(fdk-aac STATIC ${MAIN_SOURCES}) 28 | -------------------------------------------------------------------------------- /natives/samplerate/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.0) 2 | project(samplerate C) 3 | 4 | if(NOT ${CMAKE_C_COMPILER_ID} STREQUAL "MSVC") 5 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fPIC -fdata-sections -ffunction-sections") 6 | else() 7 | set(CMAKE_C_FLAGS_RELEASE "/MT") 8 | endif() 9 | 10 | include_directories(.) 11 | add_library(samplerate src/samplerate.c src/src_linear.c src/src_sinc.c src/src_zoh.c) 12 | -------------------------------------------------------------------------------- /natives/samplerate/config.h: -------------------------------------------------------------------------------- 1 | #pragma warning(disable: 4305) 2 | 3 | #define CPU_CLIPS_NEGATIVE 0 4 | #define CPU_CLIPS_POSITIVE 0 5 | 6 | #define HAVE_LRINT 1 7 | #define HAVE_LRINTF 1 8 | #define HAVE_STDINT_H 1 9 | 10 | #define PACKAGE "libsamplerate" 11 | #define VERSION "0.1.9" 12 | 13 | #ifdef _MSC_VER 14 | #define inline __inline 15 | #endif 16 | -------------------------------------------------------------------------------- /natives/vorbis/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.0) 2 | 3 | project(vorbis C) 4 | 5 | get_filename_component(ROOT_DIR "." ABSOLUTE) 6 | set(VORBIS_SOURCE "${ROOT_DIR}/libvorbis-1.3.6/lib") 7 | set(OGG_SOURCE "${ROOT_DIR}/libogg-1.3.3/src") 8 | 9 | if ("${CMAKE_C_COMPILER_ID}" STREQUAL "MSVC") 10 | set(CMAKE_C_FLAGS_RELEASE "/MT") 11 | else() 12 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fPIC -fdata-sections -ffunction-sections") 13 | endif() 14 | 15 | file(GLOB MAIN_SOURCES "${VORBIS_SOURCE}/*.c" "${OGG_SOURCE}/*.c") 16 | 17 | list(REMOVE_ITEM MAIN_SOURCES "${VORBIS_SOURCE}/psytune.c") 18 | list(REMOVE_ITEM MAIN_SOURCES "${VORBIS_SOURCE}/tone.c") 19 | 20 | include_directories("${ROOT_DIR}/libogg-1.3.3/include") 21 | include_directories("${ROOT_DIR}/libvorbis-1.3.6/include") 22 | include_directories("${ROOT_DIR}/libvorbis-1.3.6/lib") 23 | 24 | add_library(vorbis STATIC ${MAIN_SOURCES}) 25 | -------------------------------------------------------------------------------- /testbot/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | java 3 | application 4 | } 5 | 6 | dependencies { 7 | implementation(projects.main) 8 | implementation(libs.base64) 9 | implementation(libs.slf4j) 10 | runtimeOnly(libs.logback.classic) 11 | } 12 | 13 | application { 14 | mainClass.set("com.sedmelluq.discord.lavaplayer.demo.LocalPlayerDemo") 15 | } 16 | --------------------------------------------------------------------------------