Skip to content

Commit

Permalink
Merge branch 'main' into main-markozajc
Browse files Browse the repository at this point in the history
  • Loading branch information
topi314 committed Dec 2, 2023
2 parents 9c7f236 + ebdb88f commit 3387205
Show file tree
Hide file tree
Showing 11 changed files with 170 additions and 146 deletions.
15 changes: 15 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,20 @@
# Change Log

## [2.0.3] -- 2023-05-11

- Updated `IDENTITY_REGEX` in https://github.com/lavalink-devs/lavaplayer/pull/44

## [2.0.2] -- 2023-27-09

- Fix a bug with MPEG parsing that would lead to range exceptions in https://github.com/lavalink-devs/lavaplayer/pull/31
- Specify all request timeouts in https://github.com/lavalink-devs/lavaplayer/pull/33

## [2.0.1] -- 2023-14-08

### Added

- Support MPEG 2.5 by [@markozajc](https://github.com/markozajc) in https://github.com/lavalink-devs/lavaplayer/pull/30

## [2.0.0] -- 2023-03-08

### Fixed
Expand Down
10 changes: 9 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,14 @@ number: [![Maven Central](https://img.shields.io/maven-central/v/dev.arbjerg/lav
* Artifact: **dev.arbjerg:lavaplayer:x.y.z**

Snapshots are published
to https://maven.arbjerg.dev/snapshots & https://s01.oss.sonatype.org/content/repositories/snapshots
to https://maven.lavalink.dev/snapshots & https://s01.oss.sonatype.org/content/repositories/snapshots

Using in Gradle:

```gradle
repositories {
mavenCentral()
maven { url "https://jitpack.io" } // For com.github.walkyst.JAADec-fork:jaadec-ext-aac & ibxm-fork:com.github.walkyst:ibxm-fork
}
dependencies {
Expand All @@ -35,6 +36,13 @@ dependencies {
Using in Maven:

```xml
<repositories>
<repository>
<id>jitpack</id>
<url>https://jitpack.io</url>
</repository>
</repositories>

<dependencies>
<dependency>
<groupId>dev.arbjerg</groupId>
Expand Down
6 changes: 3 additions & 3 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,8 @@ subprojects {
configure<PublishingExtension> {
if (findProperty("MAVEN_PASSWORD") != null && findProperty("MAVEN_USERNAME") != null) {
repositories {
val snapshots = "https://maven.arbjerg.dev/snapshots"
val releases = "https://maven.arbjerg.dev/releases"
val snapshots = "https://maven.lavalink.dev/snapshots"
val releases = "https://maven.lavalink.dev/releases"

maven(if (release) releases else snapshots) {
credentials {
Expand All @@ -50,7 +50,7 @@ subprojects {
}
}
} else {
logger.lifecycle("Not publishing to maven.arbjerg.dev because credentials are not set")
logger.lifecycle("Not publishing to maven.lavalink.dev because credentials are not set")
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,16 +88,22 @@ private int copyScanBufferEndToBeginning() {
}

private boolean parseFrameAt(int scanOffset) {
if (scanOffset >= HEADER_SIZE && (frameSize = Mp3Decoder.getFrameSize(scanBuffer, scanOffset - HEADER_SIZE)) > 0) {
for (int i = 0; i < HEADER_SIZE; i++) {
frameBuffer[i] = scanBuffer[scanOffset - HEADER_SIZE + i];
}
int offset = scanOffset - HEADER_SIZE;
boolean invalid = offset < 0
|| !Mp3Decoder.hasFrameSync(scanBuffer, offset)
|| Mp3Decoder.isUnsupportedVersion(scanBuffer, offset)
|| !Mp3Decoder.isValidFrame(scanBuffer, offset);

frameBufferPosition = HEADER_SIZE;
return true;
if (invalid)
return false;

frameSize = Mp3Decoder.getFrameSize(scanBuffer, offset);
for (int i = 0; i < HEADER_SIZE; i++) {
frameBuffer[i] = scanBuffer[offset + i];
}

return false;
frameBufferPosition = HEADER_SIZE;
return true;
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

import com.sedmelluq.lava.common.natives.NativeResourceHolder;

import java.util.Arrays;

import java.nio.ByteBuffer;
import java.nio.ShortBuffer;

Expand Down Expand Up @@ -69,81 +71,7 @@ protected void freeResources() {
}

private static int getFrameBitRate(byte[] buffer, int offset) {
return isMpegVersionOne(buffer, offset) ? getFrameBitRateV1(buffer, offset) : getFrameBitRateV2(buffer, offset);
}

private static int getFrameBitRateV1(byte[] buffer, int offset) {
switch ((buffer[offset + 2] & 0xF0) >>> 4) {
case 1:
return 32000;
case 2:
return 40000;
case 3:
return 48000;
case 4:
return 56000;
case 5:
return 64000;
case 6:
return 80000;
case 7:
return 96000;
case 8:
return 112000;
case 9:
return 128000;
case 10:
return 160000;
case 11:
return 192000;
case 12:
return 224000;
case 13:
return 256000;
case 14:
return 320000;
default:
throw new IllegalArgumentException("Not valid bitrate");
}
}

private static int getFrameBitRateV2(byte[] buffer, int offset) {
switch ((buffer[offset + 2] & 0xF0) >>> 4) {
case 1:
return 8000;
case 2:
return 16000;
case 3:
return 24000;
case 4:
return 32000;
case 5:
return 40000;
case 6:
return 48000;
case 7:
return 56000;
case 8:
return 64000;
case 9:
return 80000;
case 10:
return 96000;
case 11:
return 112000;
case 12:
return 128000;
case 13:
return 144000;
case 14:
return 160000;
default:
throw new IllegalArgumentException("Not valid bitrate");
}
}

private static int calculateFrameSize(boolean isVersionOne, int bitRate, int sampleRate, boolean hasPadding) {
return (isVersionOne ? 144 : 72) * bitRate / sampleRate + (hasPadding ? 1 : 0);
return MpegVersion.getVersion(buffer, offset).getBitRate(buffer, offset);
}

/**
Expand All @@ -154,7 +82,7 @@ private static int calculateFrameSize(boolean isVersionOne, int bitRate, int sam
* @return Sample rate
*/
public static int getFrameSampleRate(byte[] buffer, int offset) {
return isMpegVersionOne(buffer, offset) ? getFrameSampleRateV1(buffer, offset) : getFrameSampleRateV2(buffer, offset);
return MpegVersion.getVersion(buffer, offset).getSampleRate(buffer, offset);
}

/**
Expand All @@ -168,30 +96,22 @@ public static int getFrameChannelCount(byte[] buffer, int offset) {
return (buffer[offset + 3] & 0xC0) == 0xC0 ? 1 : 2;
}

private static int getFrameSampleRateV1(byte[] buffer, int offset) {
switch ((buffer[offset + 2] & 0x0C) >>> 2) {
case 0:
return 44100;
case 1:
return 48000;
case 2:
return 32000;
default:
throw new IllegalArgumentException("Not valid sample rate");
}
public static boolean hasFrameSync(byte[] buffer, int offset) {
// must start with 11 high bits
return (buffer[offset] & 0xFF) == 0xFF && (buffer[offset + 1] & 0xE0) == 0xE0;
}

private static int getFrameSampleRateV2(byte[] buffer, int offset) {
switch ((buffer[offset + 2] & 0x0C) >>> 2) {
case 0:
return 22050;
case 1:
return 24000;
case 2:
return 16000;
default:
throw new IllegalArgumentException("Not valid sample rate");
}
public static boolean isUnsupportedVersion(byte[] buffer, int offset) {
return (buffer[offset + 1] & 0x18) >> 3 == 0x01;
}

public static boolean isValidFrame(byte[] buffer, int offset) {
int second = buffer[offset + 1] & 0xFF;
int third = buffer[offset + 2] & 0xFF;
return (second & 0x06) == 0x02 // Is Layer III
&& (third & 0xF0) != 0x00 // Has defined bitrate
&& (third & 0xF0) != 0xF0 // Valid bitrate
&& (third & 0x0C) != 0x0C; // Valid sampling rate
}

/**
Expand All @@ -202,26 +122,7 @@ private static int getFrameSampleRateV2(byte[] buffer, int offset) {
* @return Frame size, or zero if not a valid frame header
*/
public static int getFrameSize(byte[] buffer, int offset) {
int first = buffer[offset] & 0xFF;
int second = buffer[offset + 1] & 0xFF;
int third = buffer[offset + 2] & 0xFF;

boolean invalid = (first != 0xFF || (second & 0xE0) != 0xE0) // Frame sync does not match
|| (second & 0x10) != 0x10 // Not MPEG-1 nor MPEG-2, not dealing with this stuff
|| (second & 0x06) != 0x02 // Not Layer III, not dealing with this stuff
|| (third & 0xF0) == 0x00 // No defined bitrate
|| (third & 0xF0) == 0xF0 // Invalid bitrate
|| (third & 0x0C) == 0x0C; // Invalid sampling rate

if (invalid) {
return 0;
}

int bitRate = getFrameBitRate(buffer, offset);
int sampleRate = getFrameSampleRate(buffer, offset);
boolean hasPadding = (third & 0x02) != 0;

return calculateFrameSize(isMpegVersionOne(buffer, offset), bitRate, sampleRate, hasPadding);
return MpegVersion.getVersion(buffer, offset).getFrameSize(buffer, offset);
}

/**
Expand All @@ -232,10 +133,7 @@ public static int getFrameSize(byte[] buffer, int offset) {
* @return Average frame size, assuming CBR
*/
public static double getAverageFrameSize(byte[] buffer, int offset) {
int bitRate = getFrameBitRate(buffer, offset);
int sampleRate = getFrameSampleRate(buffer, offset);

return (isMpegVersionOne(buffer, offset) ? 144.0 : 72.0) * bitRate / sampleRate;
return MpegVersion.getVersion(buffer, offset).getAverageFrameSize(buffer, offset);
}

/**
Expand All @@ -244,14 +142,99 @@ public static double getAverageFrameSize(byte[] buffer, int offset) {
* @return Number of samples per frame.
*/
public static long getSamplesPerFrame(byte[] buffer, int offset) {
return isMpegVersionOne(buffer, offset) ? MPEG1_SAMPLES_PER_FRAME : MPEG2_SAMPLES_PER_FRAME;
}

private static boolean isMpegVersionOne(byte[] buffer, int offset) {
return (buffer[offset + 1] & 0x08) == 0x08;
return MpegVersion.getVersion(buffer, offset).getSamplesPerFrame();
}

public static int getMaximumFrameSize() {
return calculateFrameSize(true, 320000, 32000, true);
return MpegVersion.MAX_FRAME_SIZE;
}

private static final int[] SAMPLE_RATE_BASE = { 11025, 12000, 8000 };

public static enum MpegVersion {

MPEG_1(4, 1152, new int[] { 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320 }),
MPEG_2(2, 576, new int[] { 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160 }),
MPEG_2_5(1, MPEG_2.samplesPerFrame, MPEG_2.bitrateIndex);

public static final int MAX_FRAME_SIZE = getMaxFrameSize();

public static MpegVersion getVersion(byte[] buffer, int offset) {
// 0 - MPEG 2.5
// 1 - reserved (unsupported)
// 2 - MPEG 2
// 3 - MPEG 1
int index = (buffer[offset + 1] & 0x18) >> 3;
switch (index) {
case 0:
return MPEG_2_5;
case 2:
return MPEG_2;
case 3:
return MPEG_1;
default:
throw new IllegalArgumentException("Invalid version");
}
}

private static int getMaxFrameSize() {
int bitRate = MPEG_1.bitrateIndex[MPEG_1.bitrateIndex.length - 1] * 1000;
int sampleRate = MPEG_1.samplerateIndex[2];
return MPEG_1.calculateFrameSize(bitRate, sampleRate, true);
}

private final int samplesPerFrame;
private final int frameLengthMultiplier;
private final int[] bitrateIndex;
private final int[] samplerateIndex;

MpegVersion(int samplerateMultiplier, int samplesPerFrame, int[] bitrateIndex) {
this.samplesPerFrame = samplesPerFrame;
this.frameLengthMultiplier = samplesPerFrame / 8;
this.bitrateIndex = bitrateIndex;
this.samplerateIndex = Arrays.stream(SAMPLE_RATE_BASE).map(r -> r * samplerateMultiplier).toArray();
}

public int getSamplesPerFrame() {
return this.samplesPerFrame;
}

public int getFrameLengthMultiplier() {
return this.frameLengthMultiplier;
}

public int getBitRate(byte[] buffer, int offset) {
int index = (buffer[offset + 2] & 0xF0) >> 4;
if (index == 0 || index == 15)
throw new IllegalArgumentException("Invalid bitrate");

return this.bitrateIndex[index - 1] * 1000;
}

public int getSampleRate(byte[] buffer, int offset) {
int index = (buffer[offset + 2] & 0x0C) >> 2;
if (index == 3)
throw new IllegalArgumentException("Invalid samplerate");

return this.samplerateIndex[index];
}

public int getFrameSize(byte[] buffer, int offset) {
return calculateFrameSize(getBitRate(buffer, offset), getSampleRate(buffer, offset),
hasPadding(buffer, offset));
}

public double getAverageFrameSize(byte[] buffer, int offset) {
return (double) getFrameLengthMultiplier() * getBitRate(buffer, offset) / getSampleRate(buffer, offset);
}

private int calculateFrameSize(int bitRate, int sampleRate, boolean hasPadding) {
return getFrameLengthMultiplier() * bitRate / sampleRate + (hasPadding ? 1 : 0);
}

private static boolean hasPadding(byte[] buffer, int offset) {
return (buffer[offset + 2] & 0x02) != 0;
}

}
}
Loading

0 comments on commit 3387205

Please sign in to comment.