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 |
--------------------------------------------------------------------------------