mirror of
https://github.com/collinsmith/riiablo.git
synced 2025-07-08 23:07:46 +07:00
Additional work on Bink video codec
This commit is contained in:
@ -3,6 +3,8 @@ package com.riiablo.video;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import org.apache.commons.io.FileUtils;
|
||||
|
||||
import com.badlogic.gdx.audio.AudioDevice;
|
||||
|
||||
import com.riiablo.io.BitUtils;
|
||||
import com.riiablo.io.ByteInput;
|
||||
import com.riiablo.io.InvalidFormat;
|
||||
@ -145,7 +147,7 @@ public class BIK {
|
||||
return numTracks;
|
||||
}
|
||||
|
||||
void decode(int frame) {
|
||||
void decode(int frame, AudioDevice[] audio, float[][] out) {
|
||||
final int offset = offsets[frame];
|
||||
log.tracef("offset: +%x", offset);
|
||||
final ByteBuf slice = buffer.slice(offset, offsets[frame + 1] - offset);
|
||||
@ -157,10 +159,15 @@ public class BIK {
|
||||
final int packetSize = in.readSafe32u();
|
||||
log.trace("packetSize: {} bytes", packetSize);
|
||||
|
||||
final ByteInput audioPacket = in.readSlice(packetSize);
|
||||
// final ByteInput audioPacket = in.readSlice(packetSize);
|
||||
final ByteInput audioPacket = in;
|
||||
final int numSamples = audioPacket.readSafe32u();
|
||||
log.trace("numSamples: {}", numSamples);
|
||||
|
||||
BinkAudio track = tracks[i];
|
||||
track.decode(audioPacket.unalign(), out);
|
||||
audio[i].writeSamples(out[0], 0, numSamples);
|
||||
|
||||
log.trace("bytesRemaining: {} bytes", audioPacket.bytesRemaining());
|
||||
} finally {
|
||||
MDC.remove("track");
|
||||
|
@ -1,5 +1,9 @@
|
||||
package com.riiablo.video;
|
||||
|
||||
import java.util.Arrays;
|
||||
import org.apache.commons.math3.util.FastMath;
|
||||
|
||||
import com.riiablo.io.BitInput;
|
||||
import com.riiablo.io.ByteInput;
|
||||
import com.riiablo.io.InvalidFormat;
|
||||
import com.riiablo.logger.LogManager;
|
||||
@ -10,12 +14,13 @@ public class BinkAudio {
|
||||
|
||||
static final int SIZE = 0x0C;
|
||||
static final int MAX_CHANNELS = 2;
|
||||
static final int BLOCK_MAX_SIZE = MAX_CHANNELS << 11;
|
||||
|
||||
static final int FLAG_AUDIO_16BITS = 0x4000;
|
||||
static final int FLAG_AUDIO_STEREO = 0x2000;
|
||||
static final int FLAG_AUDIO_DCT = 0x1000;
|
||||
|
||||
private static final int[] BANDS = {
|
||||
private static final int[] CRIT_FREQ = {
|
||||
100, 200, 300, 400, 510, 630, 770, 920, 1080, 1270, 1480, 1720, 2000,
|
||||
2320, 2700, 3150, 3700, 4400, 5300, 6400, 7700, 9500, 12000, 15500,
|
||||
24500
|
||||
@ -25,6 +30,10 @@ public class BinkAudio {
|
||||
2, 3, 4, 5, 6, 8, 9, 10, 11, 12, 13, 14, 15, 16, 32, 64
|
||||
};
|
||||
|
||||
private final float[] QUANTS;
|
||||
private final int[] BANDS;
|
||||
private final float[][] PREVIOUS = new float[MAX_CHANNELS][BLOCK_MAX_SIZE >>> 4];
|
||||
|
||||
final short numChannels;
|
||||
final int sampleRate;
|
||||
final int flags;
|
||||
@ -34,6 +43,10 @@ public class BinkAudio {
|
||||
final int overlapLen;
|
||||
final int blockSize;
|
||||
final int halfSampleRate;
|
||||
final int numBands;
|
||||
final float root;
|
||||
|
||||
boolean first;
|
||||
|
||||
BinkAudio(ByteInput in) {
|
||||
log.trace("slicing {} bytes", SIZE);
|
||||
@ -69,6 +82,31 @@ public class BinkAudio {
|
||||
overlapLen = frameLen >> 4;
|
||||
blockSize = (frameLen - overlapLen) * numChannels;
|
||||
halfSampleRate = (sampleRate + 1) / 2;
|
||||
root = frameLen / ((float) FastMath.sqrt(frameLen) * 32768f);
|
||||
// if coded->id == RDFT then frameLen becomes 2f
|
||||
|
||||
QUANTS = new float[96];
|
||||
for (int i = 0; i < 96; i++) {
|
||||
/* constant is result of 0.066399999/log10(M_E) */
|
||||
QUANTS[i] = (float) FastMath.exp(i * 0.15289164787221953823f) * root;
|
||||
}
|
||||
|
||||
int numBands;
|
||||
for (numBands = 1; numBands < 25 && halfSampleRate > CRIT_FREQ[numBands - 1]; numBands++);
|
||||
this.numBands = numBands;
|
||||
|
||||
BANDS = new int[numBands + 1];
|
||||
BANDS[0] = 2;
|
||||
for (int i = 1; i < numBands; i++) {
|
||||
BANDS[i] = (CRIT_FREQ[i - 1] * frameLen / halfSampleRate) & ~1;
|
||||
}
|
||||
BANDS[numBands] = frameLen;
|
||||
|
||||
first = true;
|
||||
}
|
||||
|
||||
public float[][] createOut() {
|
||||
return new float[MAX_CHANNELS][BLOCK_MAX_SIZE];
|
||||
}
|
||||
|
||||
public String getFlagsString() {
|
||||
@ -83,4 +121,91 @@ public class BinkAudio {
|
||||
public boolean isMono() {
|
||||
return (flags & FLAG_AUDIO_STEREO) == 0;
|
||||
}
|
||||
|
||||
void decode(BitInput bits, float[][] out) {
|
||||
int ch, i, j, k;
|
||||
float q;
|
||||
float[] quant = new float[25];
|
||||
int width, coeff;
|
||||
for (ch = 0; ch < numChannels; ch++) {
|
||||
final float[] coeffs = out[ch];
|
||||
coeffs[0] = readFloat29(bits) * root;
|
||||
coeffs[1] = readFloat29(bits) * root;
|
||||
|
||||
for (i = 0; i < numBands; i++) {
|
||||
final short value = bits.read8u();
|
||||
quant[i] = QUANTS[Math.min(value, 95)];
|
||||
}
|
||||
|
||||
k = 0;
|
||||
q = quant[0];
|
||||
|
||||
// parse coefficients
|
||||
i = 2;
|
||||
while (i < frameLen) {
|
||||
{
|
||||
int v = bits.read1();
|
||||
if (v != 0) {
|
||||
v = bits.read7u(4);
|
||||
j = i + RLE[v] << 3;
|
||||
} else {
|
||||
j = i + 8;
|
||||
}
|
||||
}
|
||||
|
||||
j = Math.min(j, frameLen);
|
||||
|
||||
width = bits.read7u(4);
|
||||
if (width == 0) {
|
||||
Arrays.fill(coeffs, i, coeffs.length, 0);
|
||||
i = j;
|
||||
while (BANDS[k] < i) {
|
||||
q = quant[k++];
|
||||
}
|
||||
} else {
|
||||
while (i < j) {
|
||||
if (BANDS[k] == i) {
|
||||
q = quant[k++];
|
||||
}
|
||||
coeff = bits.read31u(width);
|
||||
if (coeff != 0) {
|
||||
final int v = bits.read1();
|
||||
coeffs[i] = (v != 0 ? -q : q) * coeff;
|
||||
} else {
|
||||
coeffs[i] = 0f;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (false) { // dct stuff
|
||||
coeffs[0] /= 0.5f;
|
||||
//dct calc stuff
|
||||
} else if (false) { // rdft decoder stuff
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
for (ch = 0; ch < numChannels; ch++) {
|
||||
final float[] current = out[ch];
|
||||
final float[] previous = PREVIOUS[ch];
|
||||
int count = overlapLen * numChannels;
|
||||
if (!first) {
|
||||
j = ch;
|
||||
for (i = 0; i < overlapLen; i++, j += numChannels) {
|
||||
out[ch][i] = (previous[i] * (count - j) + current[i] * j) / count;
|
||||
}
|
||||
}
|
||||
System.arraycopy(previous, 0, current, frameLen - overlapLen, overlapLen);
|
||||
}
|
||||
|
||||
first = false;
|
||||
}
|
||||
|
||||
static float readFloat29(BitInput bits) {
|
||||
int power = bits.read32(5);
|
||||
float f = FastMath.scalb(bits.read32(23), power - 23);
|
||||
return bits.readBoolean() ? f : -f;
|
||||
}
|
||||
}
|
||||
|
50
core/src/com/riiablo/video/VideoPlayer.java
Normal file
50
core/src/com/riiablo/video/VideoPlayer.java
Normal file
@ -0,0 +1,50 @@
|
||||
package com.riiablo.video;
|
||||
|
||||
import com.badlogic.gdx.Gdx;
|
||||
import com.badlogic.gdx.audio.AudioDevice;
|
||||
import com.badlogic.gdx.graphics.g2d.Batch;
|
||||
import com.badlogic.gdx.utils.Disposable;
|
||||
|
||||
public class VideoPlayer implements Disposable {
|
||||
private BIK bik;
|
||||
private AudioDevice[] audio;
|
||||
private float[][][] out;
|
||||
|
||||
public VideoPlayer() {
|
||||
|
||||
}
|
||||
|
||||
public void play(BIK bik) {
|
||||
if (this.bik != null) {
|
||||
throw new IllegalStateException("this.bik(" + this.bik + ") is not null");
|
||||
}
|
||||
this.bik = bik;
|
||||
|
||||
audio = new AudioDevice[bik.numTracks];
|
||||
out = new float[bik.numTracks][][];
|
||||
for (int i = 0, s = bik.numTracks; i < s; i++) {
|
||||
final BinkAudio track = bik.track(i);
|
||||
// final AudioDevice device = audio[i] = Gdx.audio.newAudioDevice(track.sampleRate, track.isMono());
|
||||
final AudioDevice device = audio[i] = Gdx.audio.newAudioDevice(track.sampleRate, true);
|
||||
device.setVolume(0.10f);
|
||||
out[i] = track.createOut();
|
||||
}
|
||||
}
|
||||
|
||||
public void resize(int width, int height) {
|
||||
|
||||
}
|
||||
|
||||
int f = 0;
|
||||
public void update(float delta) {
|
||||
bik.decode(f++, audio, out[0]);
|
||||
}
|
||||
|
||||
public void draw(Batch batch) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
|
||||
}
|
||||
}
|
@ -38,7 +38,7 @@ public class BIKTest extends RiiabloTest {
|
||||
FileHandle handle = Gdx.files.internal("test\\New_Bliz640x480.bik");
|
||||
ByteBuf buffer = Unpooled.wrappedBuffer(handle.readBytes());
|
||||
BIK bik = BIK.loadFromByteBuf(buffer);
|
||||
bik.decode(0);
|
||||
// bik.decode(0, null, null);
|
||||
|
||||
AudioDevice audio = Gdx.audio.newAudioDevice(44100, false);
|
||||
// TODO: create video tool
|
||||
|
59
tools/src/com/riiablo/video/VideoTool.java
Normal file
59
tools/src/com/riiablo/video/VideoTool.java
Normal file
@ -0,0 +1,59 @@
|
||||
package com.riiablo.video;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.Unpooled;
|
||||
|
||||
import com.badlogic.gdx.ApplicationAdapter;
|
||||
import com.badlogic.gdx.Gdx;
|
||||
import com.badlogic.gdx.backends.lwjgl.LwjglApplication;
|
||||
import com.badlogic.gdx.backends.lwjgl.LwjglApplicationConfiguration;
|
||||
import com.badlogic.gdx.files.FileHandle;
|
||||
import com.badlogic.gdx.graphics.GL20;
|
||||
|
||||
public class VideoTool extends ApplicationAdapter {
|
||||
private static final String TAG = "VideoTool";
|
||||
|
||||
public static void main(String[] args) {
|
||||
LwjglApplicationConfiguration config = new LwjglApplicationConfiguration();
|
||||
config.title = TAG;
|
||||
config.resizable = false;
|
||||
config.width = 640;
|
||||
config.height = 480;
|
||||
config.vSyncEnabled = false;
|
||||
config.foregroundFPS = config.backgroundFPS = 144;
|
||||
new LwjglApplication(new VideoTool(), config);
|
||||
}
|
||||
|
||||
VideoTool() {}
|
||||
|
||||
VideoPlayer player;
|
||||
|
||||
@Override
|
||||
public void create() {
|
||||
Gdx.gl.glClearColor(0.3f, 0.3f, 0.3f, 1.0f);
|
||||
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
|
||||
|
||||
FileHandle handle = Gdx.files.internal("test\\New_Bliz640x480.bik");
|
||||
ByteBuf buffer = Unpooled.wrappedBuffer(handle.readBytes());
|
||||
BIK bik = BIK.loadFromByteBuf(buffer);
|
||||
|
||||
player = new VideoPlayer();
|
||||
player.play(bik);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resize(int width, int height) {
|
||||
player.resize(width, height);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render() {
|
||||
System.out.println("update");
|
||||
player.update(Gdx.graphics.getDeltaTime());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
player.dispose();
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user