From 732e3420fb798631c0f7899b876e10fb17d9402e Mon Sep 17 00:00:00 2001 From: Martin Prokoph Date: Sun, 7 Jul 2024 20:11:57 +0200 Subject: [PATCH] PictureSign: Theora Addon Big thanks to @unascribed (https://github.com/TeamMidnightDust/PictureSign/pull/23) --- .gitignore | 26 + build.gradle | 81 + common/build.gradle | 25 + .../PictureSignTheoraClient.java | 10 + .../mixin/MixinMinecraftClient.java | 19 + .../picturesign_theora/ogv/Ogv.java | 351 +++++ .../picturesign_theora/ogv/Video.java | 359 +++++ .../picturesign_theora/ogv/VideoManager.java | 25 + .../ogv/jheora/Base64Converter.java | 90 ++ .../ogv/jheora/BlockMapping.java | 117 ++ .../ogv/jheora/CodingMode.java | 72 + .../ogv/jheora/Colorspace.java | 40 + .../ogv/jheora/Constants.java | 62 + .../ogv/jheora/Coordinate.java | 40 + .../ogv/jheora/DCTDecode.java | 827 ++++++++++ .../picturesign_theora/ogv/jheora/Debug.java | 82 + .../picturesign_theora/ogv/jheora/Decode.java | 940 +++++++++++ .../picturesign_theora/ogv/jheora/Filter.java | 394 +++++ .../ogv/jheora/FrArray.java | 419 +++++ .../picturesign_theora/ogv/jheora/FrInit.java | 288 ++++ .../ogv/jheora/HuffEntry.java | 83 + .../ogv/jheora/HuffTables.java | 538 +++++++ .../ogv/jheora/Huffman.java | 260 +++ .../ogv/jheora/JTheoraException.java | 42 + .../ogv/jheora/MemUtils.java | 116 ++ .../ogv/jheora/MotionVector.java | 37 + .../ogv/jheora/PixelFormat.java | 42 + .../ogv/jheora/Playback.java | 271 ++++ .../picturesign_theora/ogv/jheora/Quant.java | 318 ++++ .../picturesign_theora/ogv/jheora/Recon.java | 110 ++ .../picturesign_theora/ogv/jheora/Result.java | 36 + .../ogv/jheora/TheoraComment.java | 36 + .../ogv/jheora/TheoraInfo.java | 265 ++++ .../ogv/jheora/TheoraState.java | 156 ++ .../ogv/jheora/Version.java | 44 + .../ogv/jheora/YUVBuffer.java | 293 ++++ .../picturesign_theora/ogv/jheora/iDCT.java | 366 +++++ .../ogv/jogg/OggBuffer.java | 294 ++++ .../ogv/jogg/OggPacket.java | 47 + .../picturesign_theora/ogv/jogg/OggPage.java | 135 ++ .../ogv/jogg/OggStreamState.java | 526 +++++++ .../ogv/jogg/OggSyncState.java | 275 ++++ .../ogv/jorbis/CodeBook.java | 478 ++++++ .../picturesign_theora/ogv/jorbis/Drft.java | 1327 ++++++++++++++++ .../picturesign_theora/ogv/jorbis/Floor0.java | 335 ++++ .../picturesign_theora/ogv/jorbis/Floor1.java | 611 +++++++ .../ogv/jorbis/FuncFloor.java | 52 + .../ogv/jorbis/FuncMapping.java | 45 + .../ogv/jorbis/FuncResidue.java | 46 + .../ogv/jorbis/FuncTime.java | 45 + .../ogv/jorbis/InfoMode.java | 34 + .../ogv/jorbis/JOrbisException.java | 40 + .../picturesign_theora/ogv/jorbis/Lookup.java | 152 ++ .../picturesign_theora/ogv/jorbis/Lpc.java | 188 +++ .../picturesign_theora/ogv/jorbis/Lsp.java | 107 ++ .../ogv/jorbis/Mapping0.java | 375 +++++ .../picturesign_theora/ogv/jorbis/Mdct.java | 250 +++ .../ogv/jorbis/PsyInfo.java | 74 + .../ogv/jorbis/PsyLook.java | 42 + .../ogv/jorbis/Residue0.java | 330 ++++ .../ogv/jorbis/Residue1.java | 45 + .../ogv/jorbis/Residue2.java | 41 + .../ogv/jorbis/StaticCodeBook.java | 443 ++++++ .../picturesign_theora/ogv/jorbis/Time0.java | 52 + .../picturesign_theora/ogv/jorbis/Util.java | 30 + .../ogv/jorbis/VorbisBlock.java | 128 ++ .../ogv/jorbis/VorbisComment.java | 243 +++ .../ogv/jorbis/VorbisDspState.java | 376 +++++ .../ogv/jorbis/VorbisFile.java | 1398 +++++++++++++++++ .../ogv/jorbis/VorbisInfo.java | 470 ++++++ .../util/TheoraMediaHandler.java | 93 ++ .../resources/picturesign-theora.mixins.json | 11 + fabric/build.gradle | 63 + .../fabric/PictureSignTheoraClientFabric.java | 11 + fabric/src/main/resources/fabric.mod.json | 34 + gradle.properties | 24 + gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 43453 bytes gradle/wrapper/gradle-wrapper.properties | 7 + gradlew | 249 +++ gradlew.bat | 92 ++ neoforge/build.gradle | 76 + neoforge/gradle.properties | 1 + .../neoforge/PictureSignClientNeoForge.java | 13 + .../resources/META-INF/neoforge.mods.toml | 38 + neoforge/src/main/resources/icon.png | Bin 0 -> 13409 bytes settings.gradle | 15 + 86 files changed, 17041 insertions(+) create mode 100755 .gitignore create mode 100755 build.gradle create mode 100644 common/build.gradle create mode 100644 common/src/main/java/eu/midnightdust/picturesign_theora/PictureSignTheoraClient.java create mode 100644 common/src/main/java/eu/midnightdust/picturesign_theora/mixin/MixinMinecraftClient.java create mode 100644 common/src/main/java/eu/midnightdust/picturesign_theora/ogv/Ogv.java create mode 100644 common/src/main/java/eu/midnightdust/picturesign_theora/ogv/Video.java create mode 100644 common/src/main/java/eu/midnightdust/picturesign_theora/ogv/VideoManager.java create mode 100644 common/src/main/java/eu/midnightdust/picturesign_theora/ogv/jheora/Base64Converter.java create mode 100644 common/src/main/java/eu/midnightdust/picturesign_theora/ogv/jheora/BlockMapping.java create mode 100644 common/src/main/java/eu/midnightdust/picturesign_theora/ogv/jheora/CodingMode.java create mode 100644 common/src/main/java/eu/midnightdust/picturesign_theora/ogv/jheora/Colorspace.java create mode 100644 common/src/main/java/eu/midnightdust/picturesign_theora/ogv/jheora/Constants.java create mode 100644 common/src/main/java/eu/midnightdust/picturesign_theora/ogv/jheora/Coordinate.java create mode 100644 common/src/main/java/eu/midnightdust/picturesign_theora/ogv/jheora/DCTDecode.java create mode 100644 common/src/main/java/eu/midnightdust/picturesign_theora/ogv/jheora/Debug.java create mode 100644 common/src/main/java/eu/midnightdust/picturesign_theora/ogv/jheora/Decode.java create mode 100644 common/src/main/java/eu/midnightdust/picturesign_theora/ogv/jheora/Filter.java create mode 100644 common/src/main/java/eu/midnightdust/picturesign_theora/ogv/jheora/FrArray.java create mode 100644 common/src/main/java/eu/midnightdust/picturesign_theora/ogv/jheora/FrInit.java create mode 100644 common/src/main/java/eu/midnightdust/picturesign_theora/ogv/jheora/HuffEntry.java create mode 100644 common/src/main/java/eu/midnightdust/picturesign_theora/ogv/jheora/HuffTables.java create mode 100644 common/src/main/java/eu/midnightdust/picturesign_theora/ogv/jheora/Huffman.java create mode 100644 common/src/main/java/eu/midnightdust/picturesign_theora/ogv/jheora/JTheoraException.java create mode 100644 common/src/main/java/eu/midnightdust/picturesign_theora/ogv/jheora/MemUtils.java create mode 100644 common/src/main/java/eu/midnightdust/picturesign_theora/ogv/jheora/MotionVector.java create mode 100644 common/src/main/java/eu/midnightdust/picturesign_theora/ogv/jheora/PixelFormat.java create mode 100644 common/src/main/java/eu/midnightdust/picturesign_theora/ogv/jheora/Playback.java create mode 100644 common/src/main/java/eu/midnightdust/picturesign_theora/ogv/jheora/Quant.java create mode 100644 common/src/main/java/eu/midnightdust/picturesign_theora/ogv/jheora/Recon.java create mode 100644 common/src/main/java/eu/midnightdust/picturesign_theora/ogv/jheora/Result.java create mode 100644 common/src/main/java/eu/midnightdust/picturesign_theora/ogv/jheora/TheoraComment.java create mode 100644 common/src/main/java/eu/midnightdust/picturesign_theora/ogv/jheora/TheoraInfo.java create mode 100644 common/src/main/java/eu/midnightdust/picturesign_theora/ogv/jheora/TheoraState.java create mode 100644 common/src/main/java/eu/midnightdust/picturesign_theora/ogv/jheora/Version.java create mode 100644 common/src/main/java/eu/midnightdust/picturesign_theora/ogv/jheora/YUVBuffer.java create mode 100644 common/src/main/java/eu/midnightdust/picturesign_theora/ogv/jheora/iDCT.java create mode 100644 common/src/main/java/eu/midnightdust/picturesign_theora/ogv/jogg/OggBuffer.java create mode 100644 common/src/main/java/eu/midnightdust/picturesign_theora/ogv/jogg/OggPacket.java create mode 100644 common/src/main/java/eu/midnightdust/picturesign_theora/ogv/jogg/OggPage.java create mode 100644 common/src/main/java/eu/midnightdust/picturesign_theora/ogv/jogg/OggStreamState.java create mode 100644 common/src/main/java/eu/midnightdust/picturesign_theora/ogv/jogg/OggSyncState.java create mode 100644 common/src/main/java/eu/midnightdust/picturesign_theora/ogv/jorbis/CodeBook.java create mode 100644 common/src/main/java/eu/midnightdust/picturesign_theora/ogv/jorbis/Drft.java create mode 100644 common/src/main/java/eu/midnightdust/picturesign_theora/ogv/jorbis/Floor0.java create mode 100644 common/src/main/java/eu/midnightdust/picturesign_theora/ogv/jorbis/Floor1.java create mode 100644 common/src/main/java/eu/midnightdust/picturesign_theora/ogv/jorbis/FuncFloor.java create mode 100644 common/src/main/java/eu/midnightdust/picturesign_theora/ogv/jorbis/FuncMapping.java create mode 100644 common/src/main/java/eu/midnightdust/picturesign_theora/ogv/jorbis/FuncResidue.java create mode 100644 common/src/main/java/eu/midnightdust/picturesign_theora/ogv/jorbis/FuncTime.java create mode 100644 common/src/main/java/eu/midnightdust/picturesign_theora/ogv/jorbis/InfoMode.java create mode 100644 common/src/main/java/eu/midnightdust/picturesign_theora/ogv/jorbis/JOrbisException.java create mode 100644 common/src/main/java/eu/midnightdust/picturesign_theora/ogv/jorbis/Lookup.java create mode 100644 common/src/main/java/eu/midnightdust/picturesign_theora/ogv/jorbis/Lpc.java create mode 100644 common/src/main/java/eu/midnightdust/picturesign_theora/ogv/jorbis/Lsp.java create mode 100644 common/src/main/java/eu/midnightdust/picturesign_theora/ogv/jorbis/Mapping0.java create mode 100644 common/src/main/java/eu/midnightdust/picturesign_theora/ogv/jorbis/Mdct.java create mode 100644 common/src/main/java/eu/midnightdust/picturesign_theora/ogv/jorbis/PsyInfo.java create mode 100644 common/src/main/java/eu/midnightdust/picturesign_theora/ogv/jorbis/PsyLook.java create mode 100644 common/src/main/java/eu/midnightdust/picturesign_theora/ogv/jorbis/Residue0.java create mode 100644 common/src/main/java/eu/midnightdust/picturesign_theora/ogv/jorbis/Residue1.java create mode 100644 common/src/main/java/eu/midnightdust/picturesign_theora/ogv/jorbis/Residue2.java create mode 100644 common/src/main/java/eu/midnightdust/picturesign_theora/ogv/jorbis/StaticCodeBook.java create mode 100644 common/src/main/java/eu/midnightdust/picturesign_theora/ogv/jorbis/Time0.java create mode 100644 common/src/main/java/eu/midnightdust/picturesign_theora/ogv/jorbis/Util.java create mode 100644 common/src/main/java/eu/midnightdust/picturesign_theora/ogv/jorbis/VorbisBlock.java create mode 100644 common/src/main/java/eu/midnightdust/picturesign_theora/ogv/jorbis/VorbisComment.java create mode 100644 common/src/main/java/eu/midnightdust/picturesign_theora/ogv/jorbis/VorbisDspState.java create mode 100644 common/src/main/java/eu/midnightdust/picturesign_theora/ogv/jorbis/VorbisFile.java create mode 100644 common/src/main/java/eu/midnightdust/picturesign_theora/ogv/jorbis/VorbisInfo.java create mode 100644 common/src/main/java/eu/midnightdust/picturesign_theora/util/TheoraMediaHandler.java create mode 100755 common/src/main/resources/picturesign-theora.mixins.json create mode 100644 fabric/build.gradle create mode 100755 fabric/src/main/java/eu/midnightdust/picturesign_theora/fabric/PictureSignTheoraClientFabric.java create mode 100755 fabric/src/main/resources/fabric.mod.json create mode 100755 gradle.properties create mode 100755 gradle/wrapper/gradle-wrapper.jar create mode 100755 gradle/wrapper/gradle-wrapper.properties create mode 100755 gradlew create mode 100755 gradlew.bat create mode 100644 neoforge/build.gradle create mode 100644 neoforge/gradle.properties create mode 100644 neoforge/src/main/java/eu/midnightdust/picturesign_theora/neoforge/PictureSignClientNeoForge.java create mode 100644 neoforge/src/main/resources/META-INF/neoforge.mods.toml create mode 100755 neoforge/src/main/resources/icon.png create mode 100755 settings.gradle diff --git a/.gitignore b/.gitignore new file mode 100755 index 0000000..7b75302 --- /dev/null +++ b/.gitignore @@ -0,0 +1,26 @@ +# gradle + +.gradle/ +out/ +classes/ +build/ + +# idea + +.idea/ +*.iml +*.ipr +*.iws + +# vscode + +.settings/ +.vscode/ +bin/ +.classpath +.project + +# fabric + +run/ +/.architectury-transformer diff --git a/build.gradle b/build.gradle new file mode 100755 index 0000000..01a7a1d --- /dev/null +++ b/build.gradle @@ -0,0 +1,81 @@ +plugins { + id "architectury-plugin" version "3.4-SNAPSHOT" + id "dev.architectury.loom" version "1.6-SNAPSHOT" apply false + //id "me.shedaniel.unified-publishing" version "0.1.+" apply false + id 'com.github.johnrengelman.shadow' version '8.1.1' apply false +} + +architectury { + minecraft = rootProject.minecraft_version +} + +repositories { + maven { + url = "https://api.modrinth.com/maven" + } + maven { url 'https://jitpack.io' } +} + +subprojects { + apply plugin: "dev.architectury.loom" + repositories { + maven { + url = "https://api.modrinth.com/maven" + } + maven { url 'https://jitpack.io' } + } + + dependencies { + minecraft "com.mojang:minecraft:${rootProject.minecraft_version}" + // The following line declares the mojmap mappings, you may use other mappings as well + //mappings loom.officialMojangMappings() + // The following line declares the yarn mappings you may select this one as well. + mappings loom.layered { + it.mappings("net.fabricmc:yarn:$rootProject.yarn_mappings:v2") + it.mappings("dev.architectury:yarn-mappings-patch-neoforge:$rootProject.yarn_mappings_patch_neoforge_version") + } + } +} + + +allprojects { + apply plugin: "java" + apply plugin: "architectury-plugin" + apply plugin: "maven-publish" + + archivesBaseName = rootProject.archives_base_name + version = rootProject.mod_version + group = rootProject.maven_group + + repositories { + // Add repositories to retrieve artifacts from in here. + // You should only use this when depending on other mods because + // Loom adds the essential maven repositories to download Minecraft and libraries from automatically. + // See https://docs.gradle.org/current/userguide/declaring_repositories.html + // for more information about repositories. + } + + tasks.withType(JavaCompile) { + options.encoding = "UTF-8" + options.release = 21 + } + ext { + releaseChangelog = { + def changes = new StringBuilder() + changes << "## PictureSignTheora v$project.version for $project.minecraft_version\n[View the changelog](https://www.github.com/TeamMidnightDust/PictureSignTheora/commits/)" + def proc = "git log --max-count=1 --pretty=format:%s".execute() + proc.in.eachLine { line -> + def processedLine = line.toString() + if (!processedLine.contains("New translations") && !processedLine.contains("Merge") && !processedLine.contains("branch")) { + changes << "\n- ${processedLine.capitalize()}" + } + } + proc.waitFor() + return changes.toString() + } + } + + java { + withSourcesJar() + } +} diff --git a/common/build.gradle b/common/build.gradle new file mode 100644 index 0000000..17a3810 --- /dev/null +++ b/common/build.gradle @@ -0,0 +1,25 @@ +architectury { + common(rootProject.enabled_platforms.split(",")) +} + +dependencies { + // We depend on fabric loader here to use the fabric @Environment annotations and get the mixin dependencies + // Do NOT use other classes from fabric loader + modImplementation "net.fabricmc:fabric-loader:${rootProject.fabric_loader_version}" + modCompileOnlyApi "maven.modrinth:midnightlib:${rootProject.midnightlib_version}-fabric" + modCompileOnlyApi "maven.modrinth:picturesign:${rootProject.picturesign_version}-fabric" +} + +publishing { + publications { + mavenCommon(MavenPublication) { + artifactId = rootProject.archives_base_name + from components.java + } + } + + // See https://docs.gradle.org/current/userguide/publishing_maven.html for information on how to set up publishing. + repositories { + // Add repositories to publish to here. + } +} diff --git a/common/src/main/java/eu/midnightdust/picturesign_theora/PictureSignTheoraClient.java b/common/src/main/java/eu/midnightdust/picturesign_theora/PictureSignTheoraClient.java new file mode 100644 index 0000000..d26ca22 --- /dev/null +++ b/common/src/main/java/eu/midnightdust/picturesign_theora/PictureSignTheoraClient.java @@ -0,0 +1,10 @@ +package eu.midnightdust.picturesign_theora; + +import eu.midnightdust.picturesign.util.MediaHandler; +import eu.midnightdust.picturesign_theora.util.TheoraMediaHandler; + +public class PictureSignTheoraClient { + public static void init() { + MediaHandler.registerHandler(TheoraMediaHandler::new); + } +} diff --git a/common/src/main/java/eu/midnightdust/picturesign_theora/mixin/MixinMinecraftClient.java b/common/src/main/java/eu/midnightdust/picturesign_theora/mixin/MixinMinecraftClient.java new file mode 100644 index 0000000..5194981 --- /dev/null +++ b/common/src/main/java/eu/midnightdust/picturesign_theora/mixin/MixinMinecraftClient.java @@ -0,0 +1,19 @@ +package eu.midnightdust.picturesign_theora.mixin; + +import eu.midnightdust.picturesign_theora.util.TheoraMediaHandler; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +import net.minecraft.client.MinecraftClient; + +@Mixin(MinecraftClient.class) +public class MixinMinecraftClient { + + @Inject(at=@At("HEAD"), method="render") + public void picturesign$updateVideos(boolean b, CallbackInfo ci) { + TheoraMediaHandler.manager.update(); + } + +} diff --git a/common/src/main/java/eu/midnightdust/picturesign_theora/ogv/Ogv.java b/common/src/main/java/eu/midnightdust/picturesign_theora/ogv/Ogv.java new file mode 100644 index 0000000..ba0a9b7 --- /dev/null +++ b/common/src/main/java/eu/midnightdust/picturesign_theora/ogv/Ogv.java @@ -0,0 +1,351 @@ +package eu.midnightdust.picturesign_theora.ogv; + +import eu.midnightdust.picturesign_theora.ogv.jheora.TheoraComment; +import eu.midnightdust.picturesign_theora.ogv.jheora.TheoraInfo; +import eu.midnightdust.picturesign_theora.ogv.jheora.TheoraState; +import eu.midnightdust.picturesign_theora.ogv.jheora.YUVBuffer; +import eu.midnightdust.picturesign_theora.ogv.jogg.OggPacket; +import eu.midnightdust.picturesign_theora.ogv.jogg.OggPage; +import eu.midnightdust.picturesign_theora.ogv.jogg.OggStreamState; +import eu.midnightdust.picturesign_theora.ogv.jogg.OggSyncState; +import eu.midnightdust.picturesign_theora.ogv.jorbis.VorbisBlock; +import eu.midnightdust.picturesign_theora.ogv.jorbis.VorbisDspState; +import eu.midnightdust.picturesign_theora.ogv.jorbis.VorbisComment; +import eu.midnightdust.picturesign_theora.ogv.jorbis.VorbisInfo; + +import java.io.IOException; +import java.io.InputStream; + +public class Ogv { + public record VideoFrame(int width, int height, int[] rgba, long durationNs) {} + + private final InputStream in; + + public Ogv(InputStream in) throws IOException { + this.in = in; + + initialize(); + } + + public TheoraInfo getTheoraInfo() { + return ti; + } + + public VorbisInfo getVorbisInfo() { + return vi; + } + + private final OggPacket op = new OggPacket(); + private final OggSyncState oy = new OggSyncState(); + private final OggPage og = new OggPage(); + private OggStreamState vo = new OggStreamState(); + private OggStreamState to = new OggStreamState(); + private final TheoraInfo ti = new TheoraInfo(); + private final TheoraComment tc = new TheoraComment(); + private final TheoraState td = new TheoraState(); + private final VorbisInfo vi = new VorbisInfo(); + private final VorbisDspState vd = new VorbisDspState(); + private final VorbisBlock vb = new VorbisBlock(this.vd); + private VorbisComment vc = new VorbisComment(); + private int theora_p = 0; + private int vorbis_p = 0; + private int stateflag = 0; + private int videobuf_ready = 0; + private int audiobuf_fill = 0; + private int audiobuf_ready = 0; + private short[] audiobuf; + private int audiofd_fragsize; + private boolean end = false; + + private long granule = 0; + private VideoFrame video; + private short[] audio; + + public long getGranule() { + return granule; + } + + public VideoFrame getVideo() { + var v = video; + video = null; + return v; + } + + public short[] getAudio() { + var a = audio; + audio = null; + return a; + } + + public boolean isEnd() { + return end; + } + + private int buffer_data() throws IOException { + int fill = oy.buffer(4096); + byte[] buffer2 = oy.data; + int bytes = in.read(buffer2, fill, 4096); + if (bytes < 0) { + return bytes; + } else { + oy.wrote(bytes); + return bytes; + } + } + + void video_write() { + YUVBuffer yuv = new YUVBuffer(); + this.td.decodeYUVout(yuv); + int[] pixels = yuv.produce(); + video = new VideoFrame(yuv.y_width, yuv.y_height, pixels, (ti.fps_denominator*1_000_000_000L)/ti.fps_numerator); + } + + void audio_write() { + audio = audiobuf.clone(); + } + + int queue_page(OggPage page) { + if (this.theora_p != 0) { + this.to.pagein(this.og); + } + + if (this.vorbis_p != 0) { + this.vo.pagein(this.og); + } + + return 0; + } + + private void initialize() throws IOException { + this.oy.init(); + this.vi.init(); + this.vc.init(); + + while (this.stateflag == 0) { + int read = this.buffer_data(); + if (read <= 0) { + break; + } + + while (this.oy.pageout(this.og) > 0) { + OggStreamState test = new OggStreamState(); + if (this.og.bos() == 0) { + this.queue_page(this.og); + this.stateflag = 1; + break; + } + + test.init(this.og.serialno()); + test.pagein(this.og); + test.packetout(op); + if (this.theora_p == 0 && this.ti.decodeHeader(this.tc, op) >= 0) { + this.to = test; + this.theora_p = 1; + } else if (this.vorbis_p == 0 && this.vi.synthesis_headerin(this.vc, op) >= 0) { + this.vo = test; + this.vorbis_p = 1; + } else { + test.clear(); + } + } + } + + while (this.theora_p != 0 && this.theora_p < 3 || this.vorbis_p != 0 && this.vorbis_p < 3) { + int err; + if (this.theora_p != 0 && this.theora_p < 3 && (err = this.to.packetout(op)) != 0) { + if (err < 0) { + System.err.printf("Error parsing first Theora packet; corrupt stream? error %d\n", err); + end = true; + return; + } + + if ((err = this.ti.decodeHeader(this.tc, op)) != 0) { + System.err.printf("Error parsing Theora stream headers; corrupt stream? error %d\n", err); + end = true; + return;// if (this.vorbis_p != 0) { +// this.vo.clear(); +// this.vb.clear(); +// this.vd.clear(); +// this.vi.clear(); +// } + // +// if (this.theora_p != 0) { +// this.to.clear(); +// this.td.clear(); +// this.ti.clear(); +// } + } + + ++this.theora_p; + if (this.theora_p != 3) { + continue; + } + } + + while (true) { + if (this.vorbis_p != 0 && this.vorbis_p < 3 && (err = this.vo.packetout(op)) != 0) { + if (err < 0) { + System.err.printf("Error parsing Vorbis stream headers; corrupt stream? error %d\n", err); + end = true; + return; + } + + if ((err = this.vi.synthesis_headerin(this.vc, op)) != 0) { + System.err.printf("Error parsing Vorbis stream headers; corrupt stream? error %d\n", err); + end = true; + return; + } + + ++this.vorbis_p; + if (this.vorbis_p != 3) { + continue; + } + } + + if (this.oy.pageout(this.og) > 0) { + this.queue_page(this.og); + } else { + int ret2 = this.buffer_data(); + if (ret2 <= 0) { + System.err.print("End of file while searching for codec headers.\n"); + end = true; + return; + } + } + break; + } + } + + if (this.theora_p != 0) { + this.td.decodeInit(this.ti); +// System.out +// .printf( +// "Ogg logical stream %x is Theora %dx%d %.02f fps", +// getSerialNo(this.to), +// this.ti.width, +// this.ti.height, +// (double) this.ti.fps_numerator / (double) this.ti.fps_denominator); +// if (this.ti.width != this.ti.frame_width || this.ti.height != this.ti.frame_height) { +// System.out +// .printf(" Frame content is %dx%d with offset (%d,%d).\n", this.ti.frame_width, this.ti.frame_height, this.ti.offset_x, this.ti.offset_y); +// } + } else { + this.ti.clear(); + } + + if (this.vorbis_p != 0) { + this.vd.synthesis_init(this.vi); + this.vb.init(this.vd); + audiobuf = new short[vi.rate/4]; + audiofd_fragsize = vi.rate/2; +// System.out.printf("Ogg logical stream %x is Vorbis %d channel %d Hz audio.\n", getSerialNo(this.vo), this.vi.channels, this.vi.rate); + } else { + this.vi.clear(); + } + + this.stateflag = 0; + } + + public void step() throws IOException { + if (this.vorbis_p != 0 && this.audiobuf_ready == 0) { + float[][][] pcm = new float[1][][]; + int[] index = new int[this.vi.channels]; + int ret; + if ((ret = this.vd.synthesis_pcmout(pcm, index)) > 0) { + float[][] floatArrays = pcm[0]; + int count = this.audiobuf_fill / 2; + int maxsamples = (this.audiofd_fragsize - this.audiobuf_fill) / 2 / this.vi.channels; + + int i; + for (i = 0; i < ret && i < maxsamples; ++i) { + for (int j = 0; j < this.vi.channels; ++j) { + int val = Math.round(floatArrays[j][index[j] + i] * 32767.0F); + if (val > 32767) { + val = 32767; + } + + if (val < -32768) { + val = -32768; + } + + this.audiobuf[count++] = (short) val; + } + } + + + this.vd.synthesis_read(i); + this.audiobuf_fill += i * this.vi.channels * 2; + if (this.audiobuf_fill == this.audiofd_fragsize) { + this.audiobuf_ready = 1; + } + return; + } + + if (this.vo.packetout(op) > 0) { + if (this.vb.synthesis(op) == 0) { + this.vd.synthesis_blockin(this.vb); + } + return; + } + } + + while (this.theora_p != 0 && this.videobuf_ready == 0 && this.to.packetout(op) > 0) { + this.td.decodePacketin(op); + this.videobuf_ready = 1; + } + + if (this.videobuf_ready == 0 || this.audiobuf_ready == 0) { + int bytes = this.buffer_data(); + if (bytes < 0) { + while (this.oy.pageout(this.og) > 0) { + this.queue_page(this.og); + } + + if (this.videobuf_ready == 0 && this.audiobuf_ready == 0) { + close(); + return; + } + } + + while (this.oy.pageout(this.og) > 0) { + this.queue_page(this.og); + } + } + + if (this.stateflag != 0 && this.audiobuf_ready != 0) { + this.audio_write(); + this.audiobuf_fill = 0; + this.audiobuf_ready = 0; + } + + if (this.stateflag != 0 && this.videobuf_ready != 0) { + this.video_write(); + this.videobuf_ready = 0; + } + + if ((this.theora_p == 0 || this.videobuf_ready != 0) && (this.vorbis_p == 0 || this.audiobuf_ready != 0)) { + this.stateflag = 1; + } + + granule = op.granulepos; + } + + public void close() throws IOException { +// if (this.vorbis_p != 0) { +// this.vo.clear(); +// this.vb.clear(); +// this.vd.clear(); +// this.vi.clear(); +// } +// +// if (this.theora_p != 0) { +// this.to.clear(); +// this.td.clear(); +// this.ti.clear(); +// } + +// this.oy.clear(); + in.close(); + end = true; + } +} diff --git a/common/src/main/java/eu/midnightdust/picturesign_theora/ogv/Video.java b/common/src/main/java/eu/midnightdust/picturesign_theora/ogv/Video.java new file mode 100644 index 0000000..b895709 --- /dev/null +++ b/common/src/main/java/eu/midnightdust/picturesign_theora/ogv/Video.java @@ -0,0 +1,359 @@ +package eu.midnightdust.picturesign_theora.ogv; + +import java.io.IOException; +import java.io.InputStream; +import java.io.InterruptedIOException; +import java.net.SocketException; +import java.net.URI; +import java.net.http.HttpClient; +import java.net.http.HttpClient.Redirect; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse.BodyHandler; +import java.net.http.HttpResponse.BodyHandlers; +import java.nio.IntBuffer; +import java.nio.channels.ClosedChannelException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.time.Duration; +import java.util.ArrayDeque; +import java.util.concurrent.ArrayBlockingQueue; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Function; + +import org.lwjgl.system.MemoryUtil; + +import com.mojang.blaze3d.platform.GlStateManager; + +import com.google.common.io.ByteSource; +import com.google.common.io.MoreFiles; + +import eu.midnightdust.picturesign_theora.ogv.Ogv.VideoFrame; +import it.unimi.dsi.fastutil.ints.IntArrayList; +import it.unimi.dsi.fastutil.ints.IntList; +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.texture.AbstractTexture; +import net.minecraft.resource.ResourceManager; +import net.minecraft.sound.SoundCategory; +import net.minecraft.util.Identifier; + +import static org.lwjgl.opengl.GL12C.*; +import static org.lwjgl.openal.AL11.*; + +public class Video { + + private static final HttpClient client = HttpClient.newBuilder() + .followRedirects(Redirect.NORMAL) + .connectTimeout(Duration.ofSeconds(30)) + .build(); + + private IntBuffer nativeBuffer; + + private static final AtomicInteger nextId = new AtomicInteger(); + + private volatile AtomicBoolean alive = new AtomicBoolean(true); + private volatile boolean reset = false; + private boolean destroyed = false; + + private String url = null; + private boolean playing = false; + private volatile long time = 0; + + private volatile ArrayBlockingQueue videoQueue = new ArrayBlockingQueue<>(30); + + //private final Identifier textureId; + private final AbstractTexture tex; + private volatile Ogv ogv; + private boolean repeat = false; + private Thread thread; + private volatile int alSourceName = -1; + private final ArrayDeque unusedBuffers = new ArrayDeque<>(); + private final IntList allBuffers = new IntArrayList(); + + private volatile long seekTarget = -1; + private volatile boolean audioReady = false; + private boolean stereo = false; + + private long nextFrameUpdate = 0; + + //private float x, y, z; + private int volume; + + public Video(Identifier id) { + //this.textureId = new Identifier("dynamic/picturesign/video_"+nextId.getAndIncrement()); + tex = new AbstractTexture() { + @Override public void load(ResourceManager manager) throws IOException {} + }; + tex.setFilter(true, false); + //MinecraftClient.getInstance().getTextureManager().registerTexture(textureId, tex); + } + + public void update() { + if (ogv != null && playing) { + var now = System.nanoTime(); + + if (!audioReady && videoQueue.remainingCapacity() > 0) { + // don't play video until audio is ready unless the video queue is full + return; + } + + VideoFrame frame = null; + while (now > nextFrameUpdate) { + frame = videoQueue.poll(); + if (frame == null) return; + if (nextFrameUpdate == 0) { + nextFrameUpdate = now; + } else { + nextFrameUpdate += frame.durationNs(); + } + } + + if (frame == null) return; + if (nativeBuffer == null) { + nativeBuffer = MemoryUtil.memCallocInt(frame.rgba().length); + } else if (nativeBuffer.capacity() < frame.rgba().length) { + nativeBuffer = MemoryUtil.memRealloc(nativeBuffer, frame.rgba().length); + } + nativeBuffer.clear(); + nativeBuffer.put(frame.rgba()).flip(); + GlStateManager._bindTexture(tex.getGlId()); // do it directly to dodge sketchy mixins + GlStateManager._pixelStore(GL_UNPACK_ROW_LENGTH, 0); + GlStateManager._pixelStore(GL_UNPACK_SKIP_ROWS, 0); + GlStateManager._pixelStore(GL_UNPACK_SKIP_PIXELS, 0); + GlStateManager._pixelStore(GL_UNPACK_ALIGNMENT, 4); + GlStateManager._texImage2D(GL_TEXTURE_2D, 0, GL_RGB, frame.width(), frame.height(), 0, GL_BGRA, GL_UNSIGNED_BYTE, nativeBuffer); + + if (alSourceName != -1) { + alSourcef(alSourceName, AL_GAIN, volume); + if (alGetSourcei(alSourceName, AL_SOURCE_STATE) != AL_PLAYING) { + alSourcePlay(alSourceName); + alSourcei(alSourceName, AL_DISTANCE_MODEL, AL_LINEAR_DISTANCE); + alSourcei(alSourceName, AL_SOURCE_RELATIVE, AL_FALSE); + alSourcef(alSourceName, AL_MAX_DISTANCE, 24); + alSourcef(alSourceName, AL_ROLLOFF_FACTOR, 1); + alSourcef(alSourceName, AL_REFERENCE_DISTANCE, 0); + } + } + } + } + + public void destroy() { + if (ogv != null) { + try { + ogv.close(); + } catch (IOException ignored) {} + } + playing = false; + alive.set(false); + if (alSourceName != -1) { + alDeleteSources(alSourceName); + alDeleteBuffers(allBuffers.toIntArray()); + allBuffers.clear(); + alSourceName = -1; + } + if (nativeBuffer != null) MemoryUtil.memFree(nativeBuffer); + nativeBuffer = null; + destroyed = true; + //MinecraftClient.getInstance().getTextureManager().destroyTexture(textureId); + } + + public void stop() { + playing = false; + } + + public void play(String url) { + if (thread != null) { + if (url.equals(this.url)) { + playing = true; + return; + } + if (alSourceName != -1) { + alDeleteSources(alSourceName); + alDeleteBuffers(allBuffers.toIntArray()); + allBuffers.clear(); + alSourceName = -1; + } + var oldVideoQueue = this.videoQueue; + var oldAlive = this.alive; + videoQueue = new ArrayBlockingQueue<>(10); + alive = new AtomicBoolean(true); + // clear old queues to unblock the thread so it realizes it should exit + oldAlive.set(false); + oldVideoQueue.clear(); + } + playing = true; + this.url = url; + // make local references so we can abandon old threads without having to wait for them to exit + var alive = this.alive; + var videoQueue = this.videoQueue; + thread = new Thread(() -> { + Path tmpFile = null; + try { + var req = HttpRequest.newBuilder(URI.create(url)) + .header("User-Agent", "PictureSign Theora") + .header("Accept", "video/ogg; codecs=\"theora, vorbis\"") + .build(); + ByteSource src; + if (repeat) { + // assume it's a short video that we don't want to be constantly redownloading + tmpFile = Files.createTempFile("picturesign", ".ogv"); + src = retrying(req, BodyHandlers.ofFile(tmpFile), MoreFiles::asByteSource); + } else { + src = new ByteSource() { + @Override + public InputStream openStream() throws IOException { + try { + return retrying(req, BodyHandlers.ofInputStream(), t -> t); + } catch (InterruptedException e) { + throw new InterruptedIOException(); + } + } + }; + } + if (src == null) return; + ogv = new Ogv(src.openStream()); + if (ogv.getVorbisInfo().channels == 0) { + // there will be no audio data, let the video run free + audioReady = true; + } + while (alive.get()) { + ogv.step(); + if (ogv.isEnd() || reset) { + ogv.close(); + if (repeat || reset) { + reset = false; + ogv = new Ogv(src.openStream()); + continue; + } else { + ogv = null; + } + playing = false; + break; + } + var vid = ogv.getVideo(); + if (vid != null) { + var info = ogv.getTheoraInfo(); + time += (info.fps_denominator*1000)/info.fps_numerator; + } + var aud = ogv.getAudio(); + if (seekTarget != -1) { + if (time < seekTarget) continue; + seekTarget = -1; + } + if (aud != null) { + if (alSourceName == -1) { + alSourceName = alGenSources(); + alSourcef(alSourceName, AL_GAIN, volume); + if (ogv.getVorbisInfo().channels == 2) { + stereo = true; + } else { + stereo = false; + } + } + int[] unqueuedNames = new int[1]; + while (true) { + unqueuedNames[0] = 0; + alSourceUnqueueBuffers(alSourceName, unqueuedNames); + if (alGetError() == 0 && unqueuedNames[0] != 0) { + unusedBuffers.add(unqueuedNames[0]); + } else { + break; + } + } + + var buffer = unusedBuffers.poll(); + if (buffer == null) { + buffer = alGenBuffers(); + allBuffers.add(buffer.intValue()); + } + alBufferData(buffer, ogv.getVorbisInfo().channels == 2 ? AL_FORMAT_STEREO16 : AL_FORMAT_MONO16, aud, ogv.getVorbisInfo().rate); + alSourceQueueBuffers(alSourceName, buffer); + if (!audioReady && alGetSourcei(alSourceName, AL_BUFFERS_QUEUED) > 12) { + audioReady = true; + } + } + if (vid != null) { + videoQueue.put(vid); + } + } + } catch (ClosedChannelException | SocketException e) { + // ogv got closed by destroy + } catch (IOException e) { + e.printStackTrace(); + System.err.println("IO exception while decoding video from "+url); + } catch (InterruptedException e) { + throw new AssertionError(e); + } catch (IllegalArgumentException e) { + // bad url + } finally { + try { + if (tmpFile != null) Files.delete(tmpFile); + } catch (IOException e) {} + } + }, "Video decode thread ("+url+")"); + thread.setDaemon(true); + thread.start(); + } + + private static T retrying(HttpRequest req, BodyHandler bodyHandler, Function callback) throws IOException, InterruptedException { + int retries = 0; + while (true) { + var res = client.send(req, bodyHandler); + if (res.statusCode()/100 == 2) { + return callback.apply(res.body()); + } else if (res.statusCode()/100 == 4) { + System.err.println("Server returned non-transient error "+res.statusCode()+" for "+req.uri()); + return null; + } else { + retries++; + if (retries > 5) { + System.err.println("Maximum retries exceeded, giving up."); + return null; + } else { + System.err.println("Server returned error "+res.statusCode()+" for "+req.uri()+" - retrying in "+retries+"s"); + } + Thread.sleep(retries*1000); + } + } + } + + public boolean hasMedia() { + // returning true here will make the video not get updated when the sign is changed + return false; + } + public boolean isStopped() { + return !playing; + } + + public void setRepeat(boolean value) { + this.repeat = value; + if (value && !playing && url != null) { + play(url); + } + } + + public long getTime() { + return ogv == null ? 0 : time; + } + + public void setTime(long value) { + if (value > time) { + reset = true; + } + seekTarget = value; + videoQueue.clear(); + } + + public int getTextureId() { + return tex.getGlId(); + } + + public float getFrameRate() { + return ogv == null ? 0 : ogv.getTheoraInfo().fps_numerator/((float)ogv.getTheoraInfo().fps_denominator); + } + + public void setVolume(int volume) { + this.volume = volume; + } + +} diff --git a/common/src/main/java/eu/midnightdust/picturesign_theora/ogv/VideoManager.java b/common/src/main/java/eu/midnightdust/picturesign_theora/ogv/VideoManager.java new file mode 100644 index 0000000..db2d36f --- /dev/null +++ b/common/src/main/java/eu/midnightdust/picturesign_theora/ogv/VideoManager.java @@ -0,0 +1,25 @@ +package eu.midnightdust.picturesign_theora.ogv; + +import java.util.HashMap; +import java.util.Map; + +import net.minecraft.util.Identifier; + +public class VideoManager { + + private final Map videos = new HashMap<>(); + + public void update() { + videos.values().forEach(Video::update); + } + + public void closePlayer(Identifier id) { + var v = videos.remove(id); + if (v != null) v.destroy(); + } + + public Video getOrCreate(Identifier id) { + return videos.computeIfAbsent(id, Video::new); + } + +} diff --git a/common/src/main/java/eu/midnightdust/picturesign_theora/ogv/jheora/Base64Converter.java b/common/src/main/java/eu/midnightdust/picturesign_theora/ogv/jheora/Base64Converter.java new file mode 100644 index 0000000..30d9c4a --- /dev/null +++ b/common/src/main/java/eu/midnightdust/picturesign_theora/ogv/jheora/Base64Converter.java @@ -0,0 +1,90 @@ +/* Cortado - a video player java applet + * Copyright (C) 2004 Fluendo S.L. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA. + */ + +package eu.midnightdust.picturesign_theora.ogv.jheora; + +public class Base64Converter +{ + public static final char[] alphabet = { + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', // 0 to 7 + 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', // 8 to 15 + 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', // 16 to 23 + 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', // 24 to 31 + 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', // 32 to 39 + 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', // 40 to 47 + 'w', 'x', 'y', 'z', '0', '1', '2', '3', // 48 to 55 + '4', '5', '6', '7', '8', '9', '+', '/' + }; // 56 to 63 + + + public static String encode (byte[]octetString) + { + int bits24; + int bits6; + + char[] out = new char[((octetString.length - 1) / 3 + 1) * 4]; + + int outIndex = 0; + int i = 0; + + while ((i + 3) <= octetString.length) + { + // store the octets + bits24 = (octetString[i++] & 0xFF) << 16; + bits24 |= (octetString[i++] & 0xFF) << 8; + bits24 |= (octetString[i++] & 0xFF) << 0; + + bits6 = (bits24 & 0x00FC0000) >> 18; + out[outIndex++] = alphabet[bits6]; + bits6 = (bits24 & 0x0003F000) >> 12; + out[outIndex++] = alphabet[bits6]; + bits6 = (bits24 & 0x00000FC0) >> 6; + out[outIndex++] = alphabet[bits6]; + bits6 = (bits24 & 0x0000003F); + out[outIndex++] = alphabet[bits6]; + } + + if (octetString.length - i == 2) + { + bits24 = (octetString[i] & 0xFF) << 16; + bits24 |= (octetString[i + 1] & 0xFF) << 8; + + bits6 = (bits24 & 0x00FC0000) >> 18; + out[outIndex++] = alphabet[bits6]; + bits6 = (bits24 & 0x0003F000) >> 12; + out[outIndex++] = alphabet[bits6]; + bits6 = (bits24 & 0x00000FC0) >> 6; + out[outIndex++] = alphabet[bits6]; + + out[outIndex++] = '='; + } + else if (octetString.length - i == 1) + { + bits24 = (octetString[i] & 0xFF) << 16; + + bits6 = (bits24 & 0x00FC0000) >> 18; + out[outIndex++] = alphabet[bits6]; + bits6 = (bits24 & 0x0003F000) >> 12; + out[outIndex++] = alphabet[bits6]; + + out[outIndex++] = '='; + out[outIndex++] = '='; + } + return new String (out); + } +} diff --git a/common/src/main/java/eu/midnightdust/picturesign_theora/ogv/jheora/BlockMapping.java b/common/src/main/java/eu/midnightdust/picturesign_theora/ogv/jheora/BlockMapping.java new file mode 100644 index 0000000..2131679 --- /dev/null +++ b/common/src/main/java/eu/midnightdust/picturesign_theora/ogv/jheora/BlockMapping.java @@ -0,0 +1,117 @@ +/* Jheora + * Copyright (C) 2004 Fluendo S.L. + * + * Written by: 2004 Wim Taymans + * + * Many thanks to + * The Xiph.Org Foundation http://www.xiph.org/ + * Jheora was based on their Theora reference decoder. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public License + * as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +package eu.midnightdust.picturesign_theora.ogv.jheora; + +public final class BlockMapping +{ + private int[][][] blockMap; + + private static final int[] mbOrderMap = { 0, 2, 3, 1 }; + private static final int[][] blockOrderMap1 = + { { 0, 1, 3, 2 }, + { 0, 2, 3, 1 }, + { 0, 2, 3, 1 }, + { 3, 2, 0, 1 } + }; + + public final int quadMapToIndex1 (int sb, int mb, int b) + { + return blockMap[sb][mbOrderMap[mb]][blockOrderMap1[mb][b]]; + } + + public final int quadMapToMBTopLeft (int sb, int mb) + { + return blockMap[sb][mbOrderMap[mb]][0]; + } + + private void CreateMapping (int firstSB, + int firstFrag, int hFrags, + int vFrags) + { + int i = 0, j = 0; + int xpos; + int ypos; + int mb, B; + + int sb=firstSB; + int fragIndex=firstFrag; + + /* Set Super-Block dimensions */ + int sBRows = (vFrags>>2) + ((vFrags & 0x3) != 0 ? 1 : 0 ); + int sBCols = (hFrags>>2) + ((hFrags & 0x3) != 0 ? 1 : 0 ); + + /* Map each Super-Block */ + for (int sBRow=0; sBRow> 1); + B = ((i & 1) << 1) + (j & 1); + + /* Set mapping and move to next fragment */ + blockMap[sb][mb][B] = fragIndex++; + } + + /* Move to first fragment in next row in Super-Block */ + fragIndex += hFrags-j; + } + + /* Move on to next Super-Block */ + sb++; + fragIndex -= i*hFrags-j; + } + + /* Move to first Super-Block in next row */ + fragIndex += 3*hFrags; + } + } + + public BlockMapping (int ySuperBlocks, int uvSuperBlocks, int hFrags, int vFrags, int shiftx, int shifty) + { + blockMap = new int[ySuperBlocks + uvSuperBlocks * 2][4][4]; + + for (int i=0; i>shiftx, vFrags>>shifty ); + CreateMapping (ySuperBlocks + uvSuperBlocks, hFrags*vFrags + (hFrags>>shiftx)*(vFrags>>shifty), + hFrags>>shiftx, vFrags>>shifty ); + } + +} diff --git a/common/src/main/java/eu/midnightdust/picturesign_theora/ogv/jheora/CodingMode.java b/common/src/main/java/eu/midnightdust/picturesign_theora/ogv/jheora/CodingMode.java new file mode 100644 index 0000000..d0da5c2 --- /dev/null +++ b/common/src/main/java/eu/midnightdust/picturesign_theora/ogv/jheora/CodingMode.java @@ -0,0 +1,72 @@ +/* Jheora + * Copyright (C) 2004 Fluendo S.L. + * + * Written by: 2004 Wim Taymans + * + * Many thanks to + * The Xiph.Org Foundation http://www.xiph.org/ + * Jheora was based on their Theora reference decoder. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public License + * as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +package eu.midnightdust.picturesign_theora.ogv.jheora; + +public class CodingMode { + private int value; + + public static final CodingMode CODE_INTER_NO_MV = + new CodingMode(0x0); /* INTER prediction, (0,0) motion vector implied. */ + + public static final CodingMode CODE_INTRA = + new CodingMode(0x1); /* INTRA i.e. no prediction. */ + + public static final CodingMode CODE_INTER_PLUS_MV = + new CodingMode(0x2); /* INTER prediction, non zero motion vector. */ + + public static final CodingMode CODE_INTER_LAST_MV = + new CodingMode(0x3); /* Use Last Motion vector */ + + public static final CodingMode CODE_INTER_PRIOR_LAST = + new CodingMode(0x4); /* Prior last motion vector */ + + public static final CodingMode CODE_USING_GOLDEN = + new CodingMode(0x5); /* 'Golden frame' prediction (no MV). */ + + public static final CodingMode CODE_GOLDEN_MV = + new CodingMode(0x6); /* 'Golden frame' prediction plus MV. */ + + public static final CodingMode CODE_INTER_FOURMV = + new CodingMode(0x7); /* Inter prediction 4MV per macro block. */ + + public static final CodingMode[] MODES = { + CODE_INTER_NO_MV, + CODE_INTRA, + CODE_INTER_PLUS_MV, + CODE_INTER_LAST_MV, + CODE_INTER_PRIOR_LAST, + CODE_USING_GOLDEN, + CODE_GOLDEN_MV, + CODE_INTER_FOURMV + }; + + private CodingMode(int i) { + value=i; + } + + public int getValue() { + return value; + } +} diff --git a/common/src/main/java/eu/midnightdust/picturesign_theora/ogv/jheora/Colorspace.java b/common/src/main/java/eu/midnightdust/picturesign_theora/ogv/jheora/Colorspace.java new file mode 100644 index 0000000..e0f2788 --- /dev/null +++ b/common/src/main/java/eu/midnightdust/picturesign_theora/ogv/jheora/Colorspace.java @@ -0,0 +1,40 @@ +/* Jheora + * Copyright (C) 2004 Fluendo S.L. + * + * Written by: 2004 Wim Taymans + * + * Many thanks to + * The Xiph.Org Foundation http://www.xiph.org/ + * Jheora was based on their Theora reference decoder. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public License + * as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +package eu.midnightdust.picturesign_theora.ogv.jheora; + +public class Colorspace { + public static final Colorspace UNSPECIFIED = new Colorspace (); + public static final Colorspace ITU_REC_470M = new Colorspace (); + public static final Colorspace ITU_REC_470BG = new Colorspace (); + + public static final Colorspace[] spaces = { + UNSPECIFIED, + ITU_REC_470M, + ITU_REC_470BG + }; + + private Colorspace() { + } +} diff --git a/common/src/main/java/eu/midnightdust/picturesign_theora/ogv/jheora/Constants.java b/common/src/main/java/eu/midnightdust/picturesign_theora/ogv/jheora/Constants.java new file mode 100644 index 0000000..bfce0fe --- /dev/null +++ b/common/src/main/java/eu/midnightdust/picturesign_theora/ogv/jheora/Constants.java @@ -0,0 +1,62 @@ +/* Jheora + * Copyright (C) 2004 Fluendo S.L. + * + * Written by: 2004 Wim Taymans + * + * Many thanks to + * The Xiph.Org Foundation http://www.xiph.org/ + * Jheora was based on their Theora reference decoder. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public License + * as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +package eu.midnightdust.picturesign_theora.ogv.jheora; + +public class Constants +{ + public static final int CURRENT_ENCODE_VERSION = 1; + +/* Baseline dct height and width. */ + public static final int BLOCK_HEIGHT_WIDTH = 8; + public static final int HFRAGPIXELS = 8; + public static final int VFRAGPIXELS = 8; + +/* Baseline dct block size */ + public static final int BLOCK_SIZE = (BLOCK_HEIGHT_WIDTH * BLOCK_HEIGHT_WIDTH); + +/* Border is for unrestricted mv's */ + public static final int UMV_BORDER = 16; + public static final int STRIDE_EXTRA = (UMV_BORDER * 2); + public static final int Q_TABLE_SIZE = 64; + + public static final int BASE_FRAME = 0; + public static final int NORMAL_FRAME = 1; + + public static final int MAX_MODES = 8; + public static final int MODE_BITS = 3; + public static final int MODE_METHODS = 8; + public static final int MODE_METHOD_BITS = 3; + + public static final int[] dequant_index = { + 0, 1, 8, 16, 9, 2, 3, 10, + 17, 24, 32, 25, 18, 11, 4, 5, + 12, 19, 26, 33, 40, 48, 41, 34, + 27, 20, 13, 6, 7, 14, 21, 28, + 35, 42, 49, 56, 57, 50, 43, 36, + 29, 22, 15, 23, 30, 37, 44, 51, + 58, 59, 52, 45, 38, 31, 39, 46, + 53, 60, 61, 54, 47, 55, 62, 63 + }; +} diff --git a/common/src/main/java/eu/midnightdust/picturesign_theora/ogv/jheora/Coordinate.java b/common/src/main/java/eu/midnightdust/picturesign_theora/ogv/jheora/Coordinate.java new file mode 100644 index 0000000..6f0d060 --- /dev/null +++ b/common/src/main/java/eu/midnightdust/picturesign_theora/ogv/jheora/Coordinate.java @@ -0,0 +1,40 @@ +/* Jheora + * Copyright (C) 2004 Fluendo S.L. + * + * Written by: 2004 Wim Taymans + * + * Many thanks to + * The Xiph.Org Foundation http://www.xiph.org/ + * Jheora was based on their Theora reference decoder. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public License + * as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +package eu.midnightdust.picturesign_theora.ogv.jheora; + +public class Coordinate +{ + public int x, y; + + public Coordinate() { + x=0; + y=0; + } + + public Coordinate(int x, int y) { + this.x = x; + this.y = y; + } +} diff --git a/common/src/main/java/eu/midnightdust/picturesign_theora/ogv/jheora/DCTDecode.java b/common/src/main/java/eu/midnightdust/picturesign_theora/ogv/jheora/DCTDecode.java new file mode 100644 index 0000000..313e556 --- /dev/null +++ b/common/src/main/java/eu/midnightdust/picturesign_theora/ogv/jheora/DCTDecode.java @@ -0,0 +1,827 @@ +/* Jheora + * Copyright (C) 2004 Fluendo S.L. + * + * Written by: 2004 Wim Taymans + * + * Many thanks to + * The Xiph.Org Foundation http://www.xiph.org/ + * Jheora was based on their Theora reference decoder. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public License + * as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +package eu.midnightdust.picturesign_theora.ogv.jheora; + +public class DCTDecode +{ + private static final int PUR = 8; + private static final int PU = 4; + private static final int PUL = 2; + private static final int PL = 1; + + private short[] dequant_matrix = new short[64]; + + private static final int[] ModeUsesMC = { 0, 0, 1, 1, 1, 0, 1, 1 }; + + /* predictor multiplier up-left, up, up-right,left, shift + Entries are packed in the order L, UL, U, UR, with missing entries + moved to the end (before the shift parameters). */ + private static final short[][] pc = { + {0,0,0,0,0,0}, + {1,0,0,0,0,0}, /* PL */ + {1,0,0,0,0,0}, /* PUL */ + {1,0,0,0,0,0}, /* PUL|PL */ + {1,0,0,0,0,0}, /* PU */ + {1,1,0,0,1,1}, /* PU|PL */ + {0,1,0,0,0,0}, /* PU|PUL */ + {29,-26,29,0,5,31}, /* PU|PUL|PL */ + {1,0,0,0,0,0}, /* PUR */ + {75,53,0,0,7,127}, /* PUR|PL */ + {1,1,0,0,1,1}, /* PUR|PUL */ + {75,0,53,0,7,127}, /* PUR|PUL|PL */ + {1,0,0,0,0,0}, /* PUR|PU */ + {75,0,53,0,7,127}, /* PUR|PU|PL */ + {3,10,3,0,4,15}, /* PUR|PU|PUL */ + {29,-26,29,0,5,31} /* PUR|PU|PUL|PL */ + }; + + /* boundary case bit masks. */ + private static final int[] bc_mask = { + /* normal case no boundary condition */ + PUR|PU|PUL|PL, + /* left column */ + PUR|PU, + /* top row */ + PL, + /* top row, left column */ + 0, + /* right column */ + PU|PUL|PL, + /* right and left column */ + PU, + /* top row, right column */ + PL, + /* top row, right and left column */ + 0 + }; + + private static final short[] Mode2Frame = { + 1, /* CODE_INTER_NO_MV 0 => Encoded diff from same MB last frame */ + 0, /* CODE_INTRA 1 => DCT Encoded Block */ + 1, /* CODE_INTER_PLUS_MV 2 => Encoded diff from included MV MB last frame */ + 1, /* CODE_INTER_LAST_MV 3 => Encoded diff from MRU MV MB last frame */ + 1, /* CODE_INTER_PRIOR_MV 4 => Encoded diff from included 4 separate MV blocks */ + 2, /* CODE_USING_GOLDEN 5 => Encoded diff from same MB golden frame */ + 2, /* CODE_GOLDEN_MV 6 => Encoded diff from included MV MB golden frame */ + 1 /* CODE_INTER_FOUR_MV 7 => Encoded diff from included 4 separate MV blocks */ + }; + + private short[] ReconDataBuffer = new short[64]; + /* value left value up-left, value up, value up-right, missing + values skipped. */ + private int[] v = new int[4]; + /* fragment number left, up-left, up, up-right */ + private int[] fn = new int[4]; + private short[] Last = new short[3]; + private iDCT idct = new iDCT(); + + private void ExpandKFBlock ( Playback pbi, int FragmentNumber ){ + int ReconPixelsPerLine; + int ReconPixelIndex; + short[] dequant_coeffs; + + /* determine which quantizer was specified for this block */ + int qi = pbi.FragQs[FragmentNumber]; + + /* Select the appropriate inverse Q matrix and line stride */ + if ( FragmentNumber<(int)pbi.YPlaneFragments ){ + ReconPixelsPerLine = pbi.YStride; + // intra Y + dequant_coeffs = pbi.info.dequant_tables[0][0][pbi.frameQIS[qi]]; + dequant_matrix[0] = pbi.info.dequant_tables[0][0][pbi.frameQIS[0]][0]; + }else if(FragmentNumber < pbi.YPlaneFragments + pbi.UVPlaneFragments) { + ReconPixelsPerLine = pbi.UVStride; + // intra U + dequant_coeffs = pbi.info.dequant_tables[0][1][pbi.frameQIS[qi]]; + dequant_matrix[0] = pbi.info.dequant_tables[0][1][pbi.frameQIS[0]][0]; + } else { + ReconPixelsPerLine = pbi.UVStride; + // intra V + dequant_coeffs = pbi.info.dequant_tables[0][2][pbi.frameQIS[qi]]; + dequant_matrix[0] = pbi.info.dequant_tables[0][2][pbi.frameQIS[0]][0]; + } + + // create copy with DC coefficient of primary frame qi + System.arraycopy(dequant_coeffs, 1, dequant_matrix, 1, 63); + + /* Set up pointer into the quantisation buffer. */ + short[] quantized_list = pbi.QFragData[FragmentNumber]; + + /* Invert quantisation and DCT to get pixel data. */ + switch(pbi.FragCoefEOB[FragmentNumber]){ + case 0:case 1: + idct.IDct1(quantized_list, dequant_matrix, ReconDataBuffer ); + break; + case 2: case 3:case 4:case 5:case 6:case 7:case 8: case 9:case 10: + idct.IDct10(quantized_list, dequant_matrix, ReconDataBuffer ); + break; + default: + idct.IDctSlow(quantized_list, dequant_matrix, ReconDataBuffer ); + } + /* + for (int i=0; i<64; i++) { + System.out.print(ReconDataBuffer[i]+" "); + } + System.out.println(); + */ + + /* Convert fragment number to a pixel offset in a reconstruction buffer. */ + ReconPixelIndex = pbi.recon_pixel_index_table[FragmentNumber]; + + /* Get the pixel index for the first pixel in the fragment. */ + Recon.ReconIntra (pbi.ThisFrameRecon, ReconPixelIndex, ReconDataBuffer, ReconPixelsPerLine); + } + + private void ExpandBlock ( Playback pbi, int FragmentNumber ){ + short[] LastFrameRecPtr; /* Pointer into previous frame + reconstruction. */ + int ReconPixelsPerLine; /* Pixels per line */ + int ReconPixelIndex; /* Offset for block into a + reconstruction buffer */ + int ReconPtr2Offset; /* Offset for second + reconstruction in half pixel + MC */ + int MVOffset; /* Baseline motion vector offset */ + int MvShiftX ; /* Shift to correct to 1/2 or 1/4 pixel */ + int MvShiftY ; /* Shift to correct to 1/2 or 1/4 pixel */ + int MvModMaskX; /* Mask to determine whether 1/2 + pixel is used */ + int MvModMaskY; + short[] dequant_coeffs; + CodingMode codingMode; + + /* determine which quantizer was specified for this block */ + int qi = pbi.FragQs[FragmentNumber]; + + /* Get coding mode for this block */ + if (pbi.getFrameType() == Constants.BASE_FRAME ){ + codingMode = CodingMode.CODE_INTRA; + }else{ + /* Get Motion vector and mode for this block. */ + codingMode = pbi.FragCodingMethod[FragmentNumber]; + } + + /* Select the appropriate inverse Q matrix and line stride */ + if ( FragmentNumber<(int)pbi.YPlaneFragments ) { + ReconPixelsPerLine = pbi.YStride; + MvShiftX = MvShiftY = 1; + MvModMaskX = MvModMaskY = 0x00000001; + + /* Select appropriate dequantiser matrix. */ + if ( codingMode == CodingMode.CODE_INTRA ) { + // intra Y + dequant_coeffs = pbi.info.dequant_tables[0][0][pbi.frameQIS[qi]]; + dequant_matrix[0] = pbi.info.dequant_tables[0][0][pbi.frameQIS[0]][0]; + } else { + // inter Y + dequant_coeffs = pbi.info.dequant_tables[1][0][pbi.frameQIS[qi]]; + dequant_matrix[0] = pbi.info.dequant_tables[1][0][pbi.frameQIS[0]][0]; + } + }else{ + ReconPixelsPerLine = pbi.UVStride; + MvShiftX = pbi.UVShiftX + 1; + MvShiftY = pbi.UVShiftY + 1; + MvModMaskX = MvModMaskY = 0x00000003; + if (MvShiftX == 1) MvModMaskX = 0x00000001; + if (MvShiftY == 1) MvModMaskY = 0x00000001; + + /* Select appropriate dequantiser matrix. */ + + + if(FragmentNumber < pbi.YPlaneFragments + pbi.UVPlaneFragments) { + if ( codingMode == CodingMode.CODE_INTRA ) { + // intra U + dequant_coeffs = pbi.info.dequant_tables[0][1][pbi.frameQIS[qi]]; + dequant_matrix[0] = pbi.info.dequant_tables[0][1][pbi.frameQIS[0]][0]; + } else { + // inter U + dequant_coeffs = pbi.info.dequant_tables[1][1][pbi.frameQIS[qi]]; + dequant_matrix[0] = pbi.info.dequant_tables[1][1][pbi.frameQIS[0]][0]; + } + } else { + if ( codingMode == CodingMode.CODE_INTRA ) { + // intra V + dequant_coeffs = pbi.info.dequant_tables[0][2][pbi.frameQIS[qi]]; + dequant_matrix[0] = pbi.info.dequant_tables[0][2][pbi.frameQIS[0]][0]; + } else { + // inter V + dequant_coeffs = pbi.info.dequant_tables[1][2][pbi.frameQIS[qi]]; + dequant_matrix[0] = pbi.info.dequant_tables[1][2][pbi.frameQIS[0]][0]; + } + } + } + // create copy with DC coefficient of primary frame qi + System.arraycopy(dequant_coeffs, 1, dequant_matrix, 1, 63); + + /* Set up pointer into the quantisation buffer. */ + short[] quantized_list = pbi.QFragData[FragmentNumber]; + + /* Invert quantisation and DCT to get pixel data. */ + switch(pbi.FragCoefEOB[FragmentNumber]){ + case 0:case 1: + idct.IDct1(quantized_list, dequant_matrix, ReconDataBuffer ); + break; + case 2: case 3:case 4:case 5:case 6:case 7:case 8: case 9:case 10: + idct.IDct10(quantized_list, dequant_matrix, ReconDataBuffer ); + break; + default: + idct.IDctSlow(quantized_list, dequant_matrix, ReconDataBuffer ); + } + /* + for (int i=0; i<64; i++) { + System.out.print(ReconDataBuffer[i]+" "); + } + System.out.println(); + */ + + + /* Convert fragment number to a pixel offset in a reconstruction buffer. */ + ReconPixelIndex = pbi.recon_pixel_index_table[FragmentNumber]; + + /* Action depends on decode mode. */ + if ( codingMode == CodingMode.CODE_INTER_NO_MV ){ + /* Inter with no motion vector */ + /* Reconstruct the pixel data using the last frame reconstruction + and change data when the motion vector is (0,0), the recon is + based on the lastframe without loop filtering---- for testing */ + Recon.ReconInter(pbi.ThisFrameRecon, ReconPixelIndex, + pbi.LastFrameRecon, ReconPixelIndex, + ReconDataBuffer, ReconPixelsPerLine ); + + }else if (ModeUsesMC[codingMode.getValue()] != 0) { + /* The mode uses a motion vector. */ + /* Get vector from list */ + int dir; + + /* Work out the base motion vector offset and the 1/2 pixel offset + if any. For the U and V planes the MV specifies 1/4 pixel + accuracy. This is adjusted to 1/2 pixel as follows ( 0.0, + 1/4->1/2, 1/2->1/2, 3/4->1/2 ). */ + ReconPtr2Offset = 0; + MVOffset = 0; + + dir = pbi.FragMVect[FragmentNumber].x; + if (dir > 0) { + MVOffset = dir >> MvShiftX; + if ((dir & MvModMaskX) != 0 ) + ReconPtr2Offset = 1; + } else if (dir < 0) { + MVOffset = -((-dir) >> MvShiftX); + if (((-dir) & MvModMaskX) != 0 ) + ReconPtr2Offset = -1; + } + + dir = pbi.FragMVect[FragmentNumber].y; + if ( dir > 0 ){ + MVOffset += (dir >> MvShiftY) * ReconPixelsPerLine; + if ((dir & MvModMaskY) != 0 ) + ReconPtr2Offset += ReconPixelsPerLine; + } else if (dir < 0 ){ + MVOffset -= ((-dir) >> MvShiftY) * ReconPixelsPerLine; + if (((-dir) & MvModMaskY) != 0 ) + ReconPtr2Offset -= ReconPixelsPerLine; + } + + int LastFrameRecOffset = ReconPixelIndex + MVOffset; + + /* Set up the first of the two reconstruction buffer pointers. */ + if ( codingMode==CodingMode.CODE_GOLDEN_MV ) { + LastFrameRecPtr = pbi.GoldenFrame; + }else{ + LastFrameRecPtr = pbi.LastFrameRecon; + } + + /* + System.out.println(pbi.FragMVect[FragmentNumber].x+" "+ + pbi.FragMVect[FragmentNumber].y+" "+ + ReconPixelIndex+" "+LastFrameRecOffset+ " "+ + ReconPtr2Offset); + */ + + /* Select the appropriate reconstruction function */ + if (ReconPtr2Offset == 0 ) { + /* Reconstruct the pixel dats from the reference frame and change data + (no half pixel in this case as the two references were the same. */ + Recon.ReconInter(pbi.ThisFrameRecon, ReconPixelIndex, + LastFrameRecPtr, LastFrameRecOffset, + ReconDataBuffer, ReconPixelsPerLine ); + }else{ + /* Fractional pixel reconstruction. */ + /* Note that we only use two pixels per reconstruction even for + the diagonal. */ + Recon.ReconInterHalfPixel2(pbi.ThisFrameRecon, ReconPixelIndex, + LastFrameRecPtr, LastFrameRecOffset, + LastFrameRecPtr, LastFrameRecOffset+ReconPtr2Offset, + ReconDataBuffer, ReconPixelsPerLine ); + } + } else if ( codingMode == CodingMode.CODE_USING_GOLDEN ){ + /* Golden frame with motion vector */ + /* Reconstruct the pixel data using the golden frame + reconstruction and change data */ + Recon.ReconInter(pbi.ThisFrameRecon, ReconPixelIndex, + pbi.GoldenFrame, ReconPixelIndex , + ReconDataBuffer, ReconPixelsPerLine ); + } else { + /* Simple Intra coding */ + /* Get the pixel index for the first pixel in the fragment. */ + Recon.ReconIntra(pbi.ThisFrameRecon, ReconPixelIndex, + ReconDataBuffer, ReconPixelsPerLine ); + } + } + + private void UpdateUMV_HBorders( Playback pbi, + short[] DestReconPtr, + int PlaneFragOffset ) { + int i; + int PixelIndex; + + int PlaneStride; + int BlockVStep; + int PlaneFragments; + int LineFragments; + int PlaneBorderWidth; + int PlaneBorderHeight; + + short[] SrcPtr1; + int SrcOff1; + short[] SrcPtr2; + int SrcOff2; + short[] DestPtr1; + int DestOff1; + short[] DestPtr2; + int DestOff2; + + /* Work out various plane specific values */ + if ( PlaneFragOffset == 0 ) { + /* Y Plane */ + BlockVStep = (pbi.YStride * + (Constants.VFRAGPIXELS - 1)); + PlaneStride = pbi.YStride; + PlaneBorderWidth = Constants.UMV_BORDER; + PlaneBorderHeight = Constants.UMV_BORDER; + PlaneFragments = pbi.YPlaneFragments; + LineFragments = pbi.HFragments; + }else{ + /* U or V plane. */ + BlockVStep = (pbi.UVStride * + (Constants.VFRAGPIXELS - 1)); + PlaneStride = pbi.UVStride; + PlaneBorderWidth = Constants.UMV_BORDER >> pbi.UVShiftX; + PlaneBorderHeight = Constants.UMV_BORDER >> pbi.UVShiftY; + PlaneFragments = pbi.UVPlaneFragments; + LineFragments = pbi.HFragments >> pbi.UVShiftX; + } + + /* Setup the source and destination pointers for the top and bottom + borders */ + PixelIndex = pbi.recon_pixel_index_table[PlaneFragOffset]; + SrcPtr1 = DestReconPtr; + SrcOff1 = PixelIndex - PlaneBorderWidth; + DestPtr1 = SrcPtr1; + DestOff1 = SrcOff1 - (PlaneBorderHeight * PlaneStride); + + PixelIndex = pbi.recon_pixel_index_table[PlaneFragOffset + + PlaneFragments - LineFragments] + + BlockVStep; + SrcPtr2 = DestReconPtr; + SrcOff2 = PixelIndex - PlaneBorderWidth; + DestPtr2 = SrcPtr2; + DestOff2 = SrcOff2 + PlaneStride; + + /* Now copy the top and bottom source lines into each line of the + respective borders */ + for ( i = 0; i < PlaneBorderHeight; i++ ) { + System.arraycopy(SrcPtr1, SrcOff1, DestPtr1, DestOff1, PlaneStride); + System.arraycopy(SrcPtr2, SrcOff2, DestPtr2, DestOff2, PlaneStride); + DestOff1 += PlaneStride; + DestOff2 += PlaneStride; + } + } + + private void UpdateUMV_VBorders( Playback pbi, + short[] DestReconPtr, + int PlaneFragOffset ){ + int i; + int PixelIndex; + + int PlaneStride; + int LineFragments; + int PlaneBorderWidth; + int PlaneHeight; + + short[] SrcPtr1; + int SrcOff1; + short[] SrcPtr2; + int SrcOff2; + short[] DestPtr1; + int DestOff1; + short[] DestPtr2; + int DestOff2; + + /* Work out various plane specific values */ + if ( PlaneFragOffset == 0 ) { + /* Y Plane */ + PlaneStride = pbi.YStride; + PlaneBorderWidth = Constants.UMV_BORDER; + LineFragments = pbi.HFragments; + PlaneHeight = pbi.info.height; + }else{ + /* U or V plane. */ + PlaneStride = pbi.UVStride; + PlaneBorderWidth = Constants.UMV_BORDER >> pbi.UVShiftX; + LineFragments = pbi.HFragments >> pbi.UVShiftX; + PlaneHeight = pbi.info.height >> pbi.UVShiftY; + } + + /* Setup the source data values and destination pointers for the + left and right edge borders */ + PixelIndex = pbi.recon_pixel_index_table[PlaneFragOffset]; + SrcPtr1 = DestReconPtr; + SrcOff1 = PixelIndex; + DestPtr1 = DestReconPtr; + DestOff1 = PixelIndex - PlaneBorderWidth; + + PixelIndex = pbi.recon_pixel_index_table[PlaneFragOffset + + LineFragments - 1] + + (Constants.HFRAGPIXELS - 1); + SrcPtr2 = DestReconPtr; + SrcOff2 = PixelIndex; + DestPtr2 = DestReconPtr; + DestOff2 = PixelIndex + 1; + + /* Now copy the top and bottom source lines into each line of the + respective borders */ + for ( i = 0; i < PlaneHeight; i++ ) { + MemUtils.set(DestPtr1, DestOff1, SrcPtr1[SrcOff1], PlaneBorderWidth ); + MemUtils.set(DestPtr2, DestOff2, SrcPtr2[SrcOff2], PlaneBorderWidth ); + DestOff1 += PlaneStride; + DestOff2 += PlaneStride; + SrcOff1 += PlaneStride; + SrcOff2 += PlaneStride; + } + } + + private void UpdateUMVBorder( Playback pbi, + short[] DestReconPtr ) { + int PlaneFragOffset; + + /* Y plane */ + PlaneFragOffset = 0; + UpdateUMV_VBorders( pbi, DestReconPtr, PlaneFragOffset ); + UpdateUMV_HBorders( pbi, DestReconPtr, PlaneFragOffset ); + + /* Then the U and V Planes */ + PlaneFragOffset = pbi.YPlaneFragments; + UpdateUMV_VBorders( pbi, DestReconPtr, PlaneFragOffset ); + UpdateUMV_HBorders( pbi, DestReconPtr, PlaneFragOffset ); + + PlaneFragOffset = pbi.YPlaneFragments + pbi.UVPlaneFragments; + UpdateUMV_VBorders( pbi, DestReconPtr, PlaneFragOffset ); + UpdateUMV_HBorders( pbi, DestReconPtr, PlaneFragOffset ); + } + + private void CopyRecon( Playback pbi, short[] DestReconPtr, + short[] SrcReconPtr ) { + int i; + int PlaneLineStep; /* Pixels per line */ + int PixelIndex; + + /* Copy over only updated blocks.*/ + + /* First Y plane */ + PlaneLineStep = pbi.YStride; + for ( i = 0; i < pbi.YPlaneFragments; i++ ) { + if ( pbi.display_fragments[i] != 0) { + PixelIndex = pbi.recon_pixel_index_table[i]; + Recon.CopyBlock(SrcReconPtr, DestReconPtr, PixelIndex, PlaneLineStep); + } + } + + /* Then U and V */ + PlaneLineStep = pbi.UVStride; + for ( i = pbi.YPlaneFragments; i < pbi.UnitFragments; i++ ) { + if ( pbi.display_fragments[i] != 0) { + PixelIndex = pbi.recon_pixel_index_table[i]; + Recon.CopyBlock(SrcReconPtr, DestReconPtr, PixelIndex, PlaneLineStep); + } + } + } + + private void CopyNotRecon( Playback pbi, short[] DestReconPtr, + short[] SrcReconPtr ) { + int i; + int PlaneLineStep; /* Pixels per line */ + int PixelIndex; + + /* Copy over only updated blocks. */ + + /* First Y plane */ + PlaneLineStep = pbi.YStride; + for (i = 0; i < pbi.YPlaneFragments; i++) { + if (pbi.display_fragments[i] == 0) { + PixelIndex = pbi.recon_pixel_index_table[i]; + Recon.CopyBlock(SrcReconPtr, DestReconPtr, PixelIndex, PlaneLineStep); + } + } + + /* Then U and V */ + PlaneLineStep = pbi.UVStride; + for (i = pbi.YPlaneFragments; i < pbi.UnitFragments; i++) { + if (pbi.display_fragments[i] == 0) { + PixelIndex = pbi.recon_pixel_index_table[i]; + Recon.CopyBlock(SrcReconPtr, DestReconPtr, PixelIndex, PlaneLineStep); + } + } + } + + public void ExpandToken( short[] ExpandedBlock, + byte[] CoeffIndex, int FragIndex, int Token, + int ExtraBits ){ + /* Is the token is a combination run and value token. */ + if ( Token >= Huffman.DCT_RUN_CATEGORY1 ){ + /* Expand the token and additional bits to a zero run length and + data value. */ + if ( Token < Huffman.DCT_RUN_CATEGORY2 ) { + /* Decoding method depends on token */ + if ( Token < Huffman.DCT_RUN_CATEGORY1B ) { + /* Step on by the zero run length */ + CoeffIndex[FragIndex] += (byte)((Token - Huffman.DCT_RUN_CATEGORY1) + 1); + /* The extra bit determines the sign. */ + ExpandedBlock[CoeffIndex[FragIndex]] = (short)-(((ExtraBits&0x01)<<1)-1); + } + else if ( Token == Huffman.DCT_RUN_CATEGORY1B ) { + /* Bits 0-1 determines the zero run length */ + CoeffIndex[FragIndex] += (6 + (ExtraBits & 0x03)); + /* Bit 2 determines the sign */ + ExpandedBlock[CoeffIndex[FragIndex]] = (short)-(((ExtraBits&0x04)>>1)-1); + }else{ + /* Bits 0-2 determines the zero run length */ + CoeffIndex[FragIndex] += (10 + (ExtraBits & 0x07)); + /* Bit 3 determines the sign */ + ExpandedBlock[CoeffIndex[FragIndex]] = (short)-(((ExtraBits&0x08)>>2)-1); + } + }else{ + /* If token == Huffman.DCT_RUN_CATEGORY2 we have a single 0 followed by + a value */ + if ( Token == Huffman.DCT_RUN_CATEGORY2 ){ + /* Step on by the zero run length */ + CoeffIndex[FragIndex] += 1; + /* Bit 1 determines sign, bit 0 the value */ + ExpandedBlock[CoeffIndex[FragIndex]] = (short) ((2+(ExtraBits & 0x01)) * -((ExtraBits&0x02)-1)); + }else{ + /* else we have 2->3 zeros followed by a value */ + /* Bit 0 determines the zero run length */ + CoeffIndex[FragIndex] += 2 + (ExtraBits & 0x01); + /* Bit 2 determines the sign, bit 1 the value */ + ExpandedBlock[CoeffIndex[FragIndex]] = (short) ((2+((ExtraBits&0x02)>>1))*-(((ExtraBits&0x04)>>1)-1)); + } + } + + /* Step on over value */ + CoeffIndex[FragIndex] += 1; + + } else if ( Token == Huffman.DCT_SHORT_ZRL_TOKEN ) { + /* Token is a ZRL token so step on by the appropriate number of zeros */ + CoeffIndex[FragIndex] += ExtraBits + 1; + } else if ( Token == Huffman.DCT_ZRL_TOKEN ) { + /* Token is a ZRL token so step on by the appropriate number of zeros */ + CoeffIndex[FragIndex] += ExtraBits + 1; + } else if ( Token < Huffman.LOW_VAL_TOKENS ) { + /* Token is a small single value token. */ + switch ( Token ) { + case Huffman.ONE_TOKEN: + ExpandedBlock[CoeffIndex[FragIndex]] = 1; + break; + case Huffman.MINUS_ONE_TOKEN: + ExpandedBlock[CoeffIndex[FragIndex]] = -1; + break; + case Huffman.TWO_TOKEN: + ExpandedBlock[CoeffIndex[FragIndex]] = 2; + break; + case Huffman.MINUS_TWO_TOKEN: + ExpandedBlock[CoeffIndex[FragIndex]] = -2; + break; + } + + /* Step on the coefficient index. */ + CoeffIndex[FragIndex] += 1; + }else{ + /* Token is a larger single value token */ + /* Expand the token and additional bits to a data value. */ + if ( Token < Huffman.DCT_VAL_CATEGORY3 ) { + /* Offset from LOW_VAL_TOKENS determines value */ + Token = Token - Huffman.LOW_VAL_TOKENS; + + /* Extra bit determines sign */ + ExpandedBlock[CoeffIndex[FragIndex]] = (short) ((Token + Huffman.DCT_VAL_CAT2_MIN) * + -(((ExtraBits)<<1)-1)); + } else if ( Token == Huffman.DCT_VAL_CATEGORY3 ) { + /* Bit 1 determines sign, Bit 0 the value */ + ExpandedBlock[CoeffIndex[FragIndex]] = (short) ((Huffman.DCT_VAL_CAT3_MIN + (ExtraBits & 0x01)) * + -(((ExtraBits&0x02))-1)); + } else if ( Token == Huffman.DCT_VAL_CATEGORY4 ) { + /* Bit 2 determines sign, Bit 0-1 the value */ + ExpandedBlock[CoeffIndex[FragIndex]] = (short) ((Huffman.DCT_VAL_CAT4_MIN + (ExtraBits & 0x03)) * + -(((ExtraBits&0x04)>>1)-1)); + } else if ( Token == Huffman.DCT_VAL_CATEGORY5 ) { + /* Bit 3 determines sign, Bit 0-2 the value */ + ExpandedBlock[CoeffIndex[FragIndex]] = (short) ((Huffman.DCT_VAL_CAT5_MIN + (ExtraBits & 0x07)) * + -(((ExtraBits&0x08)>>2)-1)); + } else if ( Token == Huffman.DCT_VAL_CATEGORY6 ) { + /* Bit 4 determines sign, Bit 0-3 the value */ + ExpandedBlock[CoeffIndex[FragIndex]] = (short) ((Huffman.DCT_VAL_CAT6_MIN + (ExtraBits & 0x0F)) * + -(((ExtraBits&0x10)>>3)-1)); + } else if ( Token == Huffman.DCT_VAL_CATEGORY7 ) { + /* Bit 5 determines sign, Bit 0-4 the value */ + ExpandedBlock[CoeffIndex[FragIndex]] = (short) ((Huffman.DCT_VAL_CAT7_MIN + (ExtraBits & 0x1F)) * + -(((ExtraBits&0x20)>>4)-1)); + } else if ( Token == Huffman.DCT_VAL_CATEGORY8 ) { + /* Bit 9 determines sign, Bit 0-8 the value */ + ExpandedBlock[CoeffIndex[FragIndex]] = (short) ((Huffman.DCT_VAL_CAT8_MIN + (ExtraBits & 0x1FF)) * + -(((ExtraBits&0x200)>>8)-1)); + } + + /* Step on the coefficient index. */ + CoeffIndex[FragIndex] += 1; + } + } + + public void ReconRefFrames (Playback pbi){ + int i; + int j,k,m,n; + + /* predictor count. */ + int pcount; + + short wpc; + short PredictedDC; + int FragsAcross=pbi.HFragments; + int FromFragment; + int FragsDown = pbi.VFragments; + + int WhichFrame; + int WhichCase; + boolean isBaseFrame; + + isBaseFrame = pbi.getFrameType() == Constants.BASE_FRAME; + + pbi.filter.SetupLoopFilter(pbi.FrameQIndex); + + /* for y,u,v */ + for ( j = 0; j < 3 ; j++) { + /* pick which fragments based on Y, U, V */ + switch(j){ + case 0: /* y */ + FromFragment = 0; + FragsAcross = pbi.HFragments; + FragsDown = pbi.VFragments; + break; + case 1: /* u */ + FromFragment = pbi.YPlaneFragments; + FragsAcross = pbi.HFragments >> pbi.UVShiftX; + FragsDown = pbi.VFragments >> pbi.UVShiftY; + break; + /*case 2: v */ + default: + FromFragment = pbi.YPlaneFragments + pbi.UVPlaneFragments; + FragsAcross = pbi.HFragments >> pbi.UVShiftX; + FragsDown = pbi.VFragments >> pbi.UVShiftY; + break; + } + + /* initialize our array of last used DC Components */ + for(k=0;k<3;k++) + Last[k]=0; + + i=FromFragment; + + /* do prediction on all of Y, U or V */ + for ( m = 0 ; m < FragsDown ; m++) { + for ( n = 0 ; n < FragsAcross ; n++, i++){ + + /* only do 2 prediction if fragment coded and on non intra or + if all fragments are intra */ + if((pbi.display_fragments[i] != 0) || (pbi.getFrameType() == Constants.BASE_FRAME) ){ + /* Type of Fragment */ + WhichFrame = Mode2Frame[pbi.FragCodingMethod[i].getValue()]; + + /* Check Borderline Cases */ + WhichCase = (n==0?1:0) + ((m==0?1:0) << 1) + ((n+1 == FragsAcross?1:0) << 2); + + fn[0]=i-1; + fn[1]=i-FragsAcross-1; + fn[2]=i-FragsAcross; + fn[3]=i-FragsAcross+1; + + /* fragment valid for prediction use if coded and it comes + from same frame as the one we are predicting */ + for(k=pcount=wpc=0; k<4; k++) { + int pflag; + pflag=1<>= pc[wpc][4]; + } + + /* check for outranging on the two predictors that can outrange */ + if((wpc&(PU|PUL|PL)) == (PU|PUL|PL)){ + if(Math.abs(PredictedDC - v[2]) > 128) { + PredictedDC = (short)v[2]; + } else if( Math.abs(PredictedDC - v[0]) > 128) { + PredictedDC = (short)v[0]; + } else if( Math.abs(PredictedDC - v[1]) > 128) { + PredictedDC = (short)v[1]; + } + } + + pbi.QFragData[i][0] += PredictedDC; + } + + /* Save the last fragment coded for whatever frame we are + predicting from */ + Last[WhichFrame] = pbi.QFragData[i][0]; + + /* Inverse DCT and reconstitute buffer in thisframe */ + if (isBaseFrame) + ExpandKFBlock (pbi, i); + else + ExpandBlock (pbi, i); + } + } + } + } + + /* Copy the current reconstruction back to the last frame recon buffer. */ + if(pbi.CodedBlockIndex > (int) (pbi.UnitFragments >> 1)){ + short[] SwapReconBuffersTemp = pbi.ThisFrameRecon; + pbi.ThisFrameRecon = pbi.LastFrameRecon; + pbi.LastFrameRecon = SwapReconBuffersTemp; + CopyNotRecon(pbi, pbi.LastFrameRecon, pbi.ThisFrameRecon); + }else{ + CopyRecon(pbi, pbi.LastFrameRecon, pbi.ThisFrameRecon); + } + + /* Apply a loop filter to edge pixels of updated blocks */ + pbi.filter.LoopFilter(pbi); + + /* We may need to update the UMV border */ + UpdateUMVBorder(pbi, pbi.LastFrameRecon); + + /* Reconstruct the golden frame if necessary. + For VFW codec only on key frames */ + if (isBaseFrame) { + CopyRecon( pbi, pbi.GoldenFrame, pbi.LastFrameRecon ); + UpdateUMVBorder(pbi, pbi.GoldenFrame); + } + } +} diff --git a/common/src/main/java/eu/midnightdust/picturesign_theora/ogv/jheora/Debug.java b/common/src/main/java/eu/midnightdust/picturesign_theora/ogv/jheora/Debug.java new file mode 100644 index 0000000..404a767 --- /dev/null +++ b/common/src/main/java/eu/midnightdust/picturesign_theora/ogv/jheora/Debug.java @@ -0,0 +1,82 @@ +/* Cortado - a video player java applet + * Copyright (C) 2004 Fluendo S.L. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA. + */ + +package eu.midnightdust.picturesign_theora.ogv.jheora; + +public class Debug { + public static final int NONE = 0; + public static final int ERROR = 1; + public static final int WARNING = 2; + public static final int INFO = 3; + public static final int DEBUG = 4; + + public static int level = INFO; + + /* static id counter */ + private static int counter = 0; + private static long startTime = 0; + + public static final int genId() { + synchronized (Debug.class) { + return counter++; + } + } + + public static final String[] prefix = { + "NONE", + "ERRO", + "WARN", + "INFO", + "DBUG"}; + + public static String rpad(String s, int length) { + if ( length > s.length() ) { + int sz = length - s.length(); + char arr[] = new char[sz]; + for (int n=0; n= DEBUG) { + System.out.println( "[" + Debug.rpad( Thread.currentThread().getName(), 30 ) + " " + + Debug.rpad( Long.toString( t ), 6 ) + " " + prefix[lev] + "] " + line ); + } else { + System.out.println( "[" + prefix[lev] + "] " + line ); + } + } + } + + public static void error(String line) { Debug.log( ERROR, line ); } + public static void warning(String line) { Debug.log( WARNING, line ); } + public static void warn(String line) { Debug.log( WARNING, line ); } + public static void info(String line) { Debug.log( INFO, line ); } + public static void debug(String line) { Debug.log( DEBUG, line ); } +} diff --git a/common/src/main/java/eu/midnightdust/picturesign_theora/ogv/jheora/Decode.java b/common/src/main/java/eu/midnightdust/picturesign_theora/ogv/jheora/Decode.java new file mode 100644 index 0000000..ab3c70c --- /dev/null +++ b/common/src/main/java/eu/midnightdust/picturesign_theora/ogv/jheora/Decode.java @@ -0,0 +1,940 @@ +/* Jheora + * Copyright (C) 2004 Fluendo S.L. + * + * Written by: 2004 Wim Taymans + * + * Parts ported from the new Theora C reference encoder, which was mostly + * written by Timothy B. Terriberry + * + * Many thanks to + * The Xiph.Org Foundation http://www.xiph.org/ + * Jheora was based on their Theora reference decoder. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public License + * as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +package eu.midnightdust.picturesign_theora.ogv.jheora; + +import eu.midnightdust.picturesign_theora.ogv.jogg.*; + +interface ExtractMVectorComponent { + public int extract(OggBuffer opb); +} + +class ExtractMVectorComponentA implements ExtractMVectorComponent { + public int extract (OggBuffer opb) { + /* Get group to which coded component belongs */ + /* Now extract the appropriate number of bits to identify the component */ + switch (opb.readB(3)){ + case 0: + return 0; + case 1: + return 1; + case 2: + return -1; + case 3: + return 2 - (4*opb.readB(1)); + case 4: + return 3 - (6*opb.readB(1)); + case 5: + return (4 + opb.readB(2)) * -((opb.readB(1)<<1)-1); + case 6: + return (8 + opb.readB(3)) * -((opb.readB(1)<<1)-1); + case 7: + return (16 + opb.readB(4)) * -((opb.readB(1)<<1)-1); + } + return 0; + } +} + +class ExtractMVectorComponentB implements ExtractMVectorComponent { + public int extract (OggBuffer opb) { + /* Get group to which coded component belongs */ + return (opb.readB(5)) * -((opb.readB(1)<<1)-1); + } +} + +public final class Decode { + + private static final ExtractMVectorComponent MVA = new ExtractMVectorComponentA(); + private static final ExtractMVectorComponent MVB = new ExtractMVectorComponentB(); + + private static final CodingMode[][] modeAlphabet = { + /* Last motion vector dominates */ + { CodingMode.CODE_INTER_LAST_MV, CodingMode.CODE_INTER_PRIOR_LAST, + CodingMode.CODE_INTER_PLUS_MV, CodingMode.CODE_INTER_NO_MV, + CodingMode.CODE_INTRA, CodingMode.CODE_USING_GOLDEN, + CodingMode.CODE_GOLDEN_MV, CodingMode.CODE_INTER_FOURMV }, + + { CodingMode.CODE_INTER_LAST_MV, CodingMode.CODE_INTER_PRIOR_LAST, + CodingMode.CODE_INTER_NO_MV, CodingMode.CODE_INTER_PLUS_MV, + CodingMode.CODE_INTRA, CodingMode.CODE_USING_GOLDEN, + CodingMode.CODE_GOLDEN_MV, CodingMode.CODE_INTER_FOURMV }, + + { CodingMode.CODE_INTER_LAST_MV, CodingMode.CODE_INTER_PLUS_MV, + CodingMode.CODE_INTER_PRIOR_LAST, CodingMode.CODE_INTER_NO_MV, + CodingMode.CODE_INTRA, CodingMode.CODE_USING_GOLDEN, + CodingMode.CODE_GOLDEN_MV, CodingMode.CODE_INTER_FOURMV }, + + { CodingMode.CODE_INTER_LAST_MV, CodingMode.CODE_INTER_PLUS_MV, + CodingMode.CODE_INTER_NO_MV, CodingMode.CODE_INTER_PRIOR_LAST, + CodingMode.CODE_INTRA, CodingMode.CODE_USING_GOLDEN, + CodingMode.CODE_GOLDEN_MV, CodingMode.CODE_INTER_FOURMV }, + + /* No motion vector dominates */ + { CodingMode.CODE_INTER_NO_MV, CodingMode.CODE_INTER_LAST_MV, + CodingMode.CODE_INTER_PRIOR_LAST, CodingMode.CODE_INTER_PLUS_MV, + CodingMode.CODE_INTRA, CodingMode.CODE_USING_GOLDEN, + CodingMode.CODE_GOLDEN_MV, CodingMode.CODE_INTER_FOURMV }, + + { CodingMode.CODE_INTER_NO_MV, CodingMode.CODE_USING_GOLDEN, + CodingMode.CODE_INTER_LAST_MV, CodingMode.CODE_INTER_PRIOR_LAST, + CodingMode.CODE_INTER_PLUS_MV, CodingMode.CODE_INTRA, + CodingMode.CODE_GOLDEN_MV, CodingMode.CODE_INTER_FOURMV }, + + /* dummy */ + { CodingMode.CODE_INTER_NO_MV, CodingMode.CODE_USING_GOLDEN, + CodingMode.CODE_INTER_LAST_MV, CodingMode.CODE_INTER_PRIOR_LAST, + CodingMode.CODE_INTER_PLUS_MV, CodingMode.CODE_INTRA, + CodingMode.CODE_GOLDEN_MV, CodingMode.CODE_INTER_FOURMV }, + }; + + private int BlocksToDecode; + private int EOB_Run; + + private DCTDecode dctDecode = new DCTDecode(); + + private byte[] FragCoeffs; /* # of coeffs decoded so far for + fragment */ + + private MotionVector LastInterMV = new MotionVector (); + private MotionVector PriorLastInterMV = new MotionVector (); + private Playback pbi; + + public Decode (Playback pbi) { + FragCoeffs = new byte[pbi.UnitFragments]; + this.pbi = pbi; + } + + private int longRunBitStringDecode() { + /* lifted from new C Theora reference decoder */ + int bits; + int ret; + OggBuffer opb = pbi.opb; + /*Coding scheme: + Codeword Run Length + 0 1 + 10x 2-3 + 110x 4-5 + 1110xx 6-9 + 11110xxx 10-17 + 111110xxxx 18-33 + 111111xxxxxxxxxxxx 34-4129*/ + + bits = opb.readB(1); + if(bits==0)return 1; + bits = opb.readB(2); + if((bits&2)==0)return 2+(int)bits; + else if((bits&1)==0){ + bits = opb.readB(1); + return 4+(int)bits; + } + bits = opb.readB(3); + if((bits&4)==0)return 6+(int)bits; + else if((bits&2)==0){ + ret=10+((bits&1)<<2); + bits = opb.readB(2); + return ret+(int)bits; + } + else if((bits&1)==0){ + bits = opb.readB(4); + return 18+(int)bits; + } + bits = opb.readB(12); + return 34+(int)bits; + } + + private void decodeBlockLevelQi() { + /* lifted from new C Theora reference decoder */ + + /* pbi.CodedBlockIndex holds the number of coded blocks despite the + suboptimal variable name */ + int ncoded_frags = pbi.CodedBlockIndex; + + if(ncoded_frags <= 0) return; + if(pbi.frameNQIS == 1) { + /*If this frame has only a single qi value, then just set it in all coded + fragments.*/ + for(int coded_frag = 0; coded_frag < ncoded_frags; ++coded_frag) { + pbi.FragQs[pbi.CodedBlockList[coded_frag]] = 0; + } + } else{ + OggBuffer opb = pbi.opb; + int val; + int flag; + int nqi0; + int run_count; + /*Otherwise, we decode a qi index for each fragment, using two passes of + the same binary RLE scheme used for super-block coded bits. + The first pass marks each fragment as having a qii of 0 or greater than + 0, and the second pass (if necessary), distinguishes between a qii of + 1 and 2. + We store the qii of the fragment. */ + val = opb.readB(1); + flag = val; + run_count = nqi0 = 0; + int coded_frag = 0; + while(coded_frag < ncoded_frags){ + boolean full_run; + run_count = longRunBitStringDecode(); + full_run = (run_count >= 4129); + do { + pbi.FragQs[pbi.CodedBlockList[coded_frag++]] = (byte)flag; + if(flag < 1) ++nqi0; + } while(--run_count > 0 && coded_frag < ncoded_frags); + + if(full_run && coded_frag < ncoded_frags){ + val = opb.readB(1); + flag=(int)val; + } else { + //flag=!flag; + flag = (flag != 0) ? 0 : 1; + } + } + /*TODO: run_count should be 0 here. + If it's not, we should issue a warning of some kind.*/ + /*If we have 3 different qi's for this frame, and there was at least one + fragment with a non-zero qi, make the second pass.*/ + if(pbi.frameNQIS==3 && nqi0 < ncoded_frags){ + coded_frag = 0; + /*Skip qii==0 fragments.*/ + for(coded_frag = 0; coded_frag < ncoded_frags && pbi.FragQs[pbi.CodedBlockList[coded_frag]] == 0; ++coded_frag){} + val = opb.readB(1); + flag = val; + while(coded_frag < ncoded_frags){ + boolean full_run; + run_count = longRunBitStringDecode(); + full_run = run_count >= 4129; + for(; coded_frag < ncoded_frags; ++coded_frag){ + if(pbi.FragQs[pbi.CodedBlockList[coded_frag]] == 0) continue; + if(run_count-- <= 0) break; + pbi.FragQs[pbi.CodedBlockList[coded_frag]] += flag; + } + if(full_run && coded_frag < ncoded_frags){ + val = opb.readB(1); + flag = val; + } else { + //flag=!flag; + flag = (flag != 0) ? 0 : 1; + } + } + /*TODO: run_count should be 0 here. + If it's not, we should issue a warning of some kind.*/ + } + } + } + + private int loadFrame() + { + int DctQMask; + OggBuffer opb = pbi.opb; + + /* Is the frame and inter frame or a key frame */ + pbi.FrameType = (byte)opb.readB(1); + + /* Quality (Q) index */ + DctQMask = (int) opb.readB(6); + pbi.frameQIS[0] = DctQMask; + pbi.frameNQIS = 1; + + /* look if there are additional frame quality indices */ + int moreQs = opb.readB(1); + if(moreQs > 0) { + pbi.frameQIS[1] = (int) opb.readB(6); + pbi.frameNQIS = 2; + + moreQs = opb.readB(1); + if(moreQs > 0) { + pbi.frameQIS[2] = (int) opb.readB(6); + pbi.frameNQIS = 3; + } + } + + if ( (pbi.FrameType == Constants.BASE_FRAME) ){ + /* Read the type / coding method for the key frame. */ + pbi.KeyFrameType = (byte)opb.readB(1); + opb.readB(2); + } + + /* Set this frame quality value from Q Index */ + //pbi.ThisFrameQualityValue = pbi.QThreshTable[pbi.frameQ]; + + /* Read in the updated block map */ + pbi.frArray.quadDecodeDisplayFragments( pbi ); + + return 1; + } + + private void decodeModes (int SBRows, int SBCols) + { + int MB; + int SBcol; + int SBrow; + CodingMode[] FragCodingMethod; + int SB=0; + long ret; + int FragIndex; + CodingMode CodingMethod; + + int UVRow; + int UVColumn; + int UVFragOffset; + int MBListIndex = 0; + int i; + + FragCodingMethod = pbi.FragCodingMethod; + + /* If the frame is an intra frame then all blocks have mode intra. */ + if ( pbi.getFrameType() == Constants.BASE_FRAME ){ + MemUtils.set(FragCodingMethod, 0, CodingMode.CODE_INTRA, pbi.UnitFragments); + }else{ + /* Clear down the macro block level mode and MV arrays. Default coding mode */ + MemUtils.set(FragCodingMethod, 0, CodingMode.CODE_INTER_NO_MV, pbi.UnitFragments); + + CodingMode ModeEntry; /* Mode bits read */ + CodingMode[] ModeList; + + /* Read the coding method */ + ret = pbi.opb.readB( Constants.MODE_METHOD_BITS); + int CodingScheme=(int)ret; + + /* If the coding method is method 0 then we have to read in a + custom coding scheme */ + if ( CodingScheme == 0 ){ + CodingMode[] CustomModeAlphabet = new CodingMode[Constants.MAX_MODES]; + /* Read the coding scheme. */ + for ( i = 0; i < Constants.MAX_MODES; i++ ){ + ret = pbi.opb.readB( Constants.MODE_BITS); + CustomModeAlphabet[(int)ret]= CodingMode.MODES[i]; + } + ModeList=CustomModeAlphabet; + } + else{ + ModeList=modeAlphabet[CodingScheme-1]; + } + + /* Unravel the quad-tree */ + for ( SBrow=0; SBrow= 0){ + /* Is the Macro-Block coded: */ + if ( pbi.MBCodedFlags[MBListIndex++] != 0){ + + /* Unpack the mode. */ + if ( CodingScheme == (Constants.MODE_METHODS-1) ){ + /* This is the fall back coding scheme. */ + /* Simply MODE_BITS bits per mode entry. */ + ret = pbi.opb.readB( Constants.MODE_BITS); + CodingMethod = CodingMode.MODES[(int)ret]; + }else{ + ModeEntry = pbi.frArray.unpackMode(pbi.opb); + CodingMethod = ModeList[ModeEntry.getValue()]; + } + + /* Note the coding mode for each block in macro block. */ + FragCodingMethod[FragIndex] = CodingMethod; + FragCodingMethod[FragIndex + 1] = CodingMethod; + FragCodingMethod[FragIndex + pbi.HFragments] = CodingMethod; + FragCodingMethod[FragIndex + pbi.HFragments + 1] = CodingMethod; + + /* Matching fragments in the U and V planes */ + if (pbi.UVShiftX == 1 && pbi.UVShiftY == 1){ /* TH_PF_420 */ + UVRow = (FragIndex / (pbi.HFragments * 2)); + UVColumn = (FragIndex % pbi.HFragments) / 2; + UVFragOffset = (UVRow * (pbi.HFragments / 2)) + UVColumn; + FragCodingMethod[pbi.YPlaneFragments + UVFragOffset] = CodingMethod; + FragCodingMethod[pbi.YPlaneFragments + pbi.UVPlaneFragments + UVFragOffset] = + CodingMethod; + } else if (pbi.UVShiftX == 0) { /* TH_PF_444 */ + FragIndex += pbi.YPlaneFragments; + FragCodingMethod[FragIndex] = + FragCodingMethod[FragIndex + 1] = + FragCodingMethod[FragIndex + pbi.HFragments] = + FragCodingMethod[FragIndex + pbi.HFragments + 1] = CodingMethod; + FragIndex += pbi.UVPlaneFragments; + FragCodingMethod[FragIndex] = + FragCodingMethod[FragIndex + 1] = + FragCodingMethod[FragIndex + pbi.HFragments] = + FragCodingMethod[FragIndex + pbi.HFragments + 1] = CodingMethod; + } else { /*TH_PF_422 */ + FragIndex = pbi.YPlaneFragments + FragIndex/2; + FragCodingMethod[FragIndex] = + FragCodingMethod[FragIndex + pbi.HFragments/2] = CodingMethod; + FragIndex += pbi.UVPlaneFragments; + FragCodingMethod[FragIndex] = + FragCodingMethod[FragIndex + pbi.HFragments/2] = CodingMethod; + } + + } + } + } + + /* Next Super-Block */ + SB++; + } + } + } + } + + + private void decodeMVectors (int SBRows, int SBCols) + { + int FragIndex; + int MB; + int SBrow; + int SBcol; + int SB=0; + CodingMode CodingMethod; + + ExtractMVectorComponent MVC; + + int UVRow; + int UVColumn; + int UVFragOffset; + int x,y; + + int MBListIndex = 0; + OggBuffer opb = pbi.opb; + + /* Should not be decoding motion vectors if in INTRA only mode. */ + if (pbi.getFrameType() == Constants.BASE_FRAME ){ + return; + } + + MotionVector dummy = new MotionVector(); + + /* set the default motion vector to 0,0 */ + LastInterMV.x = 0; + LastInterMV.y = 0; + PriorLastInterMV.x = 0; + PriorLastInterMV.y = 0; + + /* Read the entropy method used and set up the appropriate decode option */ + if (opb.readB(1) == 0 ) + MVC = MVA; + else + MVC = MVB; + + /* Unravel the quad-tree */ + for ( SBrow=0; SBrow= 0 ) { + /* Is the Macro-Block further coded: */ + if (pbi.MBCodedFlags[MBListIndex++] != 0){ + /* Unpack the mode (and motion vectors if necessary). */ + CodingMethod = pbi.FragCodingMethod[FragIndex]; + + /* Note the coding mode and vector for each block in the + current macro block. */ + MotionVector MVect0 = pbi.FragMVect[FragIndex]; + MotionVector MVect1 = pbi.FragMVect[FragIndex + 1]; + MotionVector MVect2 = pbi.FragMVect[FragIndex + pbi.HFragments]; + MotionVector MVect3 = pbi.FragMVect[FragIndex + pbi.HFragments + 1]; + + /* Matching fragments in the U and V planes */ + UVRow = (FragIndex / (pbi.HFragments << pbi.UVShiftY)); + UVColumn = (FragIndex % pbi.HFragments) >> pbi.UVShiftX; + UVFragOffset = (UVRow * (pbi.HFragments >> pbi.UVShiftX)) + UVColumn; + + MotionVector MVectU0 = pbi.FragMVect[pbi.YPlaneFragments + UVFragOffset]; + MotionVector MVectV0 = pbi.FragMVect[pbi.YPlaneFragments + pbi.UVPlaneFragments + UVFragOffset]; + MotionVector MVectU1 = dummy; + MotionVector MVectV1 = dummy; + MotionVector MVectU2 = dummy; + MotionVector MVectV2 = dummy; + MotionVector MVectU3 = dummy; + MotionVector MVectV3 = dummy; + if (pbi.UVShiftY == 0) { + MVectU2 = pbi.FragMVect[pbi.YPlaneFragments + UVFragOffset + (pbi.HFragments>>pbi.UVShiftX)]; + MVectV2 = pbi.FragMVect[pbi.YPlaneFragments + pbi.UVPlaneFragments + UVFragOffset + (pbi.HFragments>>pbi.UVShiftX)]; + if (pbi.UVShiftX == 0){ + MVectU1 = pbi.FragMVect[pbi.YPlaneFragments + UVFragOffset + 1]; + MVectV1 = pbi.FragMVect[pbi.YPlaneFragments + pbi.UVPlaneFragments + UVFragOffset + 1]; + MVectU3 = pbi.FragMVect[pbi.YPlaneFragments + UVFragOffset + pbi.HFragments + 1]; + MVectV3 = pbi.FragMVect[pbi.YPlaneFragments + pbi.UVPlaneFragments + UVFragOffset + pbi.HFragments + 1]; + } + } + + /* Read the motion vector or vectors if present. */ + if (CodingMethod == CodingMode.CODE_INTER_PLUS_MV) { + PriorLastInterMV.x = LastInterMV.x; + PriorLastInterMV.y = LastInterMV.y; + LastInterMV.x = MVect0.x = + MVect1.x = + MVect2.x = + MVect3.x = + MVectU0.x = + MVectV0.x = + MVectU1.x = + MVectV1.x = + MVectU2.x = + MVectV2.x = + MVectU3.x = + MVectV3.x = MVC.extract(opb); + + LastInterMV.y = MVect0.y = + MVect1.y = + MVect2.y = + MVect3.y = + MVectU0.y = + MVectV0.y = + MVectU1.y = + MVectV1.y = + MVectU2.y = + MVectV2.y = + MVectU3.y = + MVectV3.y = MVC.extract(opb); + } + else if (CodingMethod == CodingMode.CODE_GOLDEN_MV){ + MVect0.x = MVect1.x = + MVect2.x = + MVect3.x = + MVectU0.x = + MVectV0.x = + MVectU1.x = + MVectV1.x = + MVectU2.x = + MVectV2.x = + MVectU3.x = + MVectV3.x = MVC.extract(opb); + MVect0.y = MVect1.y = + MVect2.y = + MVect3.y = + MVectU0.y = + MVectV0.y = + MVectU1.y = + MVectV1.y = + MVectU2.y = + MVectV2.y = + MVectU3.y = + MVectV3.y = MVC.extract(opb); + } + else if ( CodingMethod == CodingMode.CODE_INTER_FOURMV ){ + + /* Update last MV and prior last mv */ + PriorLastInterMV.x = LastInterMV.x; + PriorLastInterMV.y = LastInterMV.y; + + /* Extrac the 4 Y MVs */ + if(pbi.display_fragments[FragIndex] != 0) { + x = MVect0.x = MVC.extract(opb); + y = MVect0.y = MVC.extract(opb); + LastInterMV.x = MVect0.x; + LastInterMV.y = MVect0.y; + } else { + x = MVect0.x = 0; + y = MVect0.y = 0; + } + + if(pbi.display_fragments[FragIndex + 1] != 0) { + x += MVect1.x = MVC.extract(opb); + y += MVect1.y = MVC.extract(opb); + LastInterMV.x = MVect1.x; + LastInterMV.y = MVect1.y; + } else { + x += MVect1.x = 0; + y += MVect1.y = 0; + } + + if(pbi.display_fragments[FragIndex + pbi.HFragments] != 0) { + x += MVect2.x = MVC.extract(opb); + y += MVect2.y = MVC.extract(opb); + LastInterMV.x = MVect2.x; + LastInterMV.y = MVect2.y; + } else { + x += MVect2.x = 0; + y += MVect2.y = 0; + } + + if(pbi.display_fragments[FragIndex + pbi.HFragments + 1] != 0) { + x += MVect3.x = MVC.extract(opb); + y += MVect3.y = MVC.extract(opb); + LastInterMV.x = MVect3.x; + LastInterMV.y = MVect3.y; + } else { + x += MVect3.x = 0; + y += MVect3.y = 0; + } + + if(pbi.UVShiftY == 0) { + if(pbi.UVShiftX == 0) { + MVectU0.x = MVectV0.x = MVect0.x; + MVectU0.y = MVectV0.y = MVect0.y; + MVectU1.x = MVectV1.x = MVect1.x; + MVectU1.y = MVectV1.y = MVect1.y; + MVectU2.x = MVectV2.x = MVect2.x; + MVectU2.y = MVectV2.y = MVect2.y; + MVectU3.x = MVectV3.x = MVect3.x; + MVectU3.y = MVectV3.y = MVect3.y; + } else { + /* 4:2:2, so average components only horizontally */ + x = MVect0.x + MVect1.x; + if (x >= 0 ) x = (x+1) / 2; + else x = (x-1) / 2; + MVectU0.x = + MVectV0.x = x; + y = MVect0.y + MVect1.y; + if (y >= 0 ) y = (y+1) / 2; + else y = (y-1) / 2; + MVectU0.y = + MVectV0.y = y; + x = MVect2.x + MVect3.x; + if (x >= 0 ) x = (x+1) / 2; + else x = (x-1) / 2; + MVectU2.x = + MVectV2.x = x; + y = MVect2.y + MVect3.y; + if (y >= 0 ) y = (y+1) / 2; + else y = (y-1) / 2; + MVectU2.y = + MVectV2.y = y; + } + } else { + /* Calculate the U and V plane MVs as the average of the + Y plane MVs. */ + /* First .x component */ + if (x >= 0 ) x = (x+2) / 4; + else x = (x-2) / 4; + MVectU0.x = x; + MVectV0.x = x; + /* Then .y component */ + if (y >= 0 ) y = (y+2) / 4; + else y = (y-2) / 4; + MVectU0.y = y; + MVectV0.y = y; + } + + } + else if ( CodingMethod == CodingMode.CODE_INTER_LAST_MV ){ + /* Use the last coded Inter motion vector. */ + MVect0.x = MVect1.x = + MVect2.x = + MVect3.x = + MVectU0.x = + MVectV0.x = + MVectU1.x = + MVectV1.x = + MVectU2.x = + MVectV2.x = + MVectU3.x = + MVectV3.x = LastInterMV.x; + MVect0.y = MVect1.y = + MVect2.y = + MVect3.y = + MVectU0.y = + MVectV0.y = + MVectU1.y = + MVectV1.y = + MVectU2.y = + MVectV2.y = + MVectU3.y = + MVectV3.y = LastInterMV.y; + } + else if ( CodingMethod == CodingMode.CODE_INTER_PRIOR_LAST ){ + /* Use the next-to-last coded Inter motion vector. */ + MVect0.x = MVect1.x = + MVect2.x = + MVect3.x = + MVectU0.x = + MVectV0.x = + MVectU1.x = + MVectV1.x = + MVectU2.x = + MVectV2.x = + MVectU3.x = + MVectV3.x = PriorLastInterMV.x; + MVect0.y = MVect1.y = + MVect2.y = + MVect3.y = + MVectU0.y = + MVectV0.y = + MVectU1.y = + MVectV1.y = + MVectU2.y = + MVectV2.y = + MVectU3.y = + MVectV3.y = PriorLastInterMV.y; + + /* Swap the prior and last MV cases over */ + MotionVector TmpMVect = PriorLastInterMV; + PriorLastInterMV = LastInterMV; + LastInterMV = TmpMVect; + } + else { + /* Clear the motion vector else */ + MVect0.x = 0; + MVect0.y = 0; + } + } + } + } + /* Next Super-Block */ + SB++; + } + } + } + + private final int ExtractToken(OggBuffer opb, + HuffEntry CurrentRoot){ + /* Loop searches down through tree based upon bits read from the + bitstream */ + /* until it hits a leaf at which point we have decoded a token */ + while (CurrentRoot.value < 0 ){ + CurrentRoot = CurrentRoot.Child[(int)opb.readB(1)]; + } + return CurrentRoot.value; + } + + private void unpackAndExpandToken(short[] ExpandedBlock, + byte[] CoeffIndex, + int FragIndex, + int HuffChoice){ + int ExtraBits = 0; + + int Token = ExtractToken(pbi.opb, pbi.HuffRoot_VP3x[HuffChoice]); + + /* Now.. if we are using the DCT optimised coding system, extract any + * assosciated additional bits token. + */ + if (pbi.ExtraBitLengths_VP3x[Token] > 0){ + /* Extract the appropriate number of extra bits. */ + ExtraBits = (int)pbi.opb.readB(pbi.ExtraBitLengths_VP3x[Token]); + } + + /* Take token dependant action */ + if ( Token >= Huffman.DCT_SHORT_ZRL_TOKEN ) { + /* "Value", "zero run" and "zero run value" tokens */ + dctDecode.ExpandToken(ExpandedBlock, CoeffIndex, FragIndex, Token, ExtraBits ); + if ( CoeffIndex[FragIndex] >= Constants.BLOCK_SIZE ) + BlocksToDecode --; + }else{ + /* Special action and EOB tokens */ + switch ( Token ){ + case Huffman.DCT_EOB_PAIR_TOKEN: + EOB_Run = 1; + break; + case Huffman.DCT_EOB_TRIPLE_TOKEN: + EOB_Run = 2; + break; + case Huffman.DCT_REPEAT_RUN_TOKEN: + EOB_Run = ExtraBits + 3; + break; + case Huffman.DCT_REPEAT_RUN2_TOKEN: + EOB_Run = ExtraBits + 7; + break; + case Huffman.DCT_REPEAT_RUN3_TOKEN: + EOB_Run = ExtraBits + 15; + break; + case Huffman.DCT_REPEAT_RUN4_TOKEN: + EOB_Run = ExtraBits - 1; + break; + case Huffman.DCT_EOB_TOKEN: + break; + default: + return; + } + CoeffIndex[FragIndex] = Constants.BLOCK_SIZE; + BlocksToDecode --; + } + } + + private void unPackVideo () + { + int EncodedCoeffs = 1; + int FragIndex; + + int AcHuffChoice; + int AcHuffChoice1; + int AcHuffChoice2; + + int DcHuffChoice; + + /* Bail out immediately if a decode error has already been reported. */ + if ( pbi.DecoderErrorCode != 0) + return; + + /* Clear down the array that indicates the current coefficient index + for each block. */ + MemUtils.set(FragCoeffs, 0, 0, pbi.UnitFragments); + MemUtils.set(pbi.FragCoefEOB, 0, 0, pbi.UnitFragments); + + /* Note the number of blocks to decode */ + BlocksToDecode = pbi.CodedBlockIndex; + + /* Get the DC huffman table choice for Y and then UV */ + int DcHuffChoice1 = (int)(pbi.opb.readB(Huffman.DC_HUFF_CHOICE_BITS) + Huffman.DC_HUFF_OFFSET); + int DcHuffChoice2 = (int)(pbi.opb.readB(Huffman.DC_HUFF_CHOICE_BITS) + Huffman.DC_HUFF_OFFSET); + + /* UnPack DC coefficients / tokens */ + int cbl = 0; + int cble = pbi.CodedBlockIndex; + while (cbl < cble) { + /* Get the block data index */ + FragIndex = pbi.CodedBlockList[cbl]; + pbi.FragCoefEOB[FragIndex] = FragCoeffs[FragIndex]; + + /* Select the appropriate huffman table offset according to + whether the token is from a Y or UV block */ + if (FragIndex < (int)pbi.YPlaneFragments ) + DcHuffChoice = DcHuffChoice1; + else + DcHuffChoice = DcHuffChoice2; + + /* If we are in the middle of an EOB run */ + if ( EOB_Run != 0){ + /* Mark the current block as fully expanded and decrement + EOB_RUN count */ + FragCoeffs[FragIndex] = Constants.BLOCK_SIZE; + EOB_Run --; + BlocksToDecode --; + }else{ + /* Else unpack a DC token */ + unpackAndExpandToken(pbi.QFragData[FragIndex], + FragCoeffs, + FragIndex, + DcHuffChoice); + } + cbl++; + } + + /* Get the AC huffman table choice for Y and then for UV. */ + int AcHuffIndex1 = (int) (pbi.opb.readB(Huffman.AC_HUFF_CHOICE_BITS) + Huffman.AC_HUFF_OFFSET); + int AcHuffIndex2 = (int) (pbi.opb.readB(Huffman.AC_HUFF_CHOICE_BITS) + Huffman.AC_HUFF_OFFSET); + + /* Unpack Lower AC coefficients. */ + while ( EncodedCoeffs < 64 ) { + /* Repeatedly scan through the list of blocks. */ + cbl = 0; + cble = pbi.CodedBlockIndex; + + /* Huffman table selection based upon which AC coefficient we are on */ + if ( EncodedCoeffs <= Huffman.AC_TABLE_2_THRESH ){ + AcHuffChoice1 = AcHuffIndex1; + AcHuffChoice2 = AcHuffIndex2; + }else if ( EncodedCoeffs <= Huffman.AC_TABLE_3_THRESH ){ + AcHuffChoice1 = (AcHuffIndex1 + Huffman.AC_HUFF_CHOICES); + AcHuffChoice2 = (AcHuffIndex2 + Huffman.AC_HUFF_CHOICES); + } else if ( EncodedCoeffs <= Huffman.AC_TABLE_4_THRESH ){ + AcHuffChoice1 = (AcHuffIndex1 + (Huffman.AC_HUFF_CHOICES * 2)); + AcHuffChoice2 = (AcHuffIndex2 + (Huffman.AC_HUFF_CHOICES * 2)); + } else { + AcHuffChoice1 = (AcHuffIndex1 + (Huffman.AC_HUFF_CHOICES * 3)); + AcHuffChoice2 = (AcHuffIndex2 + (Huffman.AC_HUFF_CHOICES * 3)); + } + + while(cbl < cble ) { + /* Get the linear index for the current fragment. */ + FragIndex = pbi.CodedBlockList[cbl]; + + /* Should we decode a token for this block on this pass. */ + if ( FragCoeffs[FragIndex] <= EncodedCoeffs ) { + pbi.FragCoefEOB[FragIndex] = FragCoeffs[FragIndex]; + /* If we are in the middle of an EOB run */ + if ( EOB_Run != 0) { + /* Mark the current block as fully expanded and decrement + EOB_RUN count */ + FragCoeffs[FragIndex] = Constants.BLOCK_SIZE; + EOB_Run --; + BlocksToDecode --; + }else{ + /* Else unpack an AC token */ + /* Work out which huffman table to use, then decode a token */ + if ( FragIndex < (int)pbi.YPlaneFragments ) + AcHuffChoice = AcHuffChoice1; + else + AcHuffChoice = AcHuffChoice2; + + unpackAndExpandToken(pbi.QFragData[FragIndex], + FragCoeffs, + FragIndex, + AcHuffChoice); + } + } + cbl++; + } + + /* Test for condition where there are no blocks left with any + tokesn to decode */ + if ( BlocksToDecode == 0) + break; + + EncodedCoeffs ++; + } + } + + public int loadAndDecode() + { + int loadFrameOK; + + /* Load the next frame. */ + loadFrameOK = loadFrame(); + + if (loadFrameOK != 0){ + //System.out.println("Load: "+loadFrameOK+" "+pbi.ThisFrameQualityValue+" "+pbi.LastFrameQualityValue); + + /* Decode the data into the fragment buffer. */ + /* Bail out immediately if a decode error has already been reported. */ + if (pbi.DecoderErrorCode != 0) + return 0; + + /* Zero Decoder EOB run count */ + EOB_Run = 0; + + /* Make a note of the number of coded blocks this frame */ + pbi.CodedBlocksThisFrame = pbi.CodedBlockIndex; + + /* Decode the modes data */ + decodeModes(pbi.YSBRows, pbi.YSBCols); + + /* Unpack and decode the motion vectors. */ + decodeMVectors (pbi.YSBRows, pbi.YSBCols); + + /* Unpack per-block quantizer information */ + decodeBlockLevelQi(); + + /* Unpack and decode the actual video data. */ + unPackVideo(); + + /* Reconstruct and display the frame */ + dctDecode.ReconRefFrames(pbi); + + return 0; + } + + return(Result.BADPACKET); + } +} diff --git a/common/src/main/java/eu/midnightdust/picturesign_theora/ogv/jheora/Filter.java b/common/src/main/java/eu/midnightdust/picturesign_theora/ogv/jheora/Filter.java new file mode 100644 index 0000000..d188fe2 --- /dev/null +++ b/common/src/main/java/eu/midnightdust/picturesign_theora/ogv/jheora/Filter.java @@ -0,0 +1,394 @@ +/* Jheora + * Copyright (C) 2004 Fluendo S.L. + * + * Written by: 2004 Wim Taymans + * + * Many thanks to + * The Xiph.Org Foundation http://www.xiph.org/ + * Jheora was based on their Theora reference decoder. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public License + * as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +package eu.midnightdust.picturesign_theora.ogv.jheora; + +public final class Filter +{ + /* in-loop filter tables. one of these is used in dct_decode.c */ + private static final byte[] LoopFilterLimitValuesV1 = { + 30, 25, 20, 20, 15, 15, 14, 14, + 13, 13, 12, 12, 11, 11, 10, 10, + 9, 9, 8, 8, 7, 7, 7, 7, + 6, 6, 6, 6, 5, 5, 5, 5, + 4, 4, 4, 4, 3, 3, 3, 3, + 2, 2, 2, 2, 2, 2, 2, 2, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0 + }; + + /* Loop filter bounding values */ + private byte[] LoopFilterLimits = new byte[Constants.Q_TABLE_SIZE]; + private int[] FiltBoundingValue = new int[512]; + + private void SetupBoundingValueArray_Generic(int FLimit) + { + /* Set up the bounding value array. */ + MemUtils.set (FiltBoundingValue, 0, 0, 512); + for (int i = 0; i < FLimit; i++ ){ + FiltBoundingValue[256-i-FLimit] = (-FLimit+i); + FiltBoundingValue[256-i] = -i; + FiltBoundingValue[256+i] = i; + FiltBoundingValue[256+i+FLimit] = FLimit-i; + } + } + + /* copy in-loop filter limits from the bitstream header into our instance */ + public void copyFilterTables(TheoraInfo ci) { + System.arraycopy(ci.LoopFilterLimitValues, 0, LoopFilterLimits, 0, Constants.Q_TABLE_SIZE); + } + + /* initialize the filter limits from our static table */ + public void InitFilterTables() { + System.arraycopy(LoopFilterLimitValuesV1, 0, LoopFilterLimits, 0, Constants.Q_TABLE_SIZE); + } + + public void SetupLoopFilter(int FrameQIndex){ + int FLimit; + + /* nb: this was using the V2 values rather than V1 + we think is was a mistake; the results were not used */ + FLimit = LoopFilterLimits[FrameQIndex]; + SetupBoundingValueArray_Generic(FLimit); + } + + private static final short clamp255(int val) { + return (short)((~(val>>31)) & 255 & (val | ((255-val)>>31))); + } + + private void FilterHoriz(short[] PixelPtr, int idx, + int LineLength, + int[] BoundingValuePtr) + { + int j; + int FiltVal; + + for ( j = 0; j < 8; j++ ){ + FiltVal = + ( PixelPtr[0 + idx] ) - + ( PixelPtr[1 + idx] * 3 ) + + ( PixelPtr[2 + idx] * 3 ) - + ( PixelPtr[3 + idx] ); + + FiltVal = BoundingValuePtr[256 + ((FiltVal + 4) >> 3)]; + + PixelPtr[1 + idx] = clamp255(PixelPtr[1 + idx] + FiltVal); + PixelPtr[2 + idx] = clamp255(PixelPtr[2 + idx] - FiltVal); + + idx += LineLength; + } + } + + private void FilterVert(short[] PixelPtr, int idx, + int LineLength, + int[] BoundingValuePtr){ + int j; + int FiltVal; + + /* the math was correct, but negative array indicies are forbidden + by ANSI/C99 and will break optimization on several modern + compilers */ + + idx -= 2*LineLength; + + for ( j = 0; j < 8; j++ ) { + FiltVal = + ( PixelPtr[idx + 0] ) - + ( PixelPtr[idx + LineLength] * 3 ) + + ( PixelPtr[idx + 2 * LineLength] * 3 ) - + ( PixelPtr[idx + 3 * LineLength] ); + + FiltVal = BoundingValuePtr[256 + ((FiltVal + 4) >> 3)]; + + PixelPtr[idx + LineLength] = clamp255(PixelPtr[idx + LineLength] + FiltVal); + PixelPtr[idx + 2 * LineLength] = clamp255(PixelPtr[idx + 2*LineLength] - FiltVal); + + idx++; + } + } + + public void LoopFilter(Playback pbi){ + int FragsAcross=pbi.HFragments; + int FromFragment; + int FragsDown = pbi.VFragments; + int LineFragments; + int LineLength; + int FLimit; + int QIndex; + int i,j,m,n; + int index; + + /* Set the limit value for the loop filter based upon the current + quantizer. */ + + QIndex = pbi.frameQIS[0]; + + FLimit = LoopFilterLimits[QIndex]; + if ( FLimit == 0 ) return; + SetupBoundingValueArray_Generic(FLimit); + + for ( j = 0; j < 3 ; j++){ + switch(j) { + case 0: /* y */ + FromFragment = 0; + FragsAcross = pbi.HFragments; + FragsDown = pbi.VFragments; + LineLength = pbi.YStride; + LineFragments = pbi.HFragments; + break; + case 1: /* u */ + FromFragment = pbi.YPlaneFragments; + FragsAcross = pbi.HFragments >> 1; + FragsDown = pbi.VFragments >> 1; + LineLength = pbi.UVStride; + LineFragments = pbi.HFragments / 2; + break; + /*case 2: v */ + default: + FromFragment = pbi.YPlaneFragments + pbi.UVPlaneFragments; + FragsAcross = pbi.HFragments >> 1; + FragsDown = pbi.VFragments >> 1; + LineLength = pbi.UVStride; + LineFragments = pbi.HFragments / 2; + break; + } + + i=FromFragment; + + /************************************************************** + First Row + **************************************************************/ + /* first column conditions */ + /* only do 2 prediction if fragment coded and on non intra or if + all fragments are intra */ + if( pbi.display_fragments[i] != 0){ + /* Filter right hand border only if the block to the right is + not coded */ + if ( pbi.display_fragments[ i + 1 ] == 0){ + FilterHoriz(pbi.LastFrameRecon, + pbi.recon_pixel_index_table[i]+6, + LineLength,FiltBoundingValue); + } + + /* Bottom done if next row set */ + if( pbi.display_fragments[ i + LineFragments] == 0){ + FilterVert(pbi.LastFrameRecon, + pbi.recon_pixel_index_table[i+LineFragments], + LineLength, FiltBoundingValue); + } + } + i++; + + /***************************************************************/ + /* middle columns */ + for ( n = 1 ; n < FragsAcross - 1 ; n++) { + if( pbi.display_fragments[i] != 0){ + index = pbi.recon_pixel_index_table[i]; + + /* Filter Left edge always */ + FilterHoriz(pbi.LastFrameRecon, index-2, + LineLength, FiltBoundingValue); + + /* Filter right hand border only if the block to the right is + not coded */ + if (pbi.display_fragments[ i + 1 ] == 0){ + FilterHoriz(pbi.LastFrameRecon, + index+6, + LineLength, FiltBoundingValue); + } + + /* Bottom done if next row set */ + if(pbi.display_fragments[ i + LineFragments] == 0){ + FilterVert(pbi.LastFrameRecon, + pbi.recon_pixel_index_table[i+LineFragments], + LineLength, FiltBoundingValue); + } + + } + i++; + } + + /***************************************************************/ + /* Last Column */ + if( pbi.display_fragments[i] != 0){ + /* Filter Left edge always */ + FilterHoriz(pbi.LastFrameRecon, + pbi.recon_pixel_index_table[i] - 2 , + LineLength, FiltBoundingValue); + + /* Bottom done if next row set */ + if(pbi.display_fragments[ i + LineFragments] == 0){ + FilterVert(pbi.LastFrameRecon, + pbi.recon_pixel_index_table[i+LineFragments], + LineLength, FiltBoundingValue); + } + } + i++; + + /***************************************************************/ + /* Middle Rows */ + /***************************************************************/ + for ( m = 1 ; m < FragsDown-1 ; m++) { + + /*****************************************************************/ + /* first column conditions */ + /* only do 2 prediction if fragment coded and on non intra or if + all fragments are intra */ + if(pbi.display_fragments[i] != 0){ + index = pbi.recon_pixel_index_table[i]; + + /* TopRow is always done */ + FilterVert(pbi.LastFrameRecon, index, + LineLength, FiltBoundingValue); + + /* Filter right hand border only if the block to the right is + not coded */ + if (pbi.display_fragments[ i + 1 ] == 0){ + FilterHoriz(pbi.LastFrameRecon, index + 6, + LineLength, FiltBoundingValue); + } + + /* Bottom done if next row set */ + if(pbi.display_fragments[ i + LineFragments] == 0){ + FilterVert(pbi.LastFrameRecon, + pbi.recon_pixel_index_table[i+LineFragments], + LineLength, FiltBoundingValue); + } + } + i++; + + /*****************************************************************/ + /* middle columns */ + for ( n = 1 ; n < FragsAcross - 1 ; n++, i++){ + + if( pbi.display_fragments[i] != 0){ + index = pbi.recon_pixel_index_table[i]; + /* Filter Left edge always */ + FilterHoriz(pbi.LastFrameRecon, index - 2, + LineLength, FiltBoundingValue); + + /* TopRow is always done */ + FilterVert(pbi.LastFrameRecon, index, + LineLength, FiltBoundingValue); + + /* Filter right hand border only if the block to the right + is not coded */ + if (pbi.display_fragments[ i + 1 ] == 0){ + FilterHoriz(pbi.LastFrameRecon, index + 6, + LineLength, FiltBoundingValue); + } + + /* Bottom done if next row set */ + if(pbi.display_fragments[ i + LineFragments] == 0){ + FilterVert(pbi.LastFrameRecon, + pbi.recon_pixel_index_table[i+LineFragments], + LineLength, FiltBoundingValue); + } + } + } + + /******************************************************************/ + /* Last Column */ + if( pbi.display_fragments[i] != 0){ + index = pbi.recon_pixel_index_table[i]; + + /* Filter Left edge always*/ + FilterHoriz(pbi.LastFrameRecon, index - 2, + LineLength, FiltBoundingValue); + + /* TopRow is always done */ + FilterVert(pbi.LastFrameRecon, index, + LineLength, FiltBoundingValue); + + /* Bottom done if next row set */ + if(pbi.display_fragments[ i + LineFragments] == 0){ + FilterVert(pbi.LastFrameRecon, + pbi.recon_pixel_index_table[i+LineFragments], + LineLength, FiltBoundingValue); + } + } + i++; + } + + /*******************************************************************/ + /* Last Row */ + + /* first column conditions */ + /* only do 2 prediction if fragment coded and on non intra or if + all fragments are intra */ + if(pbi.display_fragments[i] != 0){ + index = pbi.recon_pixel_index_table[i]; + + /* TopRow is always done */ + FilterVert(pbi.LastFrameRecon, index, + LineLength, FiltBoundingValue); + + /* Filter right hand border only if the block to the right is + not coded */ + if (pbi.display_fragments[ i + 1 ] == 0){ + FilterHoriz(pbi.LastFrameRecon, index+6, + LineLength, FiltBoundingValue); + } + } + i++; + + /******************************************************************/ + /* middle columns */ + for ( n = 1 ; n < FragsAcross - 1 ; n++, i++){ + if( pbi.display_fragments[i] != 0){ + index = pbi.recon_pixel_index_table[i]; + + /* Filter Left edge always */ + FilterHoriz(pbi.LastFrameRecon, index-2, + LineLength, FiltBoundingValue); + + /* TopRow is always done */ + FilterVert(pbi.LastFrameRecon, index, + LineLength, FiltBoundingValue); + + /* Filter right hand border only if the block to the right is + not coded */ + if (pbi.display_fragments[ i + 1 ] == 0){ + FilterHoriz(pbi.LastFrameRecon, index+6, + LineLength, FiltBoundingValue); + } + } + } + + /******************************************************************/ + /* Last Column */ + if(pbi.display_fragments[i] != 0){ + index = pbi.recon_pixel_index_table[i]; + + /* Filter Left edge always */ + FilterHoriz(pbi.LastFrameRecon, index - 2, + LineLength, FiltBoundingValue); + + /* TopRow is always done */ + FilterVert(pbi.LastFrameRecon, index, + LineLength, FiltBoundingValue); + } + } + } +} diff --git a/common/src/main/java/eu/midnightdust/picturesign_theora/ogv/jheora/FrArray.java b/common/src/main/java/eu/midnightdust/picturesign_theora/ogv/jheora/FrArray.java new file mode 100644 index 0000000..bc55157 --- /dev/null +++ b/common/src/main/java/eu/midnightdust/picturesign_theora/ogv/jheora/FrArray.java @@ -0,0 +1,419 @@ +/* Jheora + * Copyright (C) 2004 Fluendo S.L. + * + * Written by: 2004 Wim Taymans + * + * Many thanks to + * The Xiph.Org Foundation http://www.xiph.org/ + * Jheora was based on their Theora reference decoder. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public License + * as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +package eu.midnightdust.picturesign_theora.ogv.jheora; + +import eu.midnightdust.picturesign_theora.ogv.jogg.*; + +public class FrArray { + + private int bit_pattern; + private byte bits_so_far; + private byte NextBit; + private int BitsLeft; + + public FrArray() { + } + + public void init() { + /* Initialise the decoding of a run. */ + bit_pattern = 0; + bits_so_far = 0; + } + + private int deCodeBlockRun(int bit_value){ + /* Add in the new bit value. */ + bits_so_far++; + bit_pattern = (bit_pattern << 1) + (bit_value & 1); + + /* Coding scheme: + Codeword RunLength + 0x 1-2 + 10x 3-4 + 110x 5-6 + 1110xx 7-10 + 11110xx 11-14 + 11111xxxx 15-30 + */ + + switch ( bits_so_far ){ + case 2: + /* If bit 1 is clear */ + if ((bit_pattern & 0x0002) == 0){ + BitsLeft = (bit_pattern & 0x0001) + 1; + return 1; + } + break; + + case 3: + /* If bit 1 is clear */ + if ((bit_pattern & 0x0002) == 0){ + BitsLeft = (bit_pattern & 0x0001) + 3; + return 1; + } + break; + + case 4: + /* If bit 1 is clear */ + if ((bit_pattern & 0x0002) == 0){ + BitsLeft = (bit_pattern & 0x0001) + 5; + return 1; + } + break; + + case 6: + /* If bit 2 is clear */ + if ((bit_pattern & 0x0004) == 0){ + BitsLeft = (bit_pattern & 0x0003) + 7; + return 1; + } + break; + + case 7: + /* If bit 2 is clear */ + if ((bit_pattern & 0x0004) == 0){ + BitsLeft = (bit_pattern & 0x0003) + 11; + return 1; + } + break; + + case 9: + BitsLeft = (bit_pattern & 0x000F) + 15; + return 1; + } + + return 0; + } + + private int deCodeSBRun (int bit_value){ + /* Add in the new bit value. */ + bits_so_far++; + bit_pattern = (bit_pattern << 1) + (bit_value & 1); + + /* Coding scheme: + Codeword RunLength + 0 1 + 10x 2-3 + 110x 4-5 + 1110xx 6-9 + 11110xxx 10-17 + 111110xxxx 18-33 + 111111xxxxxxxxxxxx 34-4129 + */ + + switch ( bits_so_far ){ + case 1: + if (bit_pattern == 0 ){ + BitsLeft = 1; + return 1; + } + break; + + case 3: + /* Bit 1 clear */ + if ((bit_pattern & 0x0002) == 0){ + BitsLeft = (bit_pattern & 0x0001) + 2; + return 1; + } + break; + + case 4: + /* Bit 1 clear */ + if ((bit_pattern & 0x0002) == 0){ + BitsLeft = (bit_pattern & 0x0001) + 4; + return 1; + } + break; + + case 6: + /* Bit 2 clear */ + if ((bit_pattern & 0x0004) == 0){ + BitsLeft = (bit_pattern & 0x0003) + 6; + return 1; + } + break; + + case 8: + /* Bit 3 clear */ + if ((bit_pattern & 0x0008) == 0){ + BitsLeft = (bit_pattern & 0x0007) + 10; + return 1; + } + break; + + case 10: + /* Bit 4 clear */ + if ((bit_pattern & 0x0010) == 0){ + BitsLeft = (bit_pattern & 0x000F) + 18; + return 1; + } + break; + + case 18: + BitsLeft = (bit_pattern & 0x0FFF) + 34; + return 1; + } + return 0; + } + + private void getNextBInit(OggBuffer opb){ + long ret; + + ret = opb.readB(1); + NextBit = (byte)ret; + + /* Read run length */ + init(); + do { + ret = opb.readB(1); + } + while (deCodeBlockRun((int)ret)==0); + } + + private byte getNextBBit (OggBuffer opb){ + long ret; + if (BitsLeft == 0){ + /* Toggle the value. */ + NextBit = (byte) (NextBit ^ 1); + + /* Read next run */ + init(); + do { + ret = opb.readB(1); + } + while (deCodeBlockRun((int)ret)==0); + + } + + /* Have read a bit */ + BitsLeft--; + + /* Return next bit value */ + return NextBit; + } + + private void getNextSbInit(OggBuffer opb){ + long ret; + + ret = opb.readB(1); + NextBit = (byte)ret; + + /* Read run length */ + init(); + do { + ret = opb.readB(1); + } + while (deCodeSBRun((int)ret)==0); + + } + + private byte getNextSbBit (OggBuffer opb){ + long ret; + + if (BitsLeft == 0){ + /* Toggle the value. */ + NextBit = (byte) (NextBit ^ 1); + + /* Read next run */ + init(); + do { + ret = opb.readB(1); + } + while (deCodeSBRun((int)ret)==0); + + } + + /* Have read a bit */ + BitsLeft--; + + /* Return next bit value */ + return NextBit; + } + + private final short[] empty64 = new short[64]; + public void quadDecodeDisplayFragments ( Playback pbi ){ + int SB, MB, B; + int DataToDecode; + + int dfIndex; + int MBIndex = 0; + OggBuffer opb = pbi.opb; + + /* Reset various data structures common to key frames and inter frames. */ + pbi.CodedBlockIndex = 0; + MemUtils.set ( pbi.display_fragments, 0, 0, pbi.UnitFragments ); + + /* For "Key frames" mark all blocks as coded and return. */ + /* Else initialise the ArrayPtr array to 0 (all blocks uncoded by default) */ + if ( pbi.getFrameType() == Constants.BASE_FRAME ) { + MemUtils.set( pbi.SBFullyFlags, 0, 1, pbi.SuperBlocks ); + MemUtils.set( pbi.SBCodedFlags, 0, 1, pbi.SuperBlocks ); + MemUtils.set( pbi.MBCodedFlags, 0, 0, pbi.MacroBlocks ); + }else{ + MemUtils.set( pbi.SBFullyFlags, 0, 0, pbi.SuperBlocks ); + MemUtils.set( pbi.MBCodedFlags, 0, 0, pbi.MacroBlocks ); + + /* Un-pack the list of partially coded Super-Blocks */ + getNextSbInit(opb); + for( SB = 0; SB < pbi.SuperBlocks; SB++){ + pbi.SBCodedFlags[SB] = getNextSbBit (opb); + } + + /* Scan through the list of super blocks. Unless all are marked + as partially coded we have more to do. */ + DataToDecode = 0; + for ( SB=0; SB= 0 ){ + /* Only read block level data if SB was fully or partially coded */ + if (pbi.SBCodedFlags[SB] != 0) { + for ( B=0; B<4; B++ ){ + /* If block is valid (in frame)... */ + dfIndex = pbi.BlockMap.quadMapToIndex1(SB, MB, B); + if ( dfIndex >= 0 ){ + if ( pbi.SBFullyFlags[SB] != 0) + pbi.display_fragments[dfIndex] = 1; + else + pbi.display_fragments[dfIndex] = getNextBBit(opb); + + /* Create linear list of coded block indices */ + if ( pbi.display_fragments[dfIndex] != 0) { + pbi.MBCodedFlags[MBIndex] = 1; + pbi.CodedBlockList[pbi.CodedBlockIndex] = dfIndex; + /* Clear down the pbi.QFragData structure for this coded block. */ + System.arraycopy(empty64, 0, pbi.QFragData[dfIndex], 0, 64); + pbi.CodedBlockIndex++; + } + } + } + } + MBIndex++; + + } + } + } + } + + public CodingMode unpackMode(OggBuffer opb){ + /* Coding scheme: + Token Codeword Bits + Entry 0 (most frequent) 0 1 + Entry 1 10 2 + Entry 2 110 3 + Entry 3 1110 4 + Entry 4 11110 5 + Entry 5 111110 6 + Entry 6 1111110 7 + Entry 7 1111111 7 + */ + + /* Initialise the decoding. */ + bits_so_far = 0; + + bit_pattern = (int) opb.readB(1); + + /* Do we have a match */ + if ( bit_pattern == 0 ) + return CodingMode.MODES[0]; + + /* Get the next bit */ + bit_pattern = (bit_pattern << 1) | (int)opb.readB(1); + + /* Do we have a match */ + if ( bit_pattern == 0x0002 ) + return CodingMode.MODES[1]; + + bit_pattern = (bit_pattern << 1) | (int)opb.readB(1); + + /* Do we have a match */ + if ( bit_pattern == 0x0006 ) + return CodingMode.MODES[2]; + + bit_pattern = (bit_pattern << 1) | (int)opb.readB(1); + + /* Do we have a match */ + if ( bit_pattern == 0x000E ) + return CodingMode.MODES[3]; + + bit_pattern = (bit_pattern << 1) | (int)opb.readB(1); + + /* Do we have a match */ + if ( bit_pattern == 0x001E ) + return CodingMode.MODES[4]; + + bit_pattern = (bit_pattern << 1) | (int)opb.readB(1); + + /* Do we have a match */ + if ( bit_pattern == 0x003E ) + return CodingMode.MODES[5]; + + bit_pattern = (bit_pattern << 1) | (int)opb.readB(1); + + /* Do we have a match */ + if ( bit_pattern == 0x007E ) + return CodingMode.MODES[6]; + else + return CodingMode.MODES[7]; + } +} + diff --git a/common/src/main/java/eu/midnightdust/picturesign_theora/ogv/jheora/FrInit.java b/common/src/main/java/eu/midnightdust/picturesign_theora/ogv/jheora/FrInit.java new file mode 100644 index 0000000..01e44db --- /dev/null +++ b/common/src/main/java/eu/midnightdust/picturesign_theora/ogv/jheora/FrInit.java @@ -0,0 +1,288 @@ +/* Jheora + * Copyright (C) 2004 Fluendo S.L. + * + * Written by: 2004 Wim Taymans + * + * Many thanks to + * The Xiph.Org Foundation http://www.xiph.org/ + * Jheora was based on their Theora reference decoder. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public License + * as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +package eu.midnightdust.picturesign_theora.ogv.jheora; + +public class FrInit { + static void InitializeFragCoordinates(Playback pbi) + { + int i, j; + + int HorizFrags = pbi.HFragments; + int VertFrags = pbi.VFragments; + int StartFrag = 0; + + /* Y */ + + for(i = 0; i< VertFrags; i++){ + for(j = 0; j< HorizFrags; j++){ + int ThisFrag = i * HorizFrags + j; + pbi.FragCoordinates[ ThisFrag ] = + new Coordinate(j * Constants.BLOCK_HEIGHT_WIDTH, + i * Constants.BLOCK_HEIGHT_WIDTH); + } + } + + /* U */ + HorizFrags >>= pbi.UVShiftX; + VertFrags >>= pbi.UVShiftY; + StartFrag = pbi.YPlaneFragments; + + for(i = 0; i< VertFrags; i++) { + for(j = 0; j< HorizFrags; j++) { + int ThisFrag = StartFrag + i * HorizFrags + j; + pbi.FragCoordinates[ ThisFrag ] = + new Coordinate(j * Constants.BLOCK_HEIGHT_WIDTH, + i * Constants.BLOCK_HEIGHT_WIDTH); + } + } + + /* V */ + StartFrag = pbi.YPlaneFragments + pbi.UVPlaneFragments; + for(i = 0; i< VertFrags; i++) { + for(j = 0; j< HorizFrags; j++) { + int ThisFrag = StartFrag + i * HorizFrags + j; + pbi.FragCoordinates[ ThisFrag ] = + new Coordinate(j * Constants.BLOCK_HEIGHT_WIDTH, + i * Constants.BLOCK_HEIGHT_WIDTH); + } + } + } + + static void CalcPixelIndexTable(Playback pbi){ + int i, off; + int[] PixelIndexTablePtr; + + /* Calculate the pixel index table for normal image buffers */ + PixelIndexTablePtr = pbi.pixel_index_table; + for ( i = 0; i < pbi.YPlaneFragments; i++ ) { + PixelIndexTablePtr[ i ] = + ((i / pbi.HFragments) * Constants.VFRAGPIXELS * + pbi.info.width); + PixelIndexTablePtr[ i ] += + ((i % pbi.HFragments) * Constants.HFRAGPIXELS); + } + + off = pbi.YPlaneFragments; + for ( i = 0; i < ((pbi.HFragments >> pbi.UVShiftX) * pbi.VFragments); i++ ) { + PixelIndexTablePtr[ i + off] = + ((i / (pbi.HFragments >> pbi.UVShiftX) ) * + (Constants.VFRAGPIXELS * + (pbi.info.width >> pbi.UVShiftX)) ); + PixelIndexTablePtr[ i + off] += + ((i % (pbi.HFragments >> pbi.UVShiftX) ) * + Constants.HFRAGPIXELS) + pbi.YPlaneSize; + } + + /************************************************************************/ + /* Now calculate the pixel index table for image reconstruction buffers */ + PixelIndexTablePtr = pbi.recon_pixel_index_table; + for ( i = 0; i < pbi.YPlaneFragments; i++ ){ + PixelIndexTablePtr[ i ] = + ((i / pbi.HFragments) * Constants.VFRAGPIXELS * + pbi.YStride); + PixelIndexTablePtr[ i ] += + ((i % pbi.HFragments) * Constants.HFRAGPIXELS) + + pbi.ReconYDataOffset; + } + + /* U blocks */ + off = pbi.YPlaneFragments; + for ( i = 0; i < pbi.UVPlaneFragments; i++ ) { + PixelIndexTablePtr[i+off] = + ((i / (pbi.HFragments >> pbi.UVShiftX) ) * + (Constants.VFRAGPIXELS * (pbi.UVStride)) ); + PixelIndexTablePtr[i+off] += + ((i % (pbi.HFragments >> pbi.UVShiftX) ) * + Constants.HFRAGPIXELS) + pbi.ReconUDataOffset; + } + + /* V blocks */ + off = pbi.YPlaneFragments + pbi.UVPlaneFragments; + for ( i = 0; i < pbi.UVPlaneFragments; i++ ) { + PixelIndexTablePtr[ i +off] = + ((i / (pbi.HFragments >> pbi.UVShiftX) ) * + (Constants.VFRAGPIXELS * (pbi.UVStride)) ); + PixelIndexTablePtr[ i +off] += + ((i % (pbi.HFragments >> pbi.UVShiftX) ) * Constants.HFRAGPIXELS) + + pbi.ReconVDataOffset; + } + } + + static void ClearFragmentInfo(Playback pbi){ + + /* free prior allocs if present */ + pbi.display_fragments = null; + pbi.pixel_index_table = null; + pbi.recon_pixel_index_table = null; + pbi.FragTokenCounts = null; + pbi.CodedBlockList = null; + pbi.FragMVect = null; + pbi.FragCoefEOB = null; + pbi.QFragData = null; + pbi.FragCodingMethod = null; + pbi.FragCoordinates = null; + + pbi.FragQIndex = null; + + pbi.BlockMap = null; + + pbi.SBCodedFlags = null; + pbi.SBFullyFlags = null; + pbi.MBFullyFlags = null; + pbi.MBCodedFlags = null; + } + + static void InitFragmentInfo(Playback pbi){ + /* clear any existing info */ + ClearFragmentInfo(pbi); + + /* Perform Fragment Allocations */ + pbi.display_fragments = new byte[pbi.UnitFragments]; + pbi.pixel_index_table = new int[pbi.UnitFragments]; + pbi.recon_pixel_index_table = new int[pbi.UnitFragments]; + pbi.FragTokenCounts = new int[pbi.UnitFragments]; + pbi.CodedBlockList = new int[pbi.UnitFragments]; + pbi.FragMVect = new MotionVector[pbi.UnitFragments]; + for (int i=0; i> pbi.UVShiftX; + pbi.ReconYPlaneSize = pbi.YStride * + (pbi.info.height + Constants.STRIDE_EXTRA); + pbi.ReconUVPlaneSize = pbi.ReconYPlaneSize / uv_fact; + FrameSize = pbi.ReconYPlaneSize + 2 * pbi.ReconUVPlaneSize; + + pbi.YDataOffset = 0; + pbi.UDataOffset = pbi.YPlaneSize; + pbi.VDataOffset = pbi.YPlaneSize + pbi.UVPlaneSize; + pbi.ReconYDataOffset = + (pbi.YStride * Constants.UMV_BORDER) + Constants.UMV_BORDER; + pbi.ReconUDataOffset = pbi.ReconYPlaneSize + + (pbi.UVStride * (Constants.UMV_BORDER>>pbi.UVShiftY)) + + (Constants.UMV_BORDER>>pbi.UVShiftX); + pbi.ReconVDataOffset = pbi.ReconYPlaneSize + pbi.ReconUVPlaneSize + + (pbi.UVStride * (Constants.UMV_BORDER>>pbi.UVShiftY)) + + (Constants.UMV_BORDER>>pbi.UVShiftX); + + /* Image dimensions in Super-Blocks */ + pbi.YSBRows = (pbi.info.height/32) + + ( pbi.info.height%32 !=0 ? 1 : 0 ); + pbi.YSBCols = (pbi.info.width/32) + + ( pbi.info.width%32 !=0 ? 1 : 0 ); + pbi.UVSBRows = ((pbi.info.height>>pbi.UVShiftY)/32) + + ( (pbi.info.height>>pbi.UVShiftY)%32 !=0 ? 1 : 0 ); + pbi.UVSBCols = ((pbi.info.width>>pbi.UVShiftX)/32) + + ( (pbi.info.width>>pbi.UVShiftX)%32 !=0 ? 1 : 0 ); + + /* Super-Blocks per component */ + pbi.YSuperBlocks = pbi.YSBRows * pbi.YSBCols; + pbi.UVSuperBlocks = pbi.UVSBRows * pbi.UVSBCols; + pbi.SuperBlocks = pbi.YSuperBlocks+2*pbi.UVSuperBlocks; + + /* Useful externals */ + pbi.YMacroBlocks = ((pbi.VFragments+1)/2)*((pbi.HFragments+1)/2); + pbi.UVMacroBlocks = + (((pbi.VFragments>>pbi.UVShiftY)+1)/2)*(((pbi.HFragments>>pbi.UVShiftX)+1)/2); + pbi.MacroBlocks = pbi.YMacroBlocks+2*pbi.UVMacroBlocks; + + InitFragmentInfo(pbi); + InitFrameInfo(pbi, FrameSize); + InitializeFragCoordinates(pbi); + + /* Configure mapping between quad-tree and fragments */ + pbi.BlockMap = new BlockMapping (pbi.YSuperBlocks, + pbi.UVSuperBlocks, pbi.HFragments, pbi.VFragments, + pbi.UVShiftX, pbi.UVShiftY); + + /* Re-initialise the pixel index table. */ + + CalcPixelIndexTable( pbi ); + } +} diff --git a/common/src/main/java/eu/midnightdust/picturesign_theora/ogv/jheora/HuffEntry.java b/common/src/main/java/eu/midnightdust/picturesign_theora/ogv/jheora/HuffEntry.java new file mode 100644 index 0000000..cb35bb8 --- /dev/null +++ b/common/src/main/java/eu/midnightdust/picturesign_theora/ogv/jheora/HuffEntry.java @@ -0,0 +1,83 @@ +/* Jheora + * Copyright (C) 2004 Fluendo S.L. + * + * Written by: 2004 Wim Taymans + * + * Many thanks to + * The Xiph.Org Foundation http://www.xiph.org/ + * Jheora was based on their Theora reference decoder. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public License + * as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +package eu.midnightdust.picturesign_theora.ogv.jheora; + +import eu.midnightdust.picturesign_theora.ogv.jogg.*; + +public class HuffEntry +{ + HuffEntry[] Child = new HuffEntry[2]; + HuffEntry previous; + HuffEntry next; + int value; + int frequency; + + public HuffEntry copy() + { + HuffEntry huffDst; + huffDst = new HuffEntry(); + huffDst.value = value; + if (value < 0) { + huffDst.Child[0] = Child[0].copy(); + huffDst.Child[1] = Child[1].copy(); + } + return huffDst; + } + + public int read(int depth, OggBuffer opb) + { + int bit; + int ret; + + bit = opb.readB(1); + if(bit < 0) { + return Result.BADHEADER; + } + else if(bit == 0) { + if (++depth > 32) + return Result.BADHEADER; + + Child[0] = new HuffEntry(); + ret = Child[0].read(depth, opb); + if (ret < 0) + return ret; + + Child[1] = new HuffEntry(); + ret = Child[1].read(depth, opb); + if (ret < 0) + return ret; + + value = -1; + } + else { + Child[0] = null; + Child[1] = null; + value = opb.readB(5); + if (value < 0) + return Result.BADHEADER; + } + return 0; + } +} diff --git a/common/src/main/java/eu/midnightdust/picturesign_theora/ogv/jheora/HuffTables.java b/common/src/main/java/eu/midnightdust/picturesign_theora/ogv/jheora/HuffTables.java new file mode 100644 index 0000000..744f2b8 --- /dev/null +++ b/common/src/main/java/eu/midnightdust/picturesign_theora/ogv/jheora/HuffTables.java @@ -0,0 +1,538 @@ +/* Jheora + * Copyright (C) 2004 Fluendo S.L. + * + * Written by: 2004 Wim Taymans + * + * Many thanks to + * The Xiph.Org Foundation http://www.xiph.org/ + * Jheora was based on their Theora reference decoder. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public License + * as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +package eu.midnightdust.picturesign_theora.ogv.jheora; + +public class HuffTables +{ + static final byte[] ExtraBitLengths_VP31 = { + 0, 0, 0, 2, 3, 4, 12,3, 6, /* EOB and Zero-run tokens. */ + 0, 0, 0, 0, /* Very low value tokens. */ + 1, 1, 1, 1, 2, 3, 4, 5, 6, 10, /* Other value tokens */ + 1, 1, 1, 1, 1, 3, 4, /* Category 1 runs. */ + 2, 3, /* Category 2 runs. */ + }; + + + static final short[][] FrequencyCounts_VP3 = { + /* DC Intra bias */ + { 198, 62, 22, 31, 14, 6, 6, 205, 3, + 843, 843, 415, 516, + 660, 509, 412, 347, 560, 779, 941, 930, 661, 377, + 170, 155, 39, 2, 9, 15, 11, + 128, 86, + }, + { 299, 92, 34, 39, 15, 6, 6, 132, 1, + 851, 851, 484, 485, + 666, 514, 416, 351, 567, 788, 953, 943, 670, 383, + 117, 119, 26, 4, 17, 7, 1, + 93, 56, + }, + { 367, 115, 42, 47, 16, 6, 6, 105, 1, + 896, 896, 492, 493, + 667, 510, 408, 342, 547, 760, 932, 927, 656, 379, + 114, 103, 10, 3, 6, 2, 1, + 88, 49, + }, + { 462, 158, 63, 76, 28, 9, 8, 145, 1, + 1140, 1140, 573, 574, + 754, 562, 435, 357, 555, 742, 793, 588, 274, 81, + 154, 117, 13, 6, 12, 2, 1, + 104, 62, + }, + { 558, 196, 81, 99, 36, 11, 9, 135, 1, + 1300, 1301, 606, 607, + 779, 560, 429, 349, 536, 680, 644, 405, 153, 30, + 171, 120, 12, 5, 14, 3, 1, + 104, 53, + }, + { 635, 233, 100, 122, 46, 14, 12, 113, 1, + 1414, 1415, 631, 631, + 785, 555, 432, 335, 513, 611, 521, 284, 89, 13, + 170, 113, 10, 5, 14, 3, 1, + 102, 62, + }, + { 720, 276, 119, 154, 62, 20, 16, 101, 1, + 1583, 1583, 661, 661, + 794, 556, 407, 318, 447, 472, 343, 153, 35, 1, + 172, 115, 11, 7, 14, 3, 1, + 112, 70, + }, + { 853, 326, 144, 184, 80, 27, 19, 52, 1, + 1739, 1740, 684, 685, + 800, 540, 381, 277, 364, 352, 218, 78, 13, 1, + 139, 109, 9, 6, 20, 2, 1, + 94, 50, + }, + + /* DC Inter Bias */ + { 490, 154, 57, 53, 10, 2, 1, 238, 160, + 1391, 1390, 579, 578, + 491, 273, 172, 118, 152, 156, 127, 79, 41, 39, + 712, 547, 316, 125, 183, 306, 237, + 451, 358, + }, + { 566, 184, 70, 65, 11, 2, 1, 235, 51, + 1414, 1414, 599, 598, + 510, 285, 180, 124, 157, 161, 131, 82, 42, 40, + 738, 551, 322, 138, 195, 188, 93, + 473, 365, + }, + { 711, 261, 111, 126, 27, 4, 1, 137, 52, + 1506, 1505, 645, 645, + 567, 316, 199, 136, 172, 175, 142, 88, 45, 48, + 548, 449, 255, 145, 184, 174, 121, + 260, 227, + }, + { 823, 319, 144, 175, 43, 7, 1, 53, 42, + 1648, 1648, 653, 652, + 583, 329, 205, 139, 175, 176, 139, 84, 44, 34, + 467, 389, 211, 137, 181, 186, 107, + 106, 85, + }, + { 948, 411, 201, 276, 85, 16, 2, 39, 33, + 1778, 1777, 584, 583, + 489, 265, 162, 111, 140, 140, 108, 64, 38, 23, + 428, 356, 201, 139, 186, 165, 94, + 78, 63, + }, + { 1002, 470, 248, 386, 153, 39, 6, 23, 23, + 1866, 1866, 573, 573, + 467, 249, 155, 103, 130, 128, 94, 60, 38, 14, + 323, 263, 159, 111, 156, 153, 74, + 46, 34, + }, + { 1020, 518, 291, 504, 242, 78, 18, 14, 14, + 1980, 1979, 527, 526, + 408, 219, 132, 87, 110, 104, 79, 55, 31, 7, + 265, 213, 129, 91, 131, 111, 50, + 31, 20, + }, + { 1018, 544, 320, 591, 338, 139, 47, 5, 2, + 2123, 2123, 548, 547, + 414, 212, 126, 83, 101, 96, 79, 60, 23, 1, + 120, 97, 55, 39, 60, 38, 15, + 11, 8, + }, + + /* AC INTRA Tables */ + /* AC Intra bias group 1 tables */ + { 242, 62, 22, 20, 4, 1, 1, 438, 1, + 593, 593, 489, 490, + 657, 580, 471, 374, 599, 783, 869, 770, 491, 279, + 358, 144, 82, 54, 49, 70, 5, + 289, 107, + }, + { 317, 95, 38, 41, 8, 1, 1, 479, 1, + 653, 654, 500, 501, + 682, 611, 473, 376, 582, 762, 806, 656, 358, 155, + 419, 162, 86, 58, 36, 34, 1, + 315, 126, + }, + { 382, 121, 49, 59, 15, 3, 1, 496, 1, + 674, 674, 553, 554, + 755, 636, 487, 391, 576, 718, 701, 488, 221, 72, + 448, 161, 107, 56, 37, 29, 1, + 362, 156, + }, + { 415, 138, 57, 73, 21, 5, 1, 528, 1, + 742, 741, 562, 563, + 753, 669, 492, 388, 563, 664, 589, 340, 129, 26, + 496, 184, 139, 71, 48, 33, 2, + 387, 166, + }, + { 496, 170, 73, 94, 31, 8, 2, 513, 1, + 855, 855, 604, 604, + 769, 662, 477, 356, 486, 526, 381, 183, 51, 5, + 590, 214, 160, 85, 60, 39, 3, + 427, 203, + }, + { 589, 207, 89, 116, 40, 13, 3, 491, 1, + 919, 919, 631, 631, + 769, 633, 432, 308, 408, 378, 247, 94, 17, 1, + 659, 247, 201, 105, 73, 51, 3, + 466, 242, + }, + { 727, 266, 115, 151, 49, 17, 6, 439, 1, + 977, 977, 642, 642, + 718, 572, 379, 243, 285, 251, 133, 40, 1, 1, + 756, 287, 253, 126, 94, 66, 4, + 492, 280, + }, + { 940, 392, 180, 247, 82, 30, 14, 343, 1, + 1064, 1064, 615, 616, + 596, 414, 235, 146, 149, 108, 41, 1, 1, 1, + 882, 314, 346, 172, 125, 83, 6, + 489, 291, + }, + /* AC Inter bias group 1 tables */ + { 440, 102, 33, 23, 2, 1, 1, 465, 85, + 852, 852, 744, 743, + 701, 496, 297, 193, 225, 200, 129, 58, 18, 2, + 798, 450, 269, 202, 145, 308, 154, + 646, 389, + }, + { 592, 151, 53, 43, 6, 1, 1, 409, 34, + 875, 875, 748, 747, + 723, 510, 305, 196, 229, 201, 130, 59, 18, 2, + 800, 436, 253, 185, 115, 194, 88, + 642, 368, + }, + { 759, 222, 86, 85, 17, 2, 1, 376, 46, + 888, 888, 689, 688, + 578, 408, 228, 143, 165, 141, 84, 35, 7, 1, + 878, 488, 321, 244, 147, 266, 124, + 612, 367, + }, + { 912, 298, 122, 133, 34, 7, 1, 261, 44, + 1092, 1091, 496, 496, + 409, 269, 150, 95, 106, 87, 49, 16, 1, 1, + 1102, 602, 428, 335, 193, 323, 157, + 423, 253, + }, + { 1072, 400, 180, 210, 60, 16, 3, 210, 40, + 1063, 1063, 451, 451, + 345, 221, 121, 73, 79, 64, 31, 6, 1, 1, + 1105, 608, 462, 358, 202, 330, 155, + 377, 228, + }, + { 1164, 503, 254, 330, 109, 34, 9, 167, 35, + 1038, 1037, 390, 390, + 278, 170, 89, 54, 56, 40, 13, 1, 1, 1, + 1110, 607, 492, 401, 218, 343, 141, + 323, 192, + }, + { 1173, 583, 321, 486, 196, 68, 23, 124, 23, + 1037, 1037, 347, 346, + 232, 139, 69, 40, 37, 20, 2, 1, 1, 1, + 1128, 584, 506, 410, 199, 301, 113, + 283, 159, + }, + { 1023, 591, 366, 699, 441, 228, 113, 79, 5, + 1056, 1056, 291, 291, + 173, 96, 38, 19, 8, 1, 1, 1, 1, 1, + 1187, 527, 498, 409, 147, 210, 56, + 263, 117, + }, + + /* AC Intra bias group 2 tables */ + { 311, 74, 27, 27, 5, 1, 1, 470, 24, + 665, 667, 637, 638, + 806, 687, 524, 402, 585, 679, 609, 364, 127, 20, + 448, 210, 131, 76, 52, 111, 19, + 393, 195, + }, + { 416, 104, 39, 38, 8, 1, 1, 545, 33, + 730, 731, 692, 692, + 866, 705, 501, 365, 495, 512, 387, 168, 39, 2, + 517, 240, 154, 86, 64, 127, 19, + 461, 247, + }, + { 474, 117, 43, 42, 9, 1, 1, 560, 40, + 783, 783, 759, 760, + 883, 698, 466, 318, 404, 377, 215, 66, 7, 1, + 559, 259, 176, 110, 87, 170, 22, + 520, 278, + }, + { 582, 149, 53, 53, 12, 2, 1, 473, 39, + 992, 993, 712, 713, + 792, 593, 373, 257, 299, 237, 114, 25, 1, 1, + 710, 329, 221, 143, 116, 226, 26, + 490, 259, + }, + { 744, 210, 78, 77, 16, 2, 1, 417, 37, + 1034, 1035, 728, 728, + 718, 509, 296, 175, 184, 122, 42, 3, 1, 1, + 791, 363, 255, 168, 145, 311, 35, + 492, 272, + }, + { 913, 291, 121, 128, 28, 4, 1, 334, 40, + 1083, 1084, 711, 712, + 624, 378, 191, 107, 95, 50, 7, 1, 1, 1, + 876, 414, 288, 180, 164, 382, 39, + 469, 275, + }, + { 1065, 405, 184, 216, 53, 8, 1, 236, 36, + 1134, 1134, 685, 686, + 465, 253, 113, 48, 41, 9, 1, 1, 1, 1, + 965, 451, 309, 179, 166, 429, 53, + 414, 249, + }, + { 1148, 548, 301, 438, 160, 42, 6, 84, 17, + 1222, 1223, 574, 575, + 272, 111, 23, 6, 2, 1, 1, 1, 1, 1, + 1060, 502, 328, 159, 144, 501, 54, + 302, 183, + }, + /* AC Inter bias group 2 tables */ + { 403, 80, 24, 17, 1, 1, 1, 480, 90, + 899, 899, 820, 819, + 667, 413, 228, 133, 139, 98, 42, 10, 1, 1, + 865, 470, 316, 222, 171, 419, 213, + 645, 400, + }, + { 698, 169, 59, 49, 6, 1, 1, 414, 101, + 894, 893, 761, 761, + 561, 338, 171, 96, 97, 64, 26, 6, 1, 1, + 896, 494, 343, 239, 192, 493, 215, + 583, 366, + }, + { 914, 255, 94, 80, 10, 1, 1, 345, 128, + 935, 935, 670, 671, + 415, 222, 105, 55, 51, 30, 10, 1, 1, 1, + 954, 530, 377, 274, 232, 641, 295, + 456, 298, + }, + { 1103, 359, 146, 135, 20, 1, 1, 235, 119, + 1042, 1042, 508, 507, + 293, 146, 65, 33, 30, 16, 4, 1, 1, 1, + 1031, 561, 407, 296, 265, 813, 317, + 301, 192, + }, + { 1255, 504, 238, 265, 51, 5, 1, 185, 113, + 1013, 1013, 437, 438, + 212, 92, 41, 18, 15, 6, 1, 1, 1, 1, + 976, 530, 386, 276, 260, 927, 357, + 224, 148, + }, + { 1292, 610, 332, 460, 127, 16, 1, 136, 99, + 1014, 1015, 384, 384, + 153, 65, 25, 11, 6, 1, 1, 1, 1, 1, + 942, 487, 343, 241, 238, 970, 358, + 174, 103, + }, + { 1219, 655, 407, 700, 280, 55, 2, 100, 60, + 1029, 1029, 337, 336, + 119, 43, 11, 3, 2, 1, 1, 1, 1, 1, + 894, 448, 305, 199, 213, 1005, 320, + 136, 77, + }, + { 1099, 675, 435, 971, 581, 168, 12, 37, 16, + 1181, 1081, 319, 318, + 66, 11, 6, 1, 1, 1, 1, 1, 1, 1, + 914, 370, 235, 138, 145, 949, 128, + 94, 41, + }, + + /* AC Intra bias group 3 tables */ + { 486, 112, 39, 34, 6, 1, 1, 541, 67, + 819, 818, 762, 763, + 813, 643, 403, 280, 332, 295, 164, 53, 6, 1, + 632, 294, 180, 131, 105, 208, 109, + 594, 295, + }, + { 723, 191, 69, 65, 12, 1, 1, 445, 79, + 865, 865, 816, 816, + 750, 515, 290, 172, 184, 122, 46, 5, 1, 1, + 740, 340, 213, 165, 129, 270, 168, + 603, 326, + }, + { 884, 264, 102, 103, 21, 3, 1, 382, 68, + 897, 897, 836, 836, + 684, 427, 227, 119, 119, 70, 16, 1, 1, 1, + 771, 367, 234, 184, 143, 272, 178, + 555, 326, + }, + { 1028, 347, 153, 161, 36, 8, 1, 251, 44, + 1083, 1084, 735, 735, + 541, 289, 144, 77, 57, 23, 3, 1, 1, 1, + 926, 422, 270, 215, 176, 301, 183, + 443, 248, + }, + { 1155, 465, 224, 264, 71, 14, 3, 174, 27, + 1110, 1111, 730, 731, + 429, 206, 79, 30, 19, 4, 1, 1, 1, 1, + 929, 443, 279, 225, 194, 298, 196, + 354, 223, + }, + { 1191, 576, 296, 415, 144, 36, 8, 114, 16, + 1162, 1162, 749, 749, + 338, 108, 29, 8, 5, 1, 1, 1, 1, 1, + 947, 458, 273, 207, 194, 248, 145, + 258, 152, + }, + { 1169, 619, 366, 603, 247, 92, 23, 46, 1, + 1236, 1236, 774, 775, + 191, 35, 14, 1, 1, 1, 1, 1, 1, 1, + 913, 449, 260, 214, 194, 180, 82, + 174, 98, + }, + { 1006, 537, 381, 897, 504, 266, 101, 39, 1, + 1307, 1307, 668, 667, + 116, 3, 1, 1, 1, 1, 1, 1, 1, 1, + 1175, 261, 295, 70, 164, 107, 31, + 10, 76, + }, + /* AC Inter bias group 3 tables */ + { 652, 156, 53, 43, 5, 1, 1, 368, 128, + 983, 984, 825, 825, + 583, 331, 163, 88, 84, 48, 15, 1, 1, 1, + 870, 480, 316, 228, 179, 421, 244, + 562, 349, + }, + { 988, 280, 104, 87, 12, 1, 1, 282, 194, + 980, 981, 738, 739, + 395, 189, 80, 37, 31, 12, 2, 1, 1, 1, + 862, 489, 333, 262, 214, 600, 446, + 390, 260, + }, + { 1176, 399, 165, 154, 24, 2, 1, 218, 224, + 1017, 1018, 651, 651, + 280, 111, 42, 16, 9, 3, 1, 1, 1, 1, + 787, 469, 324, 269, 229, 686, 603, + 267, 194, + }, + { 1319, 530, 255, 268, 47, 4, 1, 113, 183, + 1149, 1150, 461, 461, + 173, 58, 17, 5, 3, 1, 1, 1, 1, 1, + 768, 450, 305, 261, 221, 716, 835, + 136, 97, + }, + { 1362, 669, 355, 465, 104, 9, 1, 76, 153, + 1253, 1253, 398, 397, + 102, 21, 5, 1, 1, 1, 1, 1, 1, 1, + 596, 371, 238, 228, 196, 660, 954, + 68, 53, + }, + { 1354, 741, 446, 702, 174, 15, 1, 38, 87, + 1498, 1498, 294, 294, + 43, 7, 1, 1, 1, 1, 1, 1, 1, 1, + 381, 283, 165, 181, 155, 544, 1039, + 25, 21, + }, + { 1262, 885, 546, 947, 263, 18, 1, 18, 27, + 1908, 1908, 163, 162, + 14, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 195, 152, 83, 125, 109, 361, 827, + 7, 5, + }, + { 2539, 951, 369, 554, 212, 18, 1, 1, 1, + 2290, 2289, 64, 64, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 18, 18, 9, 55, 36, 184, 323, + 1, 1, + }, + + + /* AC Intra bias group 4 tables */ + { 921, 264, 101, 100, 19, 2, 1, 331, 98, + 1015, 1016, 799, 799, + 512, 269, 119, 60, 50, 17, 1, 1, 1, 1, + 841, 442, 307, 222, 182, 493, 256, + 438, 310, + }, + { 1147, 412, 184, 206, 50, 6, 1, 242, 141, + 977, 976, 808, 807, + 377, 135, 40, 10, 7, 1, 1, 1, 1, 1, + 788, 402, 308, 223, 205, 584, 406, + 316, 227, + }, + { 1243, 504, 238, 310, 79, 11, 1, 184, 150, + 983, 984, 814, 813, + 285, 56, 10, 1, 1, 1, 1, 1, 1, 1, + 713, 377, 287, 217, 180, 615, 558, + 208, 164, + }, + { 1266, 606, 329, 484, 161, 27, 1, 79, 92, + 1187, 1188, 589, 588, + 103, 10, 1, 1, 1, 1, 1, 1, 1, 1, + 680, 371, 278, 221, 244, 614, 728, + 80, 62, + }, + { 1126, 828, 435, 705, 443, 90, 8, 10, 55, + 1220, 1219, 350, 350, + 28, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 602, 330, 222, 168, 158, 612, 919, + 104, 5, + }, + { 1210, 506, 1014, 926, 474, 240, 4, 1, 44, + 1801, 1801, 171, 171, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 900, 132, 36, 11, 47, 191, 316, + 2, 1, + }, + { 1210, 506, 1014, 926, 474, 240, 4, 1, 44, + 1801, 1801, 171, 171, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 900, 132, 36, 11, 47, 191, 316, + 2, 1, + }, + { 1210, 506, 1014, 926, 474, 240, 4, 1, 44, + 1801, 1801, 171, 171, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 900, 132, 36, 11, 47, 191, 316, + 2, 1, + }, + /* AC Inter bias group 4 tables */ + { 1064, 325, 129, 117, 20, 2, 1, 266, 121, + 1000, 1000, 706, 706, + 348, 162, 67, 32, 25, 11, 1, 1, 1, 1, + 876, 513, 363, 274, 225, 627, 384, + 370, 251, + }, + { 1311, 517, 238, 254, 45, 3, 1, 188, 160, + 1070, 1070, 635, 635, + 239, 85, 30, 11, 6, 1, 1, 1, 1, 1, + 744, 420, 313, 239, 206, 649, 541, + 221, 155, + }, + { 1394, 632, 322, 385, 78, 7, 1, 134, 152, + 1163, 1164, 607, 607, + 185, 51, 12, 3, 1, 1, 1, 1, 1, 1, + 631, 331, 275, 203, 182, 604, 620, + 146, 98, + }, + { 1410, 727, 407, 546, 146, 19, 1, 67, 88, + 1485, 1486, 419, 418, + 103, 18, 3, 1, 1, 1, 1, 1, 1, 1, + 555, 261, 234, 164, 148, 522, 654, + 67, 39, + }, + { 1423, 822, 492, 719, 216, 22, 1, 28, 59, + 1793, 1793, 323, 324, + 37, 2, 1, 1, 1, 1, 1, 1, 1, 1, + 376, 138, 158, 102, 119, 400, 604, + 28, 9, + }, + { 1585, 923, 563, 918, 207, 25, 1, 5, 20, + 2229, 2230, 172, 172, + 7, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 191, 40, 56, 22, 65, 243, 312, + 2, 1, + }, + { 2225, 1100, 408, 608, 133, 8, 1, 1, 1, + 2658, 2658, 25, 24, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 8, 1, 1, 1, 1, 125, 16, + 1, 1, + }, + { 2539, 951, 369, 554, 212, 18, 1, 1, 1, + 2290, 2289, 64, 64, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 18, 18, 9, 55, 36, 184, 323, + 1, 1, + }, + }; +} + diff --git a/common/src/main/java/eu/midnightdust/picturesign_theora/ogv/jheora/Huffman.java b/common/src/main/java/eu/midnightdust/picturesign_theora/ogv/jheora/Huffman.java new file mode 100644 index 0000000..8b47c5d --- /dev/null +++ b/common/src/main/java/eu/midnightdust/picturesign_theora/ogv/jheora/Huffman.java @@ -0,0 +1,260 @@ +/* Jheora + * Copyright (C) 2004 Fluendo S.L. + * + * Written by: 2004 Wim Taymans + * + * Many thanks to + * The Xiph.Org Foundation http://www.xiph.org/ + * Jheora was based on their Theora reference decoder. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public License + * as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +package eu.midnightdust.picturesign_theora.ogv.jheora; + +import eu.midnightdust.picturesign_theora.ogv.jogg.*; + +public class Huffman +{ + public static final int NUM_HUFF_TABLES = 80; + public static final int DC_HUFF_OFFSET = 0; + public static final int AC_HUFF_OFFSET = 16; + public static final int AC_TABLE_2_THRESH = 5; + public static final int AC_TABLE_3_THRESH = 14; + public static final int AC_TABLE_4_THRESH = 27; + + public static final int DC_HUFF_CHOICES = 16; + public static final int DC_HUFF_CHOICE_BITS = 4; + + public static final int AC_HUFF_CHOICES = 16; + public static final int AC_HUFF_CHOICE_BITS = 4; + +/* Constants assosciated with entropy tokenisation. */ + public static final int MAX_SINGLE_TOKEN_VALUE = 6; + public static final int DCT_VAL_CAT2_MIN = 3; + public static final int DCT_VAL_CAT3_MIN = 7; + public static final int DCT_VAL_CAT4_MIN = 9; + public static final int DCT_VAL_CAT5_MIN = 13; + public static final int DCT_VAL_CAT6_MIN = 21; + public static final int DCT_VAL_CAT7_MIN = 37; + public static final int DCT_VAL_CAT8_MIN = 69; + + public static final int DCT_EOB_TOKEN = 0; + public static final int DCT_EOB_PAIR_TOKEN = 1; + public static final int DCT_EOB_TRIPLE_TOKEN = 2; + public static final int DCT_REPEAT_RUN_TOKEN = 3; + public static final int DCT_REPEAT_RUN2_TOKEN = 4; + public static final int DCT_REPEAT_RUN3_TOKEN = 5; + public static final int DCT_REPEAT_RUN4_TOKEN = 6; + + public static final int DCT_SHORT_ZRL_TOKEN = 7; + public static final int DCT_ZRL_TOKEN = 8; + + public static final int ONE_TOKEN = 9; /* Special tokens for -1,1,-2,2 */ + public static final int MINUS_ONE_TOKEN = 10; + public static final int TWO_TOKEN = 11; + public static final int MINUS_TWO_TOKEN = 12; + + public static final int LOW_VAL_TOKENS = (MINUS_TWO_TOKEN + 1); + public static final int DCT_VAL_CATEGORY3 = (LOW_VAL_TOKENS + 4); + public static final int DCT_VAL_CATEGORY4 = (DCT_VAL_CATEGORY3 + 1); + public static final int DCT_VAL_CATEGORY5 = (DCT_VAL_CATEGORY4 + 1); + public static final int DCT_VAL_CATEGORY6 = (DCT_VAL_CATEGORY5 + 1); + public static final int DCT_VAL_CATEGORY7 = (DCT_VAL_CATEGORY6 + 1); + public static final int DCT_VAL_CATEGORY8 = (DCT_VAL_CATEGORY7 + 1); + + public static final int DCT_RUN_CATEGORY1 = (DCT_VAL_CATEGORY8 + 1); + public static final int DCT_RUN_CATEGORY1B = (DCT_RUN_CATEGORY1 + 5); + public static final int DCT_RUN_CATEGORY1C = (DCT_RUN_CATEGORY1B + 1); + public static final int DCT_RUN_CATEGORY2 = (DCT_RUN_CATEGORY1C + 1); + +/* 32 */ + public static final int MAX_ENTROPY_TOKENS = (DCT_RUN_CATEGORY2 + 2); + + private static void createHuffmanList (HuffEntry[] huffRoot, + int hIndex, short[] freqList) { + HuffEntry entry_ptr; + HuffEntry search_ptr; + + /* Create a HUFF entry for token zero. */ + huffRoot[hIndex] = new HuffEntry(); + huffRoot[hIndex].previous = null; + huffRoot[hIndex].next = null; + huffRoot[hIndex].Child[0] = null; + huffRoot[hIndex].Child[1] = null; + huffRoot[hIndex].value = 0; + huffRoot[hIndex].frequency = freqList[0]; + + if ( huffRoot[hIndex].frequency == 0 ) + huffRoot[hIndex].frequency = 1; + + /* Now add entries for all the other possible tokens. */ + for (int i = 1; i < Huffman.MAX_ENTROPY_TOKENS; i++) { + entry_ptr = new HuffEntry(); + entry_ptr.value = i; + entry_ptr.frequency = freqList[i]; + entry_ptr.Child[0] = null; + entry_ptr.Child[1] = null; + + /* Force min value of 1. This prevents the tree getting too deep. */ + if ( entry_ptr.frequency == 0 ) + entry_ptr.frequency = 1; + + if ( entry_ptr.frequency <= huffRoot[hIndex].frequency ){ + entry_ptr.next = huffRoot[hIndex]; + huffRoot[hIndex].previous = entry_ptr; + entry_ptr.previous = null; + huffRoot[hIndex] = entry_ptr; + } + else + { + search_ptr = huffRoot[hIndex]; + while ( (search_ptr.next != null) && + (search_ptr.frequency < entry_ptr.frequency) ){ + search_ptr = search_ptr.next; + } + + if ( search_ptr.frequency < entry_ptr.frequency ){ + entry_ptr.next = null; + entry_ptr.previous = search_ptr; + search_ptr.next = entry_ptr; + } + else + { + entry_ptr.next = search_ptr; + entry_ptr.previous = search_ptr.previous; + search_ptr.previous.next = entry_ptr; + search_ptr.previous = entry_ptr; + } + } + } + } + + private static void createCodeArray (HuffEntry huffRoot, + int[] huffCodeArray, + byte[] huffCodeLengthArray, + int codeValue, + byte codeLength) + { + /* If we are at a leaf then fill in a code array entry. */ + if ((huffRoot.Child[0] == null) && (huffRoot.Child[1] == null)) { + huffCodeArray[huffRoot.value] = codeValue; + huffCodeLengthArray[huffRoot.value] = codeLength; + } else { + /* Recursive calls to scan down the tree. */ + codeLength++; + createCodeArray(huffRoot.Child[0], huffCodeArray, huffCodeLengthArray, + ((codeValue << 1) + 0), codeLength); + createCodeArray(huffRoot.Child[1], huffCodeArray, huffCodeLengthArray, + ((codeValue << 1) + 1), codeLength); + } + } + + public static void buildHuffmanTree (HuffEntry[] huffRoot, + int[] huffCodeArray, + byte[] huffCodeLengthArray, + int hIndex, + short[] freqList ) + { + HuffEntry entry_ptr; + HuffEntry search_ptr; + + /* First create a sorted linked list representing the frequencies of + each token. */ + createHuffmanList( huffRoot, hIndex, freqList ); + + /* Now build the tree from the list. */ + + /* While there are at least two items left in the list. */ + while ( huffRoot[hIndex].next != null ){ + /* Create the new node as the parent of the first two in the list. */ + entry_ptr = new HuffEntry(); + entry_ptr.value = -1; + entry_ptr.frequency = huffRoot[hIndex].frequency + + huffRoot[hIndex].next.frequency ; + entry_ptr.Child[0] = huffRoot[hIndex]; + entry_ptr.Child[1] = huffRoot[hIndex].next; + + /* If there are still more items in the list then insert the new + node into the list. */ + if (entry_ptr.Child[1].next != null ){ + /* Set up the provisional 'new root' */ + huffRoot[hIndex] = entry_ptr.Child[1].next; + huffRoot[hIndex].previous = null; + + /* Now scan through the remaining list to insert the new entry + at the appropriate point. */ + if ( entry_ptr.frequency <= huffRoot[hIndex].frequency ){ + entry_ptr.next = huffRoot[hIndex]; + huffRoot[hIndex].previous = entry_ptr; + entry_ptr.previous = null; + huffRoot[hIndex] = entry_ptr; + }else{ + search_ptr = huffRoot[hIndex]; + while ( (search_ptr.next != null) && + (search_ptr.frequency < entry_ptr.frequency) ){ + search_ptr = search_ptr.next; + } + + if ( search_ptr.frequency < entry_ptr.frequency ){ + entry_ptr.next = null; + entry_ptr.previous = search_ptr; + search_ptr.next = entry_ptr; + }else{ + entry_ptr.next = search_ptr; + entry_ptr.previous = search_ptr.previous; + search_ptr.previous.next = entry_ptr; + search_ptr.previous = entry_ptr; + } + } + }else{ + /* Build has finished. */ + entry_ptr.next = null; + entry_ptr.previous = null; + huffRoot[hIndex] = entry_ptr; + } + + /* Delete the next/previous properties of the children (PROB NOT NEC). */ + entry_ptr.Child[0].next = null; + entry_ptr.Child[0].previous = null; + entry_ptr.Child[1].next = null; + entry_ptr.Child[1].previous = null; + + } + + /* Now build a code array from the tree. */ + createCodeArray( huffRoot[hIndex], huffCodeArray, + huffCodeLengthArray, 0, (byte)0); + } + + public static int readHuffmanTrees(HuffEntry[] huffRoot, OggBuffer opb) { + int i; + for (i=0; i + * + * Many thanks to + * The Xiph.Org Foundation http://www.xiph.org/ + * Jheora was based on their Theora reference decoder. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public License + * as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +package eu.midnightdust.picturesign_theora.ogv.jheora; + +public class JTheoraException extends Exception { + private static final long serialVersionUID = 1L; +private int error; + + public JTheoraException() { + super(); + } + public JTheoraException(String str, int error) { + super(str); + + this.error = error; + } + public int getErrorCode() { + return error; + } +} diff --git a/common/src/main/java/eu/midnightdust/picturesign_theora/ogv/jheora/MemUtils.java b/common/src/main/java/eu/midnightdust/picturesign_theora/ogv/jheora/MemUtils.java new file mode 100644 index 0000000..25e1ff2 --- /dev/null +++ b/common/src/main/java/eu/midnightdust/picturesign_theora/ogv/jheora/MemUtils.java @@ -0,0 +1,116 @@ +/* Cortado - a video player java applet + * Copyright (C) 2004 Fluendo S.L. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA. + */ + +package eu.midnightdust.picturesign_theora.ogv.jheora; + +public class MemUtils { + private static final char[] bytes = { '0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f' }; + + public static final int cmp (byte[] mem1, byte[] mem2, int len) + { + for (int i=0; i 0x20 && b < 0x7f) + chars.append (vis.charAt(i)); + else + chars.append ("."); + + string.append (bytes[b/16]); + string.append (bytes[b%16]); + string.append (" "); + + j++; + i++; + + if (j == 16 || i == len) { + System.out.println ("" + (i-j) + " "+ string.toString() + chars.toString()); + + string.setLength(0); + chars.setLength(0); + j = 0; + } + } + } +} diff --git a/common/src/main/java/eu/midnightdust/picturesign_theora/ogv/jheora/MotionVector.java b/common/src/main/java/eu/midnightdust/picturesign_theora/ogv/jheora/MotionVector.java new file mode 100644 index 0000000..7022084 --- /dev/null +++ b/common/src/main/java/eu/midnightdust/picturesign_theora/ogv/jheora/MotionVector.java @@ -0,0 +1,37 @@ +/* Jheora + * Copyright (C) 2004 Fluendo S.L. + * + * Written by: 2004 Wim Taymans + * + * Many thanks to + * The Xiph.Org Foundation http://www.xiph.org/ + * Jheora was based on their Theora reference decoder. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public License + * as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +package eu.midnightdust.picturesign_theora.ogv.jheora; + +public class MotionVector extends Coordinate +{ + public static final MotionVector NULL = new MotionVector (0,0); + + public MotionVector () { + super(); + } + public MotionVector (int x, int y) { + super(x,y); + } +} diff --git a/common/src/main/java/eu/midnightdust/picturesign_theora/ogv/jheora/PixelFormat.java b/common/src/main/java/eu/midnightdust/picturesign_theora/ogv/jheora/PixelFormat.java new file mode 100644 index 0000000..606a518 --- /dev/null +++ b/common/src/main/java/eu/midnightdust/picturesign_theora/ogv/jheora/PixelFormat.java @@ -0,0 +1,42 @@ +/* Jheora + * Copyright (C) 2004 Fluendo S.L. + * + * Written by: 2004 Wim Taymans + * + * Many thanks to + * The Xiph.Org Foundation http://www.xiph.org/ + * Jheora was based on their Theora reference decoder. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public License + * as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +package eu.midnightdust.picturesign_theora.ogv.jheora; + +public class PixelFormat { + public static final PixelFormat TH_PF_420 = new PixelFormat (); + public static final PixelFormat TH_PF_RSVD = new PixelFormat(); + public static final PixelFormat TH_PF_422 = new PixelFormat (); + public static final PixelFormat TH_PF_444 = new PixelFormat (); + + public static final PixelFormat[] formats = { + TH_PF_420, + TH_PF_RSVD, + TH_PF_422, + TH_PF_444 + }; + + private PixelFormat() { + } +} diff --git a/common/src/main/java/eu/midnightdust/picturesign_theora/ogv/jheora/Playback.java b/common/src/main/java/eu/midnightdust/picturesign_theora/ogv/jheora/Playback.java new file mode 100644 index 0000000..a87c6ba --- /dev/null +++ b/common/src/main/java/eu/midnightdust/picturesign_theora/ogv/jheora/Playback.java @@ -0,0 +1,271 @@ +/* Jheora + * Copyright (C) 2004 Fluendo S.L. + * + * Written by: 2004 Wim Taymans + * + * Many thanks to + * The Xiph.Org Foundation http://www.xiph.org/ + * Jheora was based on their Theora reference decoder. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public License + * as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +package eu.midnightdust.picturesign_theora.ogv.jheora; + +import eu.midnightdust.picturesign_theora.ogv.jogg.*; + +public class Playback +{ + /* Different key frame types/methods */ + private static final int DCT_KEY_FRAME = 0; + + //oggpack_buffer *opb; + OggBuffer opb = new OggBuffer();; + TheoraInfo info; + /* how far do we shift the granulepos to seperate out P frame counts? */ + int keyframe_granule_shift; + + + /***********************************************************************/ + /* Decoder and Frame Type Information */ + + int DecoderErrorCode; + int FramesHaveBeenSkipped; + + int PostProcessEnabled; + int PostProcessingLevel; /* Perform post processing */ + + /* Frame Info */ + byte FrameType; + byte KeyFrameType; + int QualitySetting; + int FrameQIndex; /* Quality specified as a + table index */ + //int ThisFrameQualityValue; /* Quality value for this frame */ + //int LastFrameQualityValue; /* Last Frame's Quality */ + int CodedBlockIndex; /* Number of Coded Blocks */ + int CodedBlocksThisFrame; /* Index into coded blocks */ + int FrameSize; /* The number of bytes in the frame. */ + + int[] frameQIS = new int[3]; + int frameNQIS; /* number of quality indices this frame uses */ + + + /**********************************************************************/ + /* Frame Size & Index Information */ + + int YPlaneSize; + int UVPlaneSize; + int YStride; + int UVStride; + int VFragments; + int HFragments; + int UnitFragments; + int YPlaneFragments; + int UVPlaneFragments; + + int UVShiftX; /* 1 unless Info.pixel_fmt == TH_PF_444 */ + int UVShiftY; /* 0 unless Info.pixel_fmt == TH_PF_420 */ + + int ReconYPlaneSize; + int ReconUVPlaneSize; + + int YDataOffset; + int UDataOffset; + int VDataOffset; + int ReconYDataOffset; + int ReconUDataOffset; + int ReconVDataOffset; + int YSuperBlocks; /* Number of SuperBlocks in a Y frame */ + int UVSuperBlocks; /* Number of SuperBlocks in a U or V frame */ + int SuperBlocks; /* Total number of SuperBlocks in a + Y,U,V frame */ + + int YSBRows; /* Number of rows of SuperBlocks in a + Y frame */ + int YSBCols; /* Number of cols of SuperBlocks in a + Y frame */ + int UVSBRows; /* Number of rows of SuperBlocks in a + U or V frame */ + int UVSBCols; /* Number of cols of SuperBlocks in a + U or V frame */ + + int YMacroBlocks; /* Number of Macro-Blocks in Y component */ + int UVMacroBlocks; /* Number of Macro-Blocks in U/V component */ + int MacroBlocks; /* Total number of Macro-Blocks */ + + /**********************************************************************/ + /* Frames */ + short[] ThisFrameRecon; + short[] GoldenFrame; + short[] LastFrameRecon; + short[] PostProcessBuffer; + + /**********************************************************************/ + /* Fragment Information */ + int[] pixel_index_table; /* start address of first + pixel of fragment in + source */ + int[] recon_pixel_index_table; /* start address of first + pixel in recon buffer */ + + byte[] display_fragments; /* Fragment update map */ + int[] CodedBlockList; /* A list of fragment indices for + coded blocks. */ + MotionVector[] FragMVect; /* fragment motion vectors */ + + int[] FragTokenCounts; /* Number of tokens per fragment */ + int[] FragQIndex; /* Fragment Quality used in + PostProcess */ + + byte[] FragCoefEOB; /* Position of last non 0 coef + within QFragData */ + short[][] QFragData; /* Fragment Coefficients + Array Pointers */ + byte[] FragQs; /* per block quantizers */ + CodingMode[] FragCodingMethod; /* coding method for the + fragment */ + + /***********************************************************************/ + /* Macro Block and SuperBlock Information */ + BlockMapping BlockMap; /* super block + sub macro + block + sub frag -> + FragIndex */ + + /* Coded flag arrays and counters for them */ + byte[] SBCodedFlags; + byte[] SBFullyFlags; + byte[] MBCodedFlags; + byte[] MBFullyFlags; + + /**********************************************************************/ + + Coordinate[] FragCoordinates; + FrArray frArray = new FrArray(); + Filter filter = new Filter(); + + + /* quality index for each block */ + byte[] blockQ; + + /* Dequantiser and rounding tables */ + int[] quant_index = new int[64]; + + HuffEntry[] HuffRoot_VP3x = new HuffEntry[Huffman.NUM_HUFF_TABLES]; + int[][] HuffCodeArray_VP3x; + byte[][] HuffCodeLengthArray_VP3x; + byte[] ExtraBitLengths_VP3x; + + + public void clear() + { + if (opb != null) { + opb = null; + } + } + + private static int ilog (long v) + { + int ret=0; + + while (v != 0) { + ret++; + v>>=1; + } + return ret; + } + + public Playback (TheoraInfo ci) + { + info = ci; + + DecoderErrorCode = 0; + KeyFrameType = DCT_KEY_FRAME; + FramesHaveBeenSkipped = 0; + + FrInit.InitFrameDetails(this); + + keyframe_granule_shift = ilog(ci.keyframe_frequency_force-1); + //LastFrameQualityValue = 0; + + /* Initialise version specific quantiser and in-loop filter values */ + filter.copyFilterTables(ci); + + /* Huffman setup */ + initHuffmanTrees(ci); + } + + public int getFrameType() { + return FrameType; + } + + void setFrameType(byte FrType ){ + /* Set the appropriate frame type according to the request. */ + switch ( FrType ){ + case Constants.BASE_FRAME: + FrameType = FrType; + break; + default: + FrameType = FrType; + break; + } + } + + public void clearHuffmanSet() + { + Huffman.clearHuffmanTrees(HuffRoot_VP3x); + + HuffCodeArray_VP3x = null; + HuffCodeLengthArray_VP3x = null; + } + + public void initHuffmanSet() + { + clearHuffmanSet(); + + ExtraBitLengths_VP3x = HuffTables.ExtraBitLengths_VP31; + + HuffCodeArray_VP3x = new int[Huffman.NUM_HUFF_TABLES][Huffman.MAX_ENTROPY_TOKENS]; + HuffCodeLengthArray_VP3x = new byte[Huffman.NUM_HUFF_TABLES][Huffman.MAX_ENTROPY_TOKENS]; + + for (int i = 0; i < Huffman.NUM_HUFF_TABLES; i++ ){ + Huffman.buildHuffmanTree(HuffRoot_VP3x, + HuffCodeArray_VP3x[i], + HuffCodeLengthArray_VP3x[i], + i, HuffTables.FrequencyCounts_VP3[i]); + } + } + + public int readHuffmanTrees(TheoraInfo ci, OggBuffer opb) { + int i; + for (i=0; i + * 2008 Maik Merten + * + * Many thanks to + * The Xiph.Org Foundation http://www.xiph.org/ + * Jheora was based on their Theora reference decoder. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public License + * as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +package eu.midnightdust.picturesign_theora.ogv.jheora; + +import eu.midnightdust.picturesign_theora.ogv.jogg.*; + +public class Quant +{ + + private static int ilog (long v) + { + int ret=0; + + while (v != 0) { + ret++; + v>>=1; + } + return ret; + } + + public static int readQTables(TheoraInfo ci, OggBuffer opb) { + /* Variable names according to Theora spec where it makes sense. + * I *know* this may violate Java coding style rules, but I consider + * readability against the Theora spec to be more important */ + + long NBITS,value; + int x, bmi, NBMS; + + /* A 2 × 3 array containing the number of quant ranges for a + given qti and pli , respectively. This is at most 63. */ + int[][] NQRS = new int[2][3]; + + /* A 2 × 3 × 63 array of the sizes of each quant range for a + given qti and pli , respectively. Only the first NQRS[qti ][pli ] + values are used. */ + int[][][] QRSIZES = new int[2][3][63]; + + /* A 2 × 3 × 64 array of the bmi ’s used for each quant + range for a given qti and pli, respectively. Only the first + (NQRS[qti ][pli ] + 1) values are used. */ + int[][][] QRBMIS = new int[2][3][64]; + + int qri, qi, qtj, plj; + + + /* 1. Read a 4-bit unsigned integer. Assign NBITS the value read, plus one. */ + NBITS = opb.readB(4); NBITS++; + + /* 2. For each consecutive value of qi from 0 to 63, inclusive: + (a) Read an NBITS-bit unsigned integer as ACSCALE[qi ]. */ + for(x=0; x < 64; x++) { + value = opb.readB((int)NBITS); + if(NBITS<0) return Result.BADHEADER; + ci.AcScaleFactorTable[x]=(int)value; + } + + /* 3. Read a 4-bit unsigned integer. Assign NBITS the value read, plus one. */ + NBITS = opb.readB(4); NBITS++; + + /* 4. For each consecutive value of qi from 0 to 63, inclusive: + (a) Read an NBITS-bit unsigned integer as DCSCALE[qi ]. */ + for(x=0; x 384) { + return Result.BADHEADER; + } + ci.MaxQMatrixIndex = NBMS; + + /* 6. For each consecutive value of bmi from 0 to (NBMS - 1), inclusive: + (a) For each consecutive value of ci from 0 to 63, inclusive: + i. Read an 8-bit unsigned integer as BMS[bmi ][ci ]. */ + + ci.qmats= new short[NBMS*64]; + for(bmi=0; bmi 0 || pli > 0) { + /* i. If qti > 0 or pli > 0, read a 1-bit unsigned integer as NEWQR. */ + NEWQR = opb.readB(1); + } else { + /* ii. Else, assign NEWQR the value one. */ + NEWQR = 1; + } + + if(NEWQR == 0) { + /* If NEWQR is zero, then we are copying a previously defined set + of quant ranges. In that case: */ + + int RPQR; + if(qti > 0) { + /* A. If qti > 0, read a 1-bit unsigned integer as RPQR. */ + RPQR = opb.readB(1); + } else { + /* B. Else, assign RPQR the value zero. */ + RPQR = 0; + } + + if(RPQR == 1) { + /* C. If RPQR is one, assign qtj the value (qti - 1) and assign plj + the value pli . This selects the set of quant ranges defined + for the same color plane as this one, but for the previous + quantization type. */ + qtj = qti - 1; + plj = pli; + } else { + /* D. Else assign qtj the value (3 * qti + pli - 1)//3 and assign plj + the value (pli + 2)%3. This selects the most recent set of + quant ranges defined. */ + qtj = (3 * qti + pli - 1) / 3; + plj = (pli + 2) % 3; + } + + /* E. Assign NQRS[qti ][pli ] the value NQRS[qtj ][plj ]. */ + NQRS[qti][pli] = NQRS[qtj][plj]; + + /* F. Assign QRSIZES[qti ][pli ] the values in QRSIZES[qtj ][plj ]. */ + QRSIZES[qti][pli] = QRSIZES[qtj][plj]; + + /* G. Assign QRBMIS[qti ][pli ] the values in QRBMIS[qtj ][plj ]. */ + QRBMIS[qti][pli] = QRBMIS[qtj][plj]; + + } else { + /* Else, NEWQR is one, which indicates that we are defining a new + set of quant ranges. In that case: */ + + /*A. Assign qri the value zero. */ + qri = 0; + + /*B. Assign qi the value zero. */ + qi = 0; + + /* C. Read an ilog(NBMS - 1)-bit unsigned integer as + QRBMIS[qti ][pli ][qri ]. If this is greater than or equal to + NBMS, stop. The stream is undecodable. */ + QRBMIS[qti][pli][qri] = opb.readB(ilog(NBMS - 1)); + if(QRBMIS[qti][pli][qri] >= NBMS) { + System.out.println("bad header (1)"); + return Result.BADHEADER; + } + + do { + /* D. Read an ilog(62 - qi )-bit unsigned integer. Assign + QRSIZES[qti ][pli ][qri ] the value read, plus one. */ + QRSIZES[qti][pli][qri] = opb.readB(ilog(62 - qi)) + 1; + + /* E. Assign qi the value qi + QRSIZES[qti ][pli ][qri ]. */ + qi = qi + QRSIZES[qti][pli][qri]; + + /* F. Assign qri the value qri + 1. */ + qri = qri + 1; + + /* G. Read an ilog(NBMS - 1)-bit unsigned integer as + QRBMIS[qti ][pli ][qri ]. */ + QRBMIS[qti][pli][qri] = opb.readB(ilog(NBMS - 1)); + + /* H. If qi is less than 63, go back to step 7(a)ivD. */ + } while(qi < 63); + + /* I. If qi is greater than 63, stop. The stream is undecodable. */ + if(qi > 63) { + System.out.println("bad header (2): " + qi); + return Result.BADHEADER; + } + + /* J. Assign NQRS[qti ][pli ] the value qri . */ + NQRS[qti][pli] = qri; + } + } + } + + /* Compute all 384 matrices */ + for(int coding = 0; coding < 2; ++coding) { + for(int plane = 0; plane < 3; ++plane) { + for(int quality = 0; quality < 64; ++quality) { + short[] scaledmat = compQuantMatrix(ci.AcScaleFactorTable, ci.DcScaleFactorTable, ci.qmats, NQRS, QRSIZES, QRBMIS, coding, plane, quality); + for(int coeff = 0; coeff < 64; ++coeff) { + int j = Constants.dequant_index[coeff]; + ci.dequant_tables[coding][plane][quality][coeff] = scaledmat[j]; + } + } + } + } + + return 0; + } + + static short[] compQuantMatrix(int[] ACSCALE, short[] DCSCALE, short[] BMS, int[][] NQRS, + int[][][] QRSIZES, int[][][] QRBMIS, int qti, int pli, int qi) { + + /* Variable names according to Theora spec where it makes sense. + * I *know* this may violate Java coding style rules, but I consider + * readability against the Theora spec to be more important */ + + short[] QMAT = new short[64]; + int qri, qrj; + /* 1. Assign qri the index of a quant range such that + \qi \ge \sum_{\qrj=0}^{\qri-1} QRSIZES[\qti][\pli][\qrj], + and + \qi \le \sum_{\qrj=0}^{\qri} QRSIZES[\qti][\pli][\qrj], + */ + for(qri = 0; qri < 63; ++qri) { + int sum1 = 0; + for(qrj = 0; qrj < qri; ++qrj) { + sum1 += QRSIZES[qti][pli][qrj]; + } + + int sum2 = 0; + for(qrj = 0; qrj <= qri; ++qrj) { + sum2 += QRSIZES[qti][pli][qrj]; + } + + if(qi >= sum1 && qi <= sum2) + break; + } + + /* 2. Assign QISTART the value + \sum_{\qrj=0}^{\qri-1} QRSIZES[\qti][\pli][\qrj]. + */ + int QISTART = 0; + for(qrj = 0; qrj < qri; ++qrj) { + QISTART += QRSIZES[qti][pli][qrj]; + } + + /* 3. Assign QIEND the value + \sum_{\qrj=0}^{\qri} QRSIZES[\qti][\pli][\qrj]. + */ + int QIEND = 0; + for(qrj = 0; qrj <= qri; ++qrj) { + QIEND += QRSIZES[qti][pli][qrj]; + } + + /* 4. Assign bmi the value QRBMIS[qti ][pli ][qri ]. */ + int bmi = QRBMIS[qti][pli][qri]; + + /* 5. Assign bmj the value QRBMIS[qti ][pli ][qri + 1]. */ + int bmj = QRBMIS[qti][pli][qri + 1]; + + int[] BM = new int[64]; + int QMIN; + /* 6. For each consecutive value of ci from 0 to 63, inclusive: */ + for(int ci = 0; ci < 64; ++ci) { + /* (a) Assign BM[ci ] the value + (2 ∗ (QIEND − qi ) ∗ BMS[bmi ][ci ] + + 2 ∗ (qi − QISTART) ∗ BMS[bmj ][ci ] + + QRSIZES[qti ][pli ][qri ])//(2 ∗ QRSIZES[qti ][pli ][qri ]) */ + BM[ci] = (2 * (QIEND - qi) * BMS[(bmi<<6) + ci] + + 2 * (qi - QISTART) * BMS[(bmj<<6) + ci] + + QRSIZES[qti][pli][qri]) / (2 * QRSIZES[qti][pli][qri]); + + /* (b) Assign QMIN the value given by Table 6.18 according to qti and ci . */ + if(ci == 0 && qti == 0) + QMIN = 16; + else if(ci > 0 && qti == 0) + QMIN = 8; + else if(ci == 0 && qti == 1) + QMIN = 32; + else + QMIN = 16; + + int QSCALE; + if(ci == 0) { + /* (c) If ci equals zero, assign QSCALE the value DCSCALE[qi ]. */ + QSCALE = DCSCALE[qi]; + } else { + /* (d) Else, assign QSCALE the value ACSCALE[qi ]. */ + QSCALE = ACSCALE[qi]; + } + + /*(e) Assign QMAT[ci ] the value + max(QMIN, min((QSCALE ∗ BM[ci ]//100) ∗ 4, 4096)). */ + + QMAT[ci] = (short)Math.max(QMIN, Math.min((QSCALE * BM[ci]/100) * 4,4096)); + } + + return QMAT; + } + +} \ No newline at end of file diff --git a/common/src/main/java/eu/midnightdust/picturesign_theora/ogv/jheora/Recon.java b/common/src/main/java/eu/midnightdust/picturesign_theora/ogv/jheora/Recon.java new file mode 100644 index 0000000..90ac36d --- /dev/null +++ b/common/src/main/java/eu/midnightdust/picturesign_theora/ogv/jheora/Recon.java @@ -0,0 +1,110 @@ +/* Jheora + * Copyright (C) 2004 Fluendo S.L. + * + * Written by: 2004 Wim Taymans + * + * Many thanks to + * The Xiph.Org Foundation http://www.xiph.org/ + * Jheora was based on their Theora reference decoder. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public License + * as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +package eu.midnightdust.picturesign_theora.ogv.jheora; + +public final class Recon +{ + private static final short clamp255(int val) { + return (short)((~(val>>31)) & 255 & (val | ((255-val)>>31))); + } + + public static final void CopyBlock(short[] src, + short[] dest, int idx, + int srcstride) + { + int i, off=idx; + + for (i=0; i<8; i++){ + dest[off+0] = src[off+0]; + dest[off+1] = src[off+1]; + dest[off+2] = src[off+2]; + dest[off+3] = src[off+3]; + dest[off+4] = src[off+4]; + dest[off+5] = src[off+5]; + dest[off+6] = src[off+6]; + dest[off+7] = src[off+7]; + off+=srcstride; + } + } + + public static final void ReconIntra(short[] ReconPtr, int idx, + short[] ChangePtr, int LineStep) + { + int i, roff=idx, coff=0; + + for (i=0; i<8; i++ ){ + /* Convert the data back to 8 bit unsigned */ + /* Saturate the output to unsigned 8 bit values */ + ReconPtr[roff+0] = clamp255(ChangePtr[coff++] + 128); + ReconPtr[roff+1] = clamp255(ChangePtr[coff++] + 128); + ReconPtr[roff+2] = clamp255(ChangePtr[coff++] + 128); + ReconPtr[roff+3] = clamp255(ChangePtr[coff++] + 128); + ReconPtr[roff+4] = clamp255(ChangePtr[coff++] + 128); + ReconPtr[roff+5] = clamp255(ChangePtr[coff++] + 128); + ReconPtr[roff+6] = clamp255(ChangePtr[coff++] + 128); + ReconPtr[roff+7] = clamp255(ChangePtr[coff++] + 128); + roff += LineStep; + } + } + + public static final void ReconInter(short[] ReconPtr, int idx1, + short[] RefPtr, int idx2, short[] ChangePtr, + int LineStep ) { + int coff=0, roff1=idx1, roff2=idx2, i; + + for (i = 0; i < 8; i++) { + ReconPtr[roff1+0] = clamp255(RefPtr[roff2+0] + ChangePtr[coff++]); + ReconPtr[roff1+1] = clamp255(RefPtr[roff2+1] + ChangePtr[coff++]); + ReconPtr[roff1+2] = clamp255(RefPtr[roff2+2] + ChangePtr[coff++]); + ReconPtr[roff1+3] = clamp255(RefPtr[roff2+3] + ChangePtr[coff++]); + ReconPtr[roff1+4] = clamp255(RefPtr[roff2+4] + ChangePtr[coff++]); + ReconPtr[roff1+5] = clamp255(RefPtr[roff2+5] + ChangePtr[coff++]); + ReconPtr[roff1+6] = clamp255(RefPtr[roff2+6] + ChangePtr[coff++]); + ReconPtr[roff1+7] = clamp255(RefPtr[roff2+7] + ChangePtr[coff++]); + roff1 += LineStep; + roff2 += LineStep; + } + } + + public static final void ReconInterHalfPixel2(short[] ReconPtr, int idx1, + short[] RefPtr1, int idx2, short[] RefPtr2, int idx3, + short[] ChangePtr, int LineStep ) { + int coff=0, roff1=idx1, roff2=idx2, roff3=idx3, i; + + for (i = 0; i < 8; i++ ){ + ReconPtr[roff1+0] = clamp255(((RefPtr1[roff2+0] + RefPtr2[roff3+0]) >> 1) + ChangePtr[coff++]); + ReconPtr[roff1+1] = clamp255(((RefPtr1[roff2+1] + RefPtr2[roff3+1]) >> 1) + ChangePtr[coff++]); + ReconPtr[roff1+2] = clamp255(((RefPtr1[roff2+2] + RefPtr2[roff3+2]) >> 1) + ChangePtr[coff++]); + ReconPtr[roff1+3] = clamp255(((RefPtr1[roff2+3] + RefPtr2[roff3+3]) >> 1) + ChangePtr[coff++]); + ReconPtr[roff1+4] = clamp255(((RefPtr1[roff2+4] + RefPtr2[roff3+4]) >> 1) + ChangePtr[coff++]); + ReconPtr[roff1+5] = clamp255(((RefPtr1[roff2+5] + RefPtr2[roff3+5]) >> 1) + ChangePtr[coff++]); + ReconPtr[roff1+6] = clamp255(((RefPtr1[roff2+6] + RefPtr2[roff3+6]) >> 1) + ChangePtr[coff++]); + ReconPtr[roff1+7] = clamp255(((RefPtr1[roff2+7] + RefPtr2[roff3+7]) >> 1) + ChangePtr[coff++]); + roff1 += LineStep; + roff2 += LineStep; + roff3 += LineStep; + } + } +} diff --git a/common/src/main/java/eu/midnightdust/picturesign_theora/ogv/jheora/Result.java b/common/src/main/java/eu/midnightdust/picturesign_theora/ogv/jheora/Result.java new file mode 100644 index 0000000..4001cc7 --- /dev/null +++ b/common/src/main/java/eu/midnightdust/picturesign_theora/ogv/jheora/Result.java @@ -0,0 +1,36 @@ +/* Jheora + * Copyright (C) 2004 Fluendo S.L. + * + * Written by: 2004 Wim Taymans + * + * Many thanks to + * The Xiph.Org Foundation http://www.xiph.org/ + * Jheora was based on their Theora reference decoder. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public License + * as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +package eu.midnightdust.picturesign_theora.ogv.jheora; + +public final class Result { + public static final int FAULT = -1; + public static final int EINVAL = -10; + public static final int BADHEADER = -20; + public static final int NOTFORMAT = -21; + public static final int VERSION = -22; + public static final int IMPL = -23; + public static final int BADPACKET = -24; + public static final int NEWPACKET = -25; +} diff --git a/common/src/main/java/eu/midnightdust/picturesign_theora/ogv/jheora/TheoraComment.java b/common/src/main/java/eu/midnightdust/picturesign_theora/ogv/jheora/TheoraComment.java new file mode 100644 index 0000000..9b9238b --- /dev/null +++ b/common/src/main/java/eu/midnightdust/picturesign_theora/ogv/jheora/TheoraComment.java @@ -0,0 +1,36 @@ +/* Jheora + * Copyright (C) 2004 Fluendo S.L. + * + * Written by: 2004 Wim Taymans + * + * Many thanks to + * The Xiph.Org Foundation http://www.xiph.org/ + * Jheora was based on their Theora reference decoder. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public License + * as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +package eu.midnightdust.picturesign_theora.ogv.jheora; + +public class TheoraComment { + String[] user_comments; + String vendor; + + + public void clear() { + user_comments = null; + vendor = null; + } +} diff --git a/common/src/main/java/eu/midnightdust/picturesign_theora/ogv/jheora/TheoraInfo.java b/common/src/main/java/eu/midnightdust/picturesign_theora/ogv/jheora/TheoraInfo.java new file mode 100644 index 0000000..d6803d6 --- /dev/null +++ b/common/src/main/java/eu/midnightdust/picturesign_theora/ogv/jheora/TheoraInfo.java @@ -0,0 +1,265 @@ +/* Jheora + * Copyright (C) 2004 Fluendo S.L. + * + * Written by: 2004 Wim Taymans + * + * Many thanks to + * The Xiph.Org Foundation http://www.xiph.org/ + * Jheora was based on their Theora reference decoder. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public License + * as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +package eu.midnightdust.picturesign_theora.ogv.jheora; + +import eu.midnightdust.picturesign_theora.ogv.jogg.*; + +public class TheoraInfo { + public int width; + public int height; + public int frame_width; + public int frame_height; + public int offset_x; + public int offset_y; + public int fps_numerator; + public int fps_denominator; + public int aspect_numerator; + public int aspect_denominator; + public Colorspace colorspace; + public PixelFormat pixel_fmt; + public int target_bitrate; + public int quality; + public int quick_p; /* quick encode/decode */ + + /* decode only */ + public byte version_major; + public byte version_minor; + public byte version_subminor; + + public int keyframe_granule_shift; + public long keyframe_frequency_force; + + /* codec_setup_info */ + short[][][][] dequant_tables = new short[2][3][64][64]; + int[] AcScaleFactorTable = new int[Constants.Q_TABLE_SIZE]; + short[] DcScaleFactorTable = new short[Constants.Q_TABLE_SIZE]; + int MaxQMatrixIndex; + short[] qmats; + + HuffEntry[] HuffRoot = new HuffEntry[Huffman.NUM_HUFF_TABLES]; + byte[] LoopFilterLimitValues = new byte[Constants.Q_TABLE_SIZE]; + + private static void _tp_readbuffer(OggBuffer opb, byte[] buf, int len) + { + for (int i=0; i Version.VERSION_MINOR) + return Result.VERSION; + + width = (int)(opb.readB(16)<<4); + height = (int)(opb.readB(16)<<4); + frame_width = (int)opb.readB(24); + frame_height = (int)opb.readB(24); + offset_x = (int)opb.readB(8); + offset_y = (int)opb.readB(8); + + /* Invert the offset so that it is from the top down */ + offset_y = height-frame_height-offset_y; + + fps_numerator = opb.readB(32); + fps_denominator = opb.readB(32); + aspect_numerator = opb.readB(24); + aspect_denominator = opb.readB(24); + + colorspace = Colorspace.spaces[opb.readB(8)]; + target_bitrate = opb.readB(24); + quality = opb.readB(6); + + keyframe_granule_shift = opb.readB(5); + keyframe_frequency_force = 1< + * + * Many thanks to + * The Xiph.Org Foundation http://www.xiph.org/ + * Jheora was based on their Theora reference decoder. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public License + * as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +package eu.midnightdust.picturesign_theora.ogv.jheora; + +import eu.midnightdust.picturesign_theora.ogv.jogg.*; + +public class TheoraState +{ + long granulepos; + + private Playback pbi; + private Decode dec; + + public void clear() + { + if(pbi != null){ + pbi.info.clear(); + pbi.clearHuffmanSet(); + FrInit.ClearFragmentInfo(pbi); + FrInit.ClearFrameInfo(pbi); + pbi.clear(); + } + pbi = null; + } + + public int decodeInit(TheoraInfo ci) + { + pbi = new Playback(ci); + dec = new Decode(pbi); + granulepos=-1; + + return(0); + } + + public boolean isKeyframe (OggPacket op) + { + return (op.packet_base[op.packet] & 0x40) == 0; + } + + public int decodePacketin (OggPacket op) + { + long ret; + + pbi.DecoderErrorCode = 0; + + if (op.bytes>0) { + pbi.opb.readinit(op.packet_base, op.packet, op.bytes); + + /* verify that this is a video frame */ + ret = pbi.opb.readB(1); + + if (ret==0) { + try { + ret=dec.loadAndDecode(); + } catch(Exception e) { + /* If lock onto the bitstream is lost all sort of Exceptions can occur. + * The bitstream damage may be local, so the next packet may be okay. */ + e.printStackTrace(); + return Result.BADPACKET; + } + + if(ret != 0) + return (int) ret; + + } else { + return Result.BADPACKET; + } + } + if(op.granulepos>-1) + granulepos=op.granulepos; + else{ + if(granulepos==-1){ + granulepos=0; + } + else { + if ((op.bytes>0) && (pbi.FrameType == Constants.BASE_FRAME)){ + long frames= granulepos & ((1<>=pbi.keyframe_granule_shift; + granulepos+=frames+1; + granulepos<<=pbi.keyframe_granule_shift; + }else + granulepos++; + } + } + + return(0); + } + + public int decodeYUVout (YUVBuffer yuv) + { + yuv.y_width = pbi.info.width; + yuv.y_height = pbi.info.height; + yuv.y_stride = pbi.YStride; + + yuv.uv_width = pbi.info.width >> pbi.UVShiftX; + yuv.uv_height = pbi.info.height >> pbi.UVShiftY; + yuv.uv_stride = pbi.UVStride; + + if(pbi.PostProcessingLevel != 0){ + yuv.data = pbi.PostProcessBuffer; + }else{ + yuv.data = pbi.LastFrameRecon; + } + yuv.y_offset = pbi.ReconYDataOffset; + yuv.u_offset = pbi.ReconUDataOffset; + yuv.v_offset = pbi.ReconVDataOffset; + + /* we must flip the internal representation, + so make the stride negative and start at the end */ + yuv.y_offset += yuv.y_stride * (yuv.y_height - 1); + yuv.u_offset += yuv.uv_stride * (yuv.uv_height - 1); + yuv.v_offset += yuv.uv_stride * (yuv.uv_height - 1); + yuv.y_stride = - yuv.y_stride; + yuv.uv_stride = - yuv.uv_stride; + + yuv.newPixels(); + + return 0; + } + + /* returns, in seconds, absolute time of current packet in given + logical stream */ + public double granuleTime(long granulepos) + { + if(granulepos>=0){ + long iframe=granulepos>>pbi.keyframe_granule_shift; + long pframe=granulepos-(iframe< + * + * Many thanks to + * The Xiph.Org Foundation http://www.xiph.org/ + * Jheora was based on their Theora reference decoder. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public License + * as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +package eu.midnightdust.picturesign_theora.ogv.jheora; + +public class Version +{ + public static final int VERSION_MAJOR = 3; + public static final int VERSION_MINOR = 2; + public static final int VERSION_SUB = 0; + + private static final String VENDOR_STRING = "Xiph.Org libTheora I 20040317 3 2 0"; + + public static String getVersionString() + { + return VENDOR_STRING; + } + + public static int getVersionNumber() + { + return (VERSION_MAJOR<<16) + (VERSION_MINOR<<8) + (VERSION_SUB); + } +} diff --git a/common/src/main/java/eu/midnightdust/picturesign_theora/ogv/jheora/YUVBuffer.java b/common/src/main/java/eu/midnightdust/picturesign_theora/ogv/jheora/YUVBuffer.java new file mode 100644 index 0000000..0786764 --- /dev/null +++ b/common/src/main/java/eu/midnightdust/picturesign_theora/ogv/jheora/YUVBuffer.java @@ -0,0 +1,293 @@ +/* Jheora + * Copyright (C) 2004 Fluendo S.L. + * + * Written by: 2004 Wim Taymans + * + * Many thanks to + * The Xiph.Org Foundation http://www.xiph.org/ + * Jheora was based on their Theora reference decoder. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public License + * as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ +package eu.midnightdust.picturesign_theora.ogv.jheora; + +public class YUVBuffer { + + public int y_width; + public int y_height; + public int y_stride; + public int uv_width; + public int uv_height; + public int uv_stride; + public short[] data; + public int y_offset; + public int u_offset; + public int v_offset; + private int[] pixels; + private int pix_size; + private boolean newPixels = true; + + public int[] produce() { + prepareRGBData(0, 0, y_width, y_height); + return pixels; + } + + private synchronized void prepareRGBData(int x, int y, int width, int height) { + if (!newPixels) { + return; + } + + int size = width * height; + + try { + if (size != pix_size) { + pixels = new int[size]; + pix_size = size; + } + /* rely on the buffer size being set correctly, and the only allowed + video formats being Theora's video formats */ + if (uv_height < y_height) YUV420toRGB(x, y, width, height); + else if (uv_width == y_width) YUV444toRGB(x, y, width, height); + else YUV422toRGB(x, y, width, height); + } catch (Throwable t) { + /* ignore */ + } + newPixels = false; + } + + public synchronized void newPixels() { + newPixels = true; + } + + + private void YUV420toRGB(int x, int y, int width, int height) { + + /* + * this modified version of the original YUVtoRGB was + * provided by Ilan and Yaniv Ben Hagai. + * + * additional thanks to Gumboot for helping with making this + * code perform better. + */ + + // Set up starting values for YUV pointers + int YPtr = y_offset + x + y * (y_stride); + int YPtr2 = YPtr + y_stride; + int UPtr = u_offset + x / 2 + (y / 2) * (uv_stride); + int VPtr = v_offset + x / 2 + (y / 2) * (uv_stride); + int RGBPtr = 0; + int RGBPtr2 = width; + int width2 = width / 2; + int height2 = height / 2; + + // Set the line step for the Y and UV planes and YPtr2 + int YStep = y_stride * 2 - (width2) * 2; + int UVStep = uv_stride - (width2); + int RGBStep = width; + + for (int i = 0; i < height2; i++) { + for (int j = 0; j < width2; j++) { + int D, E, r, g, b, t1, t2, t3, t4; + + D = data[UPtr++]; + E = data[VPtr++]; + + t1 = 298 * (data[YPtr] - 16); + t2 = 409 * E - 409*128 + 128; + t3 = (100 * D) + (208 * E) - 100*128 - 208*128 - 128; + t4 = 516 * D - 516*128 + 128; + + r = (t1 + t2); + g = (t1 - t3); + b = (t1 + t4); + + // retrieve data for next pixel now, hide latency? + t1 = 298 * (data[YPtr + 1] - 16); + + // pack pixel + pixels[RGBPtr] = + (clamp65280(r) << 8) | clamp65280(g) | (clamp65280(b)>>8) | 0xff000000; + + r = (t1 + t2); + g = (t1 - t3); + b = (t1 + t4); + + // retrieve data for next pixel now, hide latency? + t1 = 298 * (data[YPtr2] - 16); + + // pack pixel + pixels[RGBPtr + 1] = + (clamp65280(r) << 8) | clamp65280(g) | (clamp65280(b)>>8) | 0xff000000; + + + r = (t1 + t2); + g = (t1 - t3); + b = (t1 + t4); + + // retrieve data for next pixel now, hide latency? + t1 = 298 * (data[YPtr2 + 1] - 16); + + // pack pixel + pixels[RGBPtr2] = + (clamp65280(r) << 8) | clamp65280(g) | (clamp65280(b)>>8) | 0xff000000; + + + r = (t1 + t2); + g = (t1 - t3); + b = (t1 + t4); + + // pack pixel + pixels[RGBPtr2 + 1] = + (clamp65280(r) << 8) | clamp65280(g) | (clamp65280(b)>>8) | 0xff000000; + YPtr += 2; + YPtr2 += 2; + RGBPtr += 2; + RGBPtr2 += 2; + } + + // Increment the various pointers + YPtr += YStep; + YPtr2 += YStep; + UPtr += UVStep; + VPtr += UVStep; + RGBPtr += RGBStep; + RGBPtr2 += RGBStep; + } + } + + // kept for reference + /*private static final int clamp255(int val) { + return ((~(val>>31)) & 255 & (val | ((255-val)>>31))); + }*/ + + private static final int clamp65280(int val) { + /* 65280 == 255 << 8 == 0x0000FF00 */ + /* This function is just like clamp255, but only acting on the top + 24 bits (bottom 8 are zero'd). This allows val, initially scaled + to 65536, to be clamped without shifting, thereby saving one shift. + (RGB packing must be aware that the info is in the second-lowest + byte.) */ + return ((~(val>>31)) & 65280 & (val | ((65280-val)>>31))); + } + + private void YUV444toRGB(int x, int y, int width, int height) { + for (int j = 0; j < height; j++) { + for (int i = 0; i < width; i++) { + int D, E, r, g, b, t1, t2, t3, t4, p; + p = x + i + (j + y)*y_stride; + + D = data[u_offset + p]; + E = data[v_offset + p]; + + t1 = 298 * (data[y_offset + p] - 16); + t2 = 409 * E - 409*128 + 128; + t3 = (100 * D) + (208 * E) - 100*128 - 208*128 - 128; + t4 = 516 * D - 516*128 + 128; + + r = (t1 + t2); + g = (t1 - t3); + b = (t1 + t4); + + // pack pixel + pixels[i + j*width] = + (clamp65280(r) << 8) | clamp65280(g) | (clamp65280(b)>>8) | 0xff000000; + } + } + } + + private void YUV422toRGB(int x, int y, int width, int height) { + int x2 = x/2; + int width2 = width/2; + for (int j = 0; j < height; j++) { + for (int i = 0; i < width2; i++) { + int D, E, r, g, b, t1, t2, t3, t4, p; + p = x2 + i + (y + j)*uv_stride; + + D = data[u_offset + p]; + E = data[v_offset + p]; + + p = y_offset + 2*(x2 + i) + (y + j)*y_stride; + t1 = 298 * (data[p] - 16); + t2 = 409 * E - 409*128 + 128; + t3 = (100 * D) + (208 * E) - 100*128 - 208*128 - 128; + t4 = 516 * D - 516*128 + 128; + + r = (t1 + t2); + g = (t1 - t3); + b = (t1 + t4); + + p++; + t1 = 298 * (data[p] - 16); + + // pack pixel + p = 2*i + j*width; + pixels[p] = + (clamp65280(r) << 8) | clamp65280(g) | (clamp65280(b)>>8) | 0xff000000; + + r = (t1 + t2); + g = (t1 - t3); + b = (t1 + t4); + p++; + + // pack pixel + pixels[p] = + (clamp65280(r) << 8) | clamp65280(g) | (clamp65280(b)>>8) | 0xff000000; + } + } + } + + + // some benchmarking stuff, uncomment if you need it + /*public static void main(String[] args) { + YUVBuffer yuvbuf = new YUVBuffer(); + + // let's create a 1280x720 picture with noise + + int x = 1280; + int y = 720; + + int size = (x * y) + (x * y) / 2; + short[] picdata = new short[size]; + + Random r = new Random(); + for (int i = 0; i < picdata.length; ++i) { + picdata[i] = (short) (r.nextInt(255) | 0xFF); + } + + System.out.println("bench..."); + + yuvbuf.data = picdata; + yuvbuf.y_height = y; + yuvbuf.y_width = x; + yuvbuf.y_stride = x; + yuvbuf.uv_height = y / 2; + yuvbuf.uv_width = x / 2; + yuvbuf.uv_stride = x / 2; + yuvbuf.u_offset = x / 2; + yuvbuf.v_offset = x + x / 2; + + int times = 5000; + + long start = System.currentTimeMillis(); + for (int i = 0; i < times; ++i) { + yuvbuf.newPixels(); + yuvbuf.prepareRGBData(0, 0, x, y); + } + long end = System.currentTimeMillis(); + + System.out.println("average conversion time per frame: " + ((double) (end - start)) / (times * 1f) + " ms."); + + }*/ +} diff --git a/common/src/main/java/eu/midnightdust/picturesign_theora/ogv/jheora/iDCT.java b/common/src/main/java/eu/midnightdust/picturesign_theora/ogv/jheora/iDCT.java new file mode 100644 index 0000000..855dca4 --- /dev/null +++ b/common/src/main/java/eu/midnightdust/picturesign_theora/ogv/jheora/iDCT.java @@ -0,0 +1,366 @@ +/* Jheora + * Copyright (C) 2004 Fluendo S.L. + * + * Written by: 2004 Wim Taymans + * + * Many thanks to + * The Xiph.Org Foundation http://www.xiph.org/ + * Jheora was based on their Theora reference decoder. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public License + * as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +package eu.midnightdust.picturesign_theora.ogv.jheora; + +public final class iDCT +{ + private static final int IdctAdjustBeforeShift = 8; + private static final int xC1S7 = 64277; + private static final int xC2S6 = 60547; + private static final int xC3S5 = 54491; + private static final int xC4S4 = 46341; + private static final int xC5S3 = 36410; + private static final int xC6S2 = 25080; + private static final int xC7S1 = 12785; + + private int[] ip = new int[64]; + + private final void dequant_slow(short[] dequant_coeffs, + short[] quantized_list, + int[] DCT_block) + { + for(int i = 0; i < 64; i++) { + DCT_block[Constants.dequant_index[i]] = quantized_list[i] * dequant_coeffs[i]; + /* + if (i%8 ==0) + System.out.print(": "); + System.out.print("("+DCT_block[Constants.dequant_index[i]]+" "); + System.out.print(dequant_coeffs[i]+" "); + System.out.print(quantized_list[i]+")"); + if (i%8 ==7) + System.out.print("\n"); + */ + } + } + + public final void IDctSlow (short[] InputData, short[]QuantMatrix, short[] OutputData) + { + short[] op = OutputData; + + int _A, _B, _C, _D, _Ad, _Bd, _Cd, _Dd, _E, _F, _G, _H; + int _Ed, _Gd, _Add, _Bdd, _Fd, _Hd; + int t1, t2; + + dequant_slow (QuantMatrix, InputData, ip); + + /* Inverse DCT on the rows now */ + for (int loop = 0, off=0; loop < 8; loop++, off+=8){ + /* Check for non-zero values */ + if ((ip[0 + off] | ip[1 + off] | ip[2 + off] | ip[3 + off] | + ip[4 + off] | ip[5 + off] | ip[6 + off] | ip[7 + off]) != 0 ) + { + t1 = (xC1S7 * ip[1 + off]) >> 16; + t2 = (xC7S1 * ip[7 + off]) >> 16; + _A = t1 + t2; + + t1 = (xC7S1 * ip[1 + off]) >> 16; + t2 = (xC1S7 * ip[7 + off]) >> 16; + _B = t1 - t2; + + t1 = (xC3S5 * ip[3 + off]) >> 16; + t2 = (xC5S3 * ip[5 + off]) >> 16; + _C = t1 + t2; + + t1 = (xC3S5 * ip[5 + off]) >> 16; + t2 = (xC5S3 * ip[3 + off]) >> 16; + _D = t1 - t2; + + _Ad = (xC4S4 * (short)(_A - _C)) >> 16; + _Bd = (xC4S4 * (short)(_B - _D)) >> 16; + + _Cd = _A + _C; + _Dd = _B + _D; + + _E = (xC4S4 * (short)(ip[0 + off] + ip[4 + off])) >> 16; + _F = (xC4S4 * (short)(ip[0 + off] - ip[4 + off])) >> 16; + + t1 = (xC2S6 * ip[2 + off]) >> 16; + t2 = (xC6S2 * ip[6 + off]) >> 16; + _G = t1 + t2; + + t1 = (xC6S2 * ip[2 + off]) >> 16; + t2 = (xC2S6 * ip[6 + off]) >> 16; + _H = t1 - t2; + + + _Ed = _E - _G; + _Gd = _E + _G; + + _Add = _F + _Ad; + _Bdd = _Bd - _H; + + _Fd = _F - _Ad; + _Hd = _Bd + _H; + + /* Final sequence of operations over-write original inputs. */ + ip[0 + off] = (short)((_Gd + _Cd ) >> 0); + ip[7 + off] = (short)((_Gd - _Cd ) >> 0); + + ip[1 + off] = (short)((_Add + _Hd ) >> 0); + ip[2 + off] = (short)((_Add - _Hd ) >> 0); + + ip[3 + off] = (short)((_Ed + _Dd ) >> 0); + ip[4 + off] = (short)((_Ed - _Dd ) >> 0); + + ip[5 + off] = (short)((_Fd + _Bdd ) >> 0); + ip[6 + off] = (short)((_Fd - _Bdd ) >> 0); + } + } + + for (int loop = 0, off=0; loop < 8; loop++, off++){ + /* Check for non-zero values (bitwise or faster than ||) */ + if ((ip[0 * 8 + off] | ip[1 * 8 + off] | ip[2 * 8 + off] | ip[3 * 8 + off] | + ip[4 * 8 + off] | ip[5 * 8 + off] | ip[6 * 8 + off] | ip[7 * 8 + off]) != 0) + { + t1 = (xC1S7 * ip[1*8 + off]) >> 16; + t2 = (xC7S1 * ip[7*8 + off]) >> 16; + _A = t1 + t2; + + t1 = (xC7S1 * ip[1*8 + off]) >> 16; + t2 = (xC1S7 * ip[7*8 + off]) >> 16; + _B = t1 - t2; + + t1 = (xC3S5 * ip[3*8 + off]) >> 16; + t2 = (xC5S3 * ip[5*8 + off]) >> 16; + _C = t1 + t2; + + t1 = (xC3S5 * ip[5*8 + off]) >> 16; + t2 = (xC5S3 * ip[3*8 + off]) >> 16; + _D = t1 - t2; + + _Ad = (xC4S4 * (short)(_A - _C)) >> 16; + _Bd = (xC4S4 * (short)(_B - _D)) >> 16; + + _Cd = _A + _C; + _Dd = _B + _D; + + _E = (xC4S4 * (short)(ip[0*8 + off] + ip[4*8 + off])) >> 16; + _F = (xC4S4 * (short)(ip[0*8 + off] - ip[4*8 + off])) >> 16; + + t1 = (xC2S6 * ip[2*8 + off]) >> 16; + t2 = (xC6S2 * ip[6*8 + off]) >> 16; + _G = t1 + t2; + + t1 = (xC6S2 * ip[2*8 + off]) >> 16; + t2 = (xC2S6 * ip[6*8 + off]) >> 16; + _H = t1 - t2; + + _Ed = _E - _G; + _Gd = _E + _G; + + _Add = _F + _Ad; + _Bdd = _Bd - _H; + + _Fd = _F - _Ad; + _Hd = _Bd + _H; + + _Gd += IdctAdjustBeforeShift; + _Add += IdctAdjustBeforeShift; + _Ed += IdctAdjustBeforeShift; + _Fd += IdctAdjustBeforeShift; + + /* Final sequence of operations over-write original inputs. */ + op[0*8 + off] = (short)((_Gd + _Cd ) >> 4); + op[7*8 + off] = (short)((_Gd - _Cd ) >> 4); + + op[1*8 + off] = (short)((_Add + _Hd ) >> 4); + op[2*8 + off] = (short)((_Add - _Hd ) >> 4); + + op[3*8 + off] = (short)((_Ed + _Dd ) >> 4); + op[4*8 + off] = (short)((_Ed - _Dd ) >> 4); + + op[5*8 + off] = (short)((_Fd + _Bdd ) >> 4); + op[6*8 + off] = (short)((_Fd - _Bdd ) >> 4); + }else{ + op[0*8 + off] = 0; + op[7*8 + off] = 0; + op[1*8 + off] = 0; + op[2*8 + off] = 0; + op[3*8 + off] = 0; + op[4*8 + off] = 0; + op[5*8 + off] = 0; + op[6*8 + off] = 0; + } + } + } + + + /************************ + x x x x 0 0 0 0 + x x x 0 0 0 0 0 + x x 0 0 0 0 0 0 + x 0 0 0 0 0 0 0 + 0 0 0 0 0 0 0 0 + 0 0 0 0 0 0 0 0 + 0 0 0 0 0 0 0 0 + 0 0 0 0 0 0 0 0 + *************************/ + + private final void dequant_slow10 (short[] dequant_coeffs, + short[] quantized_list, + int[] DCT_block) + { + for(int i=0; i<32; i++) + DCT_block[i] = 0; + + for(int i=0; i<10; i++) + DCT_block[Constants.dequant_index[i]] = quantized_list[i] * dequant_coeffs[i]; + } + + public final void IDct10 (short[] InputData, short[]QuantMatrix, short[] OutputData) + { + + short[] op = OutputData; + + int _A, _B, _C, _D, _Ad, _Bd, _Cd, _Dd, _E, _F, _G, _H; + int _Ed, _Gd, _Add, _Bdd, _Fd, _Hd; + + dequant_slow10 (QuantMatrix, InputData, ip); + + /* Inverse DCT on the rows now */ + for (int loop = 0, off = 0; loop < 4; loop++, off += 8){ + /* Check for non-zero values */ + if ((ip[0 + off] | ip[1 + off] | ip[2 + off] | ip[3 + off]) != 0 ){ + _A = (xC1S7 * ip[1 + off]) >> 16; + _B = (xC7S1 * ip[1 + off]) >> 16; + _C = (xC3S5 * ip[3 + off]) >> 16; + _D = -((xC5S3 * ip[3 + off]) >> 16); + + _Ad = (xC4S4 * (short)(_A - _C)) >> 16; + _Bd = (xC4S4 * (short)(_B - _D)) >> 16; + + _Cd = _A + _C; + _Dd = _B + _D; + + _E = (xC4S4 * ip[0 + off] ) >> 16; + _F = _E; + _G = (xC2S6 * ip[2 + off]) >> 16; + _H = (xC6S2 * ip[2 + off]) >> 16; + + _Ed = _E - _G; + _Gd = _E + _G; + + _Add = _F + _Ad; + _Bdd = _Bd - _H; + + _Fd = _F - _Ad; + _Hd = _Bd + _H; + + /* Final sequence of operations over-write original inputs. */ + ip[0 + off] = (short)((_Gd + _Cd ) >> 0); + ip[7 + off] = (short)((_Gd - _Cd ) >> 0); + + ip[1 + off] = (short)((_Add + _Hd ) >> 0); + ip[2 + off] = (short)((_Add - _Hd ) >> 0); + + ip[3 + off] = (short)((_Ed + _Dd ) >> 0); + ip[4 + off] = (short)((_Ed - _Dd ) >> 0); + + ip[5 + off] = (short)((_Fd + _Bdd ) >> 0); + ip[6 + off] = (short)((_Fd - _Bdd ) >> 0); + + } + } + + for (int loop = 0, off = 0; loop < 8; loop++, off++) { + /* Check for non-zero values (bitwise or faster than ||) */ + if ((ip[0*8 + off] | ip[1*8 + off] | ip[2*8 + off] | ip[3*8 + off]) != 0) { + + _A = (xC1S7 * ip[1*8 + off]) >> 16; + _B = (xC7S1 * ip[1*8 + off]) >> 16; + _C = (xC3S5 * ip[3*8 + off]) >> 16; + _D = -((xC5S3 * ip[3*8 + off]) >> 16); + + _Ad = (xC4S4 * (short)(_A - _C)) >> 16; + _Bd = (xC4S4 * (short)(_B - _D)) >> 16; + + _Cd = _A + _C; + _Dd = _B + _D; + + _E = (xC4S4 * ip[0*8 + off]) >> 16; + _F = _E; + _G = (xC2S6 * ip[2*8 + off]) >> 16; + _H = (xC6S2 * ip[2*8 + off]) >> 16; + + _Ed = _E - _G; + _Gd = _E + _G; + + _Add = _F + _Ad; + _Bdd = _Bd - _H; + + _Fd = _F - _Ad; + _Hd = _Bd + _H; + + _Gd += IdctAdjustBeforeShift; + _Add += IdctAdjustBeforeShift; + _Ed += IdctAdjustBeforeShift; + _Fd += IdctAdjustBeforeShift; + + /* Final sequence of operations over-write original inputs. */ + op[0*8 + off] = (short)((_Gd + _Cd ) >> 4); + op[7*8 + off] = (short)((_Gd - _Cd ) >> 4); + + op[1*8 + off] = (short)((_Add + _Hd ) >> 4); + op[2*8 + off] = (short)((_Add - _Hd ) >> 4); + + op[3*8 + off] = (short)((_Ed + _Dd ) >> 4); + op[4*8 + off] = (short)((_Ed - _Dd ) >> 4); + + op[5*8 + off] = (short)((_Fd + _Bdd ) >> 4); + op[6*8 + off] = (short)((_Fd - _Bdd ) >> 4); + }else{ + op[0*8 + off] = 0; + op[7*8 + off] = 0; + op[1*8 + off] = 0; + op[2*8 + off] = 0; + op[3*8 + off] = 0; + op[4*8 + off] = 0; + op[5*8 + off] = 0; + op[6*8 + off] = 0; + } + } + } + + + /*************************** + x 0 0 0 0 0 0 0 + 0 0 0 0 0 0 0 0 + 0 0 0 0 0 0 0 0 + 0 0 0 0 0 0 0 0 + 0 0 0 0 0 0 0 0 + 0 0 0 0 0 0 0 0 + 0 0 0 0 0 0 0 0 + 0 0 0 0 0 0 0 0 + **************************/ + + public final void IDct1( short[] InputData, + short[]QuantMatrix, + short[] OutputData ){ + short OutD=(short) ((int)(InputData[0]*QuantMatrix[0]+15)>>5); + + for (int loop=0; loop<64; loop++) + OutputData[loop]=OutD; + } +} diff --git a/common/src/main/java/eu/midnightdust/picturesign_theora/ogv/jogg/OggBuffer.java b/common/src/main/java/eu/midnightdust/picturesign_theora/ogv/jogg/OggBuffer.java new file mode 100644 index 0000000..5523f91 --- /dev/null +++ b/common/src/main/java/eu/midnightdust/picturesign_theora/ogv/jogg/OggBuffer.java @@ -0,0 +1,294 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* JOrbis + * Copyright (C) 2000 ymnk, JCraft,Inc. + * + * Written by: 2000 ymnk + * + * Many thanks to + * Monty and + * The XIPHOPHORUS Company http://www.xiph.org/ . + * JOrbis has been based on their awesome works, Vorbis codec. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public License + * as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +package eu.midnightdust.picturesign_theora.ogv.jogg; + +public class OggBuffer{ + private static final int BUFFER_INCREMENT=256; + + private static final int[] mask= {0x00000000, 0x00000001, 0x00000003, + 0x00000007, 0x0000000f, 0x0000001f, 0x0000003f, 0x0000007f, 0x000000ff, + 0x000001ff, 0x000003ff, 0x000007ff, 0x00000fff, 0x00001fff, 0x00003fff, + 0x00007fff, 0x0000ffff, 0x0001ffff, 0x0003ffff, 0x0007ffff, 0x000fffff, + 0x001fffff, 0x003fffff, 0x007fffff, 0x00ffffff, 0x01ffffff, 0x03ffffff, + 0x07ffffff, 0x0fffffff, 0x1fffffff, 0x3fffffff, 0x7fffffff, 0xffffffff}; + + int ptr=0; + byte[] buffer=null; + int endbit=0; + int endbyte=0; + int storage=0; + + public void writeinit(){ + buffer=new byte[BUFFER_INCREMENT]; + ptr=0; + buffer[0]=(byte)'\0'; + storage=BUFFER_INCREMENT; + } + + public void write(byte[] s){ + for(int i=0; i=storage){ + byte[] foo=new byte[storage+BUFFER_INCREMENT]; + System.arraycopy(buffer, 0, foo, 0, storage); + buffer=foo; + storage+=BUFFER_INCREMENT; + } + + value&=mask[bits]; + bits+=endbit; + buffer[ptr]|=(byte)(value<=8){ + buffer[ptr+1]=(byte)(value>>>(8-endbit)); + if(bits>=16){ + buffer[ptr+2]=(byte)(value>>>(16-endbit)); + if(bits>=24){ + buffer[ptr+3]=(byte)(value>>>(24-endbit)); + if(bits>=32){ + if(endbit>0) + buffer[ptr+4]=(byte)(value>>>(32-endbit)); + else + buffer[ptr+4]=0; + } + } + } + } + + endbyte+=bits/8; + ptr+=bits/8; + endbit=bits&7; + } + + public int look(int bits){ + int ret; + int m=mask[bits]; + + bits+=endbit; + + if(endbyte+4>=storage){ + if(endbyte+(bits-1)/8>=storage) + return (-1); + } + + ret=((buffer[ptr])&0xff)>>>endbit; + if(bits>8){ + ret|=((buffer[ptr+1])&0xff)<<(8-endbit); + if(bits>16){ + ret|=((buffer[ptr+2])&0xff)<<(16-endbit); + if(bits>24){ + ret|=((buffer[ptr+3])&0xff)<<(24-endbit); + if(bits>32&&endbit!=0){ + ret|=((buffer[ptr+4])&0xff)<<(32-endbit); + } + } + } + } + return (m&ret); + } + + public int look1(){ + if(endbyte>=storage) + return (-1); + return ((buffer[ptr]>>endbit)&1); + } + + public void adv(int bits){ + bits+=endbit; + ptr+=bits/8; + endbyte+=bits/8; + endbit=bits&7; + } + + public void adv1(){ + ++endbit; + if(endbit>7){ + endbit=0; + ptr++; + endbyte++; + } + } + + public int read(int bits){ + int ret; + int m=mask[bits]; + + bits+=endbit; + + if(endbyte+4>=storage){ + ret=-1; + if(endbyte+(bits-1)/8>=storage){ + ptr+=bits/8; + endbyte+=bits/8; + endbit=bits&7; + return (ret); + } + } + + ret=((buffer[ptr])&0xff)>>>endbit; + if(bits>8){ + ret|=((buffer[ptr+1])&0xff)<<(8-endbit); + if(bits>16){ + ret|=((buffer[ptr+2])&0xff)<<(16-endbit); + if(bits>24){ + ret|=((buffer[ptr+3])&0xff)<<(24-endbit); + if(bits>32&&endbit!=0){ + ret|=((buffer[ptr+4])&0xff)<<(32-endbit); + } + } + } + } + + ret&=m; + + ptr+=bits/8; + endbyte+=bits/8; + endbit=bits&7; + return (ret); + } + + public int readB(int bits){ + int ret; + int m=32-bits; + + bits+=endbit; + + if(endbyte+4>=storage){ + /* not the main path */ + ret=-1; + if(endbyte*8+bits>storage*8){ + ptr+=bits/8; + endbyte+=bits/8; + endbit=bits&7; + return (ret); + } + } + + ret=(buffer[ptr]&0xff)<<(24+endbit); + if(bits>8){ + ret|=(buffer[ptr+1]&0xff)<<(16+endbit); + if(bits>16){ + ret|=(buffer[ptr+2]&0xff)<<(8+endbit); + if(bits>24){ + ret|=(buffer[ptr+3]&0xff)<<(endbit); + if(bits>32&&(endbit!=0)) + ret|=(buffer[ptr+4]&0xff)>>(8-endbit); + } + } + } + ret=(ret>>>(m>>1))>>>((m+1)>>1); + + ptr+=bits/8; + endbyte+=bits/8; + endbit=bits&7; + return (ret); + } + + public int read1(){ + int ret; + if(endbyte>=storage){ + ret=-1; + endbit++; + if(endbit>7){ + endbit=0; + ptr++; + endbyte++; + } + return (ret); + } + + ret=(buffer[ptr]>>endbit)&1; + + endbit++; + if(endbit>7){ + endbit=0; + ptr++; + endbyte++; + } + return (ret); + } + + public int bytes(){ + return (endbyte+(endbit+7)/8); + } + + public int bits(){ + return (endbyte*8+endbit); + } + + public byte[] buffer(){ + return (buffer); + } + + public static int ilog(int v){ + int ret=0; + while(v>0){ + ret++; + v>>>=1; + } + return (ret); + } + + public static void report(String in){ + System.err.println(in); + System.exit(1); + } +} diff --git a/common/src/main/java/eu/midnightdust/picturesign_theora/ogv/jogg/OggPacket.java b/common/src/main/java/eu/midnightdust/picturesign_theora/ogv/jogg/OggPacket.java new file mode 100644 index 0000000..8b72c03 --- /dev/null +++ b/common/src/main/java/eu/midnightdust/picturesign_theora/ogv/jogg/OggPacket.java @@ -0,0 +1,47 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* JOrbis + * Copyright (C) 2000 ymnk, JCraft,Inc. + * + * Written by: 2000 ymnk + * + * Many thanks to + * Monty and + * The XIPHOPHORUS Company http://www.xiph.org/ . + * JOrbis has been based on their awesome works, Vorbis codec. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public License + * as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +package eu.midnightdust.picturesign_theora.ogv.jogg; + +public class OggPacket{ + public byte[] packet_base; + public int packet; + public int bytes; + public int b_o_s; + public int e_o_s; + + public long granulepos; + + /** + * sequence number for decode; the framing + * knows where there's a hole in the data, + * but we need coupling so that the codec + * (which is in a seperate abstraction + * layer) also knows about the gap + */ + public long packetno; + +} diff --git a/common/src/main/java/eu/midnightdust/picturesign_theora/ogv/jogg/OggPage.java b/common/src/main/java/eu/midnightdust/picturesign_theora/ogv/jogg/OggPage.java new file mode 100644 index 0000000..dcb1a02 --- /dev/null +++ b/common/src/main/java/eu/midnightdust/picturesign_theora/ogv/jogg/OggPage.java @@ -0,0 +1,135 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* JOrbis + * Copyright (C) 2000 ymnk, JCraft,Inc. + * + * Written by: 2000 ymnk + * + * Many thanks to + * Monty and + * The XIPHOPHORUS Company http://www.xiph.org/ . + * JOrbis has been based on their awesome works, Vorbis codec. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public License + * as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +package eu.midnightdust.picturesign_theora.ogv.jogg; + +public class OggPage{ + private static int[] crc_lookup=new int[256]; + static{ + for(int i=0; i>>24)&0xff)^(header_base[header+i]&0xff)]; + } + for(int i=0; i>>24)&0xff)^(body_base[body+i]&0xff)]; + } + header_base[header+22]=(byte)crc_reg; + header_base[header+23]=(byte)(crc_reg>>>8); + header_base[header+24]=(byte)(crc_reg>>>16); + header_base[header+25]=(byte)(crc_reg>>>24); + } + + public OggPage copy(){ + return copy(new OggPage()); + } + + public OggPage copy(OggPage p){ + byte[] tmp=new byte[header_len]; + System.arraycopy(header_base, header, tmp, 0, header_len); + p.header_len=header_len; + p.header_base=tmp; + p.header=0; + tmp=new byte[body_len]; + System.arraycopy(body_base, body, tmp, 0, body_len); + p.body_len=body_len; + p.body_base=tmp; + p.body=0; + return p; + } + +} diff --git a/common/src/main/java/eu/midnightdust/picturesign_theora/ogv/jogg/OggStreamState.java b/common/src/main/java/eu/midnightdust/picturesign_theora/ogv/jogg/OggStreamState.java new file mode 100644 index 0000000..922d197 --- /dev/null +++ b/common/src/main/java/eu/midnightdust/picturesign_theora/ogv/jogg/OggStreamState.java @@ -0,0 +1,526 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* JOrbis + * Copyright (C) 2000 ymnk, JCraft,Inc. + * + * Written by: 2000 ymnk + * + * Many thanks to + * Monty and + * The XIPHOPHORUS Company http://www.xiph.org/ . + * JOrbis has been based on their awesome works, Vorbis codec. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public License + * as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +package eu.midnightdust.picturesign_theora.ogv.jogg; + +public class OggStreamState{ + byte[] body_data; /* bytes from packet bodies */ + int body_storage; /* storage elements allocated */ + int body_fill; /* elements stored; fill mark */ + private int body_returned; /* elements of fill returned */ + + int[] lacing_vals; /* The values that will go to the segment table */ + long[] granule_vals; /* pcm_pos values for headers. Not compact + this way, but it is simple coupled to the + lacing fifo */ + int lacing_storage; + int lacing_fill; + int lacing_packet; + int lacing_returned; + + byte[] header=new byte[282]; /* working space for header encode */ + int header_fill; + + public int e_o_s; /* set when we have buffered the last packet in the + logical bitstream */ + int b_o_s; /* set after we've written the initial page + of a logical bitstream */ + int serialno; + int pageno; + long packetno; /* sequence number for decode; the framing + knows where there's a hole in the data, + but we need coupling so that the codec + (which is in a seperate abstraction + layer) also knows about the gap */ + long granulepos; + + public OggStreamState(){ + init(); + } + + OggStreamState(int serialno){ + this(); + init(serialno); + } + + void init(){ + body_storage=16*1024; + body_data=new byte[body_storage]; + lacing_storage=1024; + lacing_vals=new int[lacing_storage]; + granule_vals=new long[lacing_storage]; + } + + public void init(int serialno){ + if(body_data==null){ + init(); + } + else{ + for(int i=0; i0) + return (-1); + + lacing_expand(segments+1); + + // are we in sequence? + if(_pageno!=pageno){ + int i; + + // unroll previous partial packet (if any) + for(i=lacing_packet; i0) + lacing_vals[lacing_fill-1]|=0x200; + } + + pageno=_pageno+1; + return (0); + } + + /* This will flush remaining packets into a page (returning nonzero), + even if there is not enough data to trigger a flush normally + (undersized page). If there are no packets or partial packets to + flush, ogg_stream_flush returns 0. Note that ogg_stream_flush will + try to flush a normal sized page like ogg_stream_pageout; a call to + ogg_stream_flush does not gurantee that all packets have flushed. + Only a return value of 0 from ogg_stream_flush indicates all packet + data is flushed into pages. + + ogg_stream_page will flush the last page in a stream even if it's + undersized; you almost certainly want to use ogg_stream_pageout + (and *not* ogg_stream_flush) unless you need to flush an undersized + page in the middle of a stream for some reason. */ + + public int flush(OggPage og){ + + int i; + int vals=0; + int maxvals=(lacing_fill>255 ? 255 : lacing_fill); + int bytes=0; + int acc=0; + long granule_pos=granule_vals[0]; + + if(maxvals==0) + return (0); + + /* construct a page */ + /* decide how many segments to include */ + + /* If this is the initial header case, the first page must only include + the initial header packet */ + if(b_o_s==0){ /* 'initial header page' case */ + granule_pos=0; + for(vals=0; vals4096) + break; + acc+=(lacing_vals[vals]&0x0ff); + granule_pos=granule_vals[vals]; + } + } + + /* construct the header in temp storage */ + System.arraycopy("OggS".getBytes(), 0, header, 0, 4); + + /* stream structure version */ + header[4]=0x00; + + /* continued packet flag? */ + header[5]=0x00; + if((lacing_vals[0]&0x100)==0) + header[5]|=0x01; + /* first page flag? */ + if(b_o_s==0) + header[5]|=0x02; + /* last page flag? */ + if(e_o_s!=0&&lacing_fill==vals) + header[5]|=0x04; + b_o_s=1; + + /* 64 bits of PCM position */ + for(i=6; i<14; i++){ + header[i]=(byte)granule_pos; + granule_pos>>>=8; + } + + /* 32 bits of stream serial number */ + { + int _serialno=serialno; + for(i=14; i<18; i++){ + header[i]=(byte)_serialno; + _serialno>>>=8; + } + } + + /* 32 bits of page counter (we have both counter and page header + because this val can roll over) */ + if(pageno==-1) + pageno=0; /* because someone called + stream_reset; this would be a + strange thing to do in an + encode stream, but it has + plausible uses */ + { + int _pageno=pageno++; + for(i=18; i<22; i++){ + header[i]=(byte)_pageno; + _pageno>>>=8; + } + } + + /* zero for computation; filled in later */ + header[22]=0; + header[23]=0; + header[24]=0; + header[25]=0; + + /* segment table */ + header[26]=(byte)vals; + for(i=0; i4096|| /* 'page nominal size' case */ + lacing_fill>=255|| /* 'segment table full' case */ + (lacing_fill!=0&&b_o_s==0)){ /* 'initial header page' case */ + return flush(og); + } + return 0; + } + + public int eof(){ + return e_o_s; + } + + public int reset(){ + body_fill=0; + body_returned=0; + + lacing_fill=0; + lacing_packet=0; + lacing_returned=0; + + header_fill=0; + + e_o_s=0; + b_o_s=0; + pageno=-1; + packetno=0; + granulepos=0; + return (0); + } +} diff --git a/common/src/main/java/eu/midnightdust/picturesign_theora/ogv/jogg/OggSyncState.java b/common/src/main/java/eu/midnightdust/picturesign_theora/ogv/jogg/OggSyncState.java new file mode 100644 index 0000000..e8bc17a --- /dev/null +++ b/common/src/main/java/eu/midnightdust/picturesign_theora/ogv/jogg/OggSyncState.java @@ -0,0 +1,275 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* JOrbis + * Copyright (C) 2000 ymnk, JCraft,Inc. + * + * Written by: 2000 ymnk + * + * Many thanks to + * Monty and + * The XIPHOPHORUS Company http://www.xiph.org/ . + * JOrbis has been based on their awesome works, Vorbis codec. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public License + * as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +package eu.midnightdust.picturesign_theora.ogv.jogg; + +// DECODING PRIMITIVES: packet streaming layer + +// This has two layers to place more of the multi-serialno and paging +// control in the application's hands. First, we expose a data buffer +// using ogg_decode_buffer(). The app either copies into the +// buffer, or passes it directly to read(), etc. We then call +// ogg_decode_wrote() to tell how many bytes we just added. +// +// Pages are returned (pointers into the buffer in ogg_sync_state) +// by ogg_decode_stream(). The page is then submitted to +// ogg_decode_page() along with the appropriate +// ogg_stream_state* (ie, matching serialno). We then get raw +// packets out calling ogg_stream_packet() with a +// ogg_stream_state. See the 'frame-prog.txt' docs for details and +// example code. + +public class OggSyncState{ + + public byte[] data; + int storage; + int fill; + int returned; + + int unsynced; + int headerbytes; + int bodybytes; + + public int clear(){ + data=null; + return (0); + } + + public int buffer(int size){ + // first, clear out any space that has been previously returned + if(returned!=0){ + fill-=returned; + if(fill>0){ + System.arraycopy(data, returned, data, 0, fill); + } + returned=0; + } + + if(size>storage-fill){ + // We need to extend the internal buffer + int newsize=size+fill+4096; // an extra page to be nice + if(data!=null){ + byte[] foo=new byte[newsize]; + System.arraycopy(data, 0, foo, 0, data.length); + data=foo; + } + else{ + data=new byte[newsize]; + } + storage=newsize; + } + + return (fill); + } + + public int wrote(int bytes){ + if(fill+bytes>storage) + return (-1); + fill+=bytes; + return (0); + } + + // sync the stream. This is meant to be useful for finding page + // boundaries. + // + // return values for this: + // -n) skipped n bytes + // 0) page not ready; more data (no bytes skipped) + // n) page synced at current location; page length n bytes + private OggPage pageseek=new OggPage(); + private byte[] chksum=new byte[4]; + + public int pageseek(OggPage og){ + int page=returned; + int next; + int bytes=fill-returned; + + if(headerbytes==0){ + int _headerbytes, i; + if(bytes<27) + return (0); // not enough for a header + + /* verify capture pattern */ + if(data[page]!='O'||data[page+1]!='g'||data[page+2]!='g' + ||data[page+3]!='S'){ + headerbytes=0; + bodybytes=0; + + // search for possible capture + next=0; + for(int ii=0; iibytes) + return (0); + + // The whole test page is buffered. Verify the checksum + synchronized(chksum){ + // Grab the checksum bytes, set the header field to zero + + System.arraycopy(data, page+22, chksum, 0, 4); + data[page+22]=0; + data[page+23]=0; + data[page+24]=0; + data[page+25]=0; + + // set up a temp page struct and recompute the checksum + OggPage log=pageseek; + log.header_base=data; + log.header=page; + log.header_len=headerbytes; + + log.body_base=data; + log.body=page+headerbytes; + log.body_len=bodybytes; + log.checksum(); + + // Compare + if(chksum[0]!=data[page+22]||chksum[1]!=data[page+23] + ||chksum[2]!=data[page+24]||chksum[3]!=data[page+25]){ + // D'oh. Mismatch! Corrupt page (or miscapture and not a page at all) + // replace the computed checksum with the one actually read in + System.arraycopy(chksum, 0, data, page+22, 4); + // Bad checksum. Lose sync */ + + headerbytes=0; + bodybytes=0; + // search for possible capture + next=0; + for(int ii=0; ii0){ + // have a page + return (1); + } + if(ret==0){ + // need more data + return (0); + } + + // head did not start a synced page... skipped some bytes + if(unsynced==0){ + unsynced=1; + return (-1); + } + // loop. keep looking + } + } + + // clear things to an initial state. Good to call, eg, before seeking + public int reset(){ + fill=0; + returned=0; + unsynced=0; + headerbytes=0; + bodybytes=0; + return (0); + } + + public void init(){ + } + + public int getDataOffset(){ + return returned; + } + + public int getBufferOffset(){ + return fill; + } +} diff --git a/common/src/main/java/eu/midnightdust/picturesign_theora/ogv/jorbis/CodeBook.java b/common/src/main/java/eu/midnightdust/picturesign_theora/ogv/jorbis/CodeBook.java new file mode 100644 index 0000000..8c255aa --- /dev/null +++ b/common/src/main/java/eu/midnightdust/picturesign_theora/ogv/jorbis/CodeBook.java @@ -0,0 +1,478 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* JOrbis + * Copyright (C) 2000 ymnk, JCraft,Inc. + * + * Written by: 2000 ymnk + * + * Many thanks to + * Monty and + * The XIPHOPHORUS Company http://www.xiph.org/ . + * JOrbis has been based on their awesome works, Vorbis codec. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public License + * as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +package eu.midnightdust.picturesign_theora.ogv.jorbis; + +import eu.midnightdust.picturesign_theora.ogv.jogg.*; + +class CodeBook{ + int dim; // codebook dimensions (elements per vector) + int entries; // codebook entries + StaticCodeBook c=new StaticCodeBook(); + + float[] valuelist; // list of dim*entries actual entry values + int[] codelist; // list of bitstream codewords for each entry + DecodeAux decode_tree; + + // returns the number of bits + int encode(int a, OggBuffer b){ + b.write(codelist[a], c.lengthlist[a]); + return (c.lengthlist[a]); + } + + // One the encode side, our vector writers are each designed for a + // specific purpose, and the encoder is not flexible without modification: + // + // The LSP vector coder uses a single stage nearest-match with no + // interleave, so no step and no error return. This is specced by floor0 + // and doesn't change. + // + // Residue0 encoding interleaves, uses multiple stages, and each stage + // peels of a specific amount of resolution from a lattice (thus we want + // to match by threshhold, not nearest match). Residue doesn't *have* to + // be encoded that way, but to change it, one will need to add more + // infrastructure on the encode side (decode side is specced and simpler) + + // floor0 LSP (single stage, non interleaved, nearest match) + // returns entry number and *modifies a* to the quantization value + int errorv(float[] a){ + int best=best(a, 1); + for(int k=0; k8){ + for(i=0; i declarative (set the value) + // stage==1 -> additive + // stage==2 -> multiplicitive + + // returns the entry number or -1 on eof + int decode(OggBuffer b){ + int ptr=0; + DecodeAux t=decode_tree; + int lok=b.look(t.tabn); + + if(lok>=0){ + ptr=t.tab[lok]; + b.adv(t.tabl[lok]); + if(ptr<=0){ + return -ptr; + } + } + do{ + switch(b.read1()){ + case 0: + ptr=t.ptr0[ptr]; + break; + case 1: + ptr=t.ptr1[ptr]; + break; + case -1: + default: + return (-1); + } + } + while(ptr>0); + return (-ptr); + } + + // returns the entry number or -1 on eof + int decodevs(float[] a, int index, OggBuffer b, int step, int addmul){ + int entry=decode(b); + if(entry==-1) + return (-1); + switch(addmul){ + case -1: + for(int i=0, o=0; i0){ // also handles the -1 out of data case + int maxval=(1<0){ // also handles the -1 out of data case + int maxval=(1<m+1 must be less than l->ln, but guard in case we get a bad stream + float[] lcurve=new float[Math.max(l.ln*2, l.m*2+2)]; + + if(amp==0){ + for(int j=0; j + * + * Many thanks to + * Monty and + * The XIPHOPHORUS Company http://www.xiph.org/ . + * JOrbis has been based on their awesome works, Vorbis codec. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public License + * as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +package eu.midnightdust.picturesign_theora.ogv.jorbis; + +import eu.midnightdust.picturesign_theora.ogv.jogg.*; + +class Floor1 extends FuncFloor{ + static final int floor1_rangedb=140; + static final int VIF_POSIT=63; + + void pack(Object i, OggBuffer opb){ + InfoFloor1 info=(InfoFloor1)i; + + int count=0; + int rangebits; + int maxposit=info.postlist[1]; + int maxclass=-1; + + /* save out partitions */ + opb.write(info.partitions, 5); /* only 0 to 31 legal */ + for(int j=0; j=vi.books){ + info.free(); + return (null); + } + for(int k=0; k<(1<=vi.books){ + info.free(); + return (null); + } + } + } + + /* read the post list */ + info.mult=opb.read(2)+1; /* only 1,2,3,4 legal now */ + rangebits=opb.read(4); + + for(int j=0, k=0; j=(1<info.postlist[sortpointer[k]]){ + foo=sortpointer[k]; + sortpointer[k]=sortpointer[j]; + sortpointer[j]=foo; + } + } + } + + /* points from sort order back to range number */ + for(int j=0; j<_n; j++){ + look.forward_index[j]=sortpointer[j]; + } + /* points from range order to sorted position */ + for(int j=0; j<_n; j++){ + look.reverse_index[look.forward_index[j]]=j; + } + /* we actually need the post values too */ + for(int j=0; j<_n; j++){ + look.sorted_index[j]=info.postlist[look.forward_index[j]]; + } + + /* quantize values to multiplier spec */ + switch(info.mult){ + case 1: /* 1024 -> 256 */ + look.quant_q=256; + break; + case 2: /* 1024 -> 128 */ + look.quant_q=128; + break; + case 3: /* 1024 -> 86 */ + look.quant_q=86; + break; + case 4: /* 1024 -> 64 */ + look.quant_q=64; + break; + default: + look.quant_q=-1; + } + + /* discover our neighbors for decode where we don't use fit flags + (that would push the neighbors outward) */ + for(int j=0; j<_n-2; j++){ + int lo=0; + int hi=1; + int lx=0; + int hx=look.n; + int currentx=info.postlist[j+2]; + for(int k=0; klx&&xcurrentx){ + hi=k; + hx=x; + } + } + look.loneighbor[j]=lo; + look.hineighbor[j]=hi; + } + + return look; + } + + void free_info(Object i){ + } + + void free_look(Object i){ + } + + void free_state(Object vs){ + } + + int forward(VorbisBlock vb, Object i, float[] in, float[] out, Object vs){ + return 0; + } + + Object inverse1(VorbisBlock vb, Object ii, Object memo){ + LookFloor1 look=(LookFloor1)ii; + InfoFloor1 info=look.vi; + CodeBook[] books=vb.vd.fullbooks; + + /* unpack wrapped/predicted values from stream */ + if(vb.opb.read(1)==1){ + int[] fit_value=null; + if(memo instanceof int[]){ + fit_value=(int[])memo; + } + if(fit_value==null||fit_value.length>>=csubbits; + if(book>=0){ + if((fit_value[j+k]=books[book].decode(vb.opb))==-1){ + return (null); + } + } + else{ + fit_value[j+k]=0; + } + } + j+=cdim; + } + + /* unwrap positive values and reconsitute via linear interpolation */ + for(int i=2; i=room){ + if(hiroom>loroom){ + val=val-loroom; + } + else{ + val=-1-(val-hiroom); + } + } + else{ + if((val&1)!=0){ + val=-((val+1)>>>1); + } + else{ + val>>=1; + } + } + + fit_value[i]=val+predicted; + fit_value[look.loneighbor[i-2]]&=0x7fff; + fit_value[look.hineighbor[i-2]]&=0x7fff; + } + else{ + fit_value[i]=predicted|0x8000; + } + } + return (fit_value); + } + + return (null); + } + + private static int render_point(int x0, int x1, int y0, int y1, int x){ + y0&=0x7fff; /* mask off flag */ + y1&=0x7fff; + + { + int dy=y1-y0; + int adx=x1-x0; + int ady=Math.abs(dy); + int err=ady*(x-x0); + + int off=(int)(err/adx); + if(dy<0) + return (y0-off); + return (y0+off); + } + } + + int inverse2(VorbisBlock vb, Object i, Object memo, float[] out){ + LookFloor1 look=(LookFloor1)i; + InfoFloor1 info=look.vi; + int n=vb.vd.vi.blocksizes[vb.mode]/2; + + if(memo!=null){ + /* render the lines */ + int[] fit_value=(int[])memo; + int hx=0; + int lx=0; + int ly=fit_value[0]*info.mult; + for(int j=1; j=adx){ + err-=adx; + y+=sy; + } + else{ + y+=base; + } + d[x]*=FLOOR_fromdB_LOOKUP[y]; + } + } + + class InfoFloor1{ + static final int VIF_POSIT=63; + static final int VIF_CLASS=16; + static final int VIF_PARTS=31; + + int partitions; /* 0 to 31 */ + int[] partitionclass=new int[VIF_PARTS]; /* 0 to 15 */ + + int[] class_dim=new int[VIF_CLASS]; /* 1 to 8 */ + int[] class_subs=new int[VIF_CLASS]; /* 0,1,2,3 (bits: 1< + * + * Many thanks to + * Monty and + * The XIPHOPHORUS Company http://www.xiph.org/ . + * JOrbis has been based on their awesome works, Vorbis codec. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public License + * as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +package eu.midnightdust.picturesign_theora.ogv.jorbis; + +import eu.midnightdust.picturesign_theora.ogv.jogg.*; + +abstract class FuncFloor{ + + public static FuncFloor[] floor_P= {new Floor0(), new Floor1()}; + + abstract void pack(Object i, OggBuffer opb); + + abstract Object unpack(VorbisInfo vi, OggBuffer opb); + + abstract Object look(VorbisDspState vd, InfoMode mi, Object i); + + abstract void free_info(Object i); + + abstract void free_look(Object i); + + abstract void free_state(Object vs); + + abstract int forward(VorbisBlock vb, Object i, float[] in, float[] out, Object vs); + + abstract Object inverse1(VorbisBlock vb, Object i, Object memo); + + abstract int inverse2(VorbisBlock vb, Object i, Object memo, float[] out); +} diff --git a/common/src/main/java/eu/midnightdust/picturesign_theora/ogv/jorbis/FuncMapping.java b/common/src/main/java/eu/midnightdust/picturesign_theora/ogv/jorbis/FuncMapping.java new file mode 100644 index 0000000..8af3108 --- /dev/null +++ b/common/src/main/java/eu/midnightdust/picturesign_theora/ogv/jorbis/FuncMapping.java @@ -0,0 +1,45 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* JOrbis + * Copyright (C) 2000 ymnk, JCraft,Inc. + * + * Written by: 2000 ymnk + * + * Many thanks to + * Monty and + * The XIPHOPHORUS Company http://www.xiph.org/ . + * JOrbis has been based on their awesome works, Vorbis codec. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public License + * as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +package eu.midnightdust.picturesign_theora.ogv.jorbis; + +import eu.midnightdust.picturesign_theora.ogv.jogg.*; + +abstract class FuncMapping{ + public static FuncMapping[] mapping_P= {new Mapping0()}; + + abstract void pack(VorbisInfo info, Object imap, OggBuffer buffer); + + abstract Object unpack(VorbisInfo info, OggBuffer buffer); + + abstract Object look(VorbisDspState vd, InfoMode vm, Object m); + + abstract void free_info(Object imap); + + abstract void free_look(Object imap); + + abstract int inverse(VorbisBlock vd, Object lm); +} diff --git a/common/src/main/java/eu/midnightdust/picturesign_theora/ogv/jorbis/FuncResidue.java b/common/src/main/java/eu/midnightdust/picturesign_theora/ogv/jorbis/FuncResidue.java new file mode 100644 index 0000000..f002dfd --- /dev/null +++ b/common/src/main/java/eu/midnightdust/picturesign_theora/ogv/jorbis/FuncResidue.java @@ -0,0 +1,46 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* JOrbis + * Copyright (C) 2000 ymnk, JCraft,Inc. + * + * Written by: 2000 ymnk + * + * Many thanks to + * Monty and + * The XIPHOPHORUS Company http://www.xiph.org/ . + * JOrbis has been based on their awesome works, Vorbis codec. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public License + * as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +package eu.midnightdust.picturesign_theora.ogv.jorbis; + +import eu.midnightdust.picturesign_theora.ogv.jogg.*; + +abstract class FuncResidue{ + public static FuncResidue[] residue_P= {new Residue0(), new Residue1(), + new Residue2()}; + + abstract void pack(Object vr, OggBuffer opb); + + abstract Object unpack(VorbisInfo vi, OggBuffer opb); + + abstract Object look(VorbisDspState vd, InfoMode vm, Object vr); + + abstract void free_info(Object i); + + abstract void free_look(Object i); + + abstract int inverse(VorbisBlock vb, Object vl, float[][] in, int[] nonzero, int ch); +} diff --git a/common/src/main/java/eu/midnightdust/picturesign_theora/ogv/jorbis/FuncTime.java b/common/src/main/java/eu/midnightdust/picturesign_theora/ogv/jorbis/FuncTime.java new file mode 100644 index 0000000..3c28244 --- /dev/null +++ b/common/src/main/java/eu/midnightdust/picturesign_theora/ogv/jorbis/FuncTime.java @@ -0,0 +1,45 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* JOrbis + * Copyright (C) 2000 ymnk, JCraft,Inc. + * + * Written by: 2000 ymnk + * + * Many thanks to + * Monty and + * The XIPHOPHORUS Company http://www.xiph.org/ . + * JOrbis has been based on their awesome works, Vorbis codec. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public License + * as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +package eu.midnightdust.picturesign_theora.ogv.jorbis; + +import eu.midnightdust.picturesign_theora.ogv.jogg.*; + +abstract class FuncTime{ + public static FuncTime[] time_P= {new Time0()}; + + abstract void pack(Object i, OggBuffer opb); + + abstract Object unpack(VorbisInfo vi, OggBuffer opb); + + abstract Object look(VorbisDspState vd, InfoMode vm, Object i); + + abstract void free_info(Object i); + + abstract void free_look(Object i); + + abstract int inverse(VorbisBlock vb, Object i, float[] in, float[] out); +} diff --git a/common/src/main/java/eu/midnightdust/picturesign_theora/ogv/jorbis/InfoMode.java b/common/src/main/java/eu/midnightdust/picturesign_theora/ogv/jorbis/InfoMode.java new file mode 100644 index 0000000..4943f8d --- /dev/null +++ b/common/src/main/java/eu/midnightdust/picturesign_theora/ogv/jorbis/InfoMode.java @@ -0,0 +1,34 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* JOrbis + * Copyright (C) 2000 ymnk, JCraft,Inc. + * + * Written by: 2000 ymnk + * + * Many thanks to + * Monty and + * The XIPHOPHORUS Company http://www.xiph.org/ . + * JOrbis has been based on their awesome works, Vorbis codec. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public License + * as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +package eu.midnightdust.picturesign_theora.ogv.jorbis; + +class InfoMode{ + int blockflag; + int windowtype; + int transformtype; + int mapping; +} diff --git a/common/src/main/java/eu/midnightdust/picturesign_theora/ogv/jorbis/JOrbisException.java b/common/src/main/java/eu/midnightdust/picturesign_theora/ogv/jorbis/JOrbisException.java new file mode 100644 index 0000000..5a5010a --- /dev/null +++ b/common/src/main/java/eu/midnightdust/picturesign_theora/ogv/jorbis/JOrbisException.java @@ -0,0 +1,40 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* JOrbis + * Copyright (C) 2000 ymnk, JCraft,Inc. + * + * Written by: 2000 ymnk + * + * Many thanks to + * Monty and + * The XIPHOPHORUS Company http://www.xiph.org/ . + * JOrbis has been based on their awesome works, Vorbis codec. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public License + * as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +package eu.midnightdust.picturesign_theora.ogv.jorbis; + +public class JOrbisException extends Exception{ + + private static final long serialVersionUID=1L; + + public JOrbisException(){ + super(); + } + + public JOrbisException(String s){ + super("JOrbis: "+s); + } +} diff --git a/common/src/main/java/eu/midnightdust/picturesign_theora/ogv/jorbis/Lookup.java b/common/src/main/java/eu/midnightdust/picturesign_theora/ogv/jorbis/Lookup.java new file mode 100644 index 0000000..8075c60 --- /dev/null +++ b/common/src/main/java/eu/midnightdust/picturesign_theora/ogv/jorbis/Lookup.java @@ -0,0 +1,152 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* JOrbis + * Copyright (C) 2000 ymnk, JCraft,Inc. + * + * Written by: 2000 ymnk + * + * Many thanks to + * Monty and + * The XIPHOPHORUS Company http://www.xiph.org/ . + * JOrbis has been based on their awesome works, Vorbis codec. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public License + * as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +package eu.midnightdust.picturesign_theora.ogv.jorbis; + +class Lookup{ + static final int COS_LOOKUP_SZ=128; + static final float[] COS_LOOKUP= {+1.0000000000000f, +0.9996988186962f, + +0.9987954562052f, +0.9972904566787f, +0.9951847266722f, + +0.9924795345987f, +0.9891765099648f, +0.9852776423889f, + +0.9807852804032f, +0.9757021300385f, +0.9700312531945f, + +0.9637760657954f, +0.9569403357322f, +0.9495281805930f, + +0.9415440651830f, +0.9329927988347f, +0.9238795325113f, + +0.9142097557035f, +0.9039892931234f, +0.8932243011955f, + +0.8819212643484f, +0.8700869911087f, +0.8577286100003f, + +0.8448535652497f, +0.8314696123025f, +0.8175848131516f, + +0.8032075314806f, +0.7883464276266f, +0.7730104533627f, + +0.7572088465065f, +0.7409511253550f, +0.7242470829515f, + +0.7071067811865f, +0.6895405447371f, +0.6715589548470f, + +0.6531728429538f, +0.6343932841636f, +0.6152315905806f, + +0.5956993044924f, +0.5758081914178f, +0.5555702330196f, + +0.5349976198871f, +0.5141027441932f, +0.4928981922298f, + +0.4713967368260f, +0.4496113296546f, +0.4275550934303f, + +0.4052413140050f, +0.3826834323651f, +0.3598950365350f, + +0.3368898533922f, +0.3136817403989f, +0.2902846772545f, + +0.2667127574749f, +0.2429801799033f, +0.2191012401569f, + +0.1950903220161f, +0.1709618887603f, +0.1467304744554f, + +0.1224106751992f, +0.0980171403296f, +0.0735645635997f, + +0.0490676743274f, +0.0245412285229f, +0.0000000000000f, + -0.0245412285229f, -0.0490676743274f, -0.0735645635997f, + -0.0980171403296f, -0.1224106751992f, -0.1467304744554f, + -0.1709618887603f, -0.1950903220161f, -0.2191012401569f, + -0.2429801799033f, -0.2667127574749f, -0.2902846772545f, + -0.3136817403989f, -0.3368898533922f, -0.3598950365350f, + -0.3826834323651f, -0.4052413140050f, -0.4275550934303f, + -0.4496113296546f, -0.4713967368260f, -0.4928981922298f, + -0.5141027441932f, -0.5349976198871f, -0.5555702330196f, + -0.5758081914178f, -0.5956993044924f, -0.6152315905806f, + -0.6343932841636f, -0.6531728429538f, -0.6715589548470f, + -0.6895405447371f, -0.7071067811865f, -0.7242470829515f, + -0.7409511253550f, -0.7572088465065f, -0.7730104533627f, + -0.7883464276266f, -0.8032075314806f, -0.8175848131516f, + -0.8314696123025f, -0.8448535652497f, -0.8577286100003f, + -0.8700869911087f, -0.8819212643484f, -0.8932243011955f, + -0.9039892931234f, -0.9142097557035f, -0.9238795325113f, + -0.9329927988347f, -0.9415440651830f, -0.9495281805930f, + -0.9569403357322f, -0.9637760657954f, -0.9700312531945f, + -0.9757021300385f, -0.9807852804032f, -0.9852776423889f, + -0.9891765099648f, -0.9924795345987f, -0.9951847266722f, + -0.9972904566787f, -0.9987954562052f, -0.9996988186962f, + -1.0000000000000f,}; + + /* interpolated lookup based cos function, domain 0 to PI only */ + static float coslook(float a){ + double d=a*(.31830989*(float)COS_LOOKUP_SZ); + int i=(int)d; + return COS_LOOKUP[i]+((float)(d-i))*(COS_LOOKUP[i+1]-COS_LOOKUP[i]); + } + + static final int INVSQ_LOOKUP_SZ=32; + static final float[] INVSQ_LOOKUP= {1.414213562373f, 1.392621247646f, + 1.371988681140f, 1.352246807566f, 1.333333333333f, 1.315191898443f, + 1.297771369046f, 1.281025230441f, 1.264911064067f, 1.249390095109f, + 1.234426799697f, 1.219988562661f, 1.206045378311f, 1.192569588000f, + 1.179535649239f, 1.166919931983f, 1.154700538379f, 1.142857142857f, + 1.131370849898f, 1.120224067222f, 1.109400392450f, 1.098884511590f, + 1.088662107904f, 1.078719779941f, 1.069044967650f, 1.059625885652f, + 1.050451462878f, 1.041511287847f, 1.032795558989f, 1.024295039463f, + 1.016001016002f, 1.007905261358f, 1.000000000000f,}; + + /* interpolated 1./sqrt(p) where .5 <= p < 1. */ + static float invsqlook(float a){ + double d=a*(2.f*(float)INVSQ_LOOKUP_SZ)-(float)INVSQ_LOOKUP_SZ; + int i=(int)d; + return INVSQ_LOOKUP[i]+((float)(d-i))*(INVSQ_LOOKUP[i+1]-INVSQ_LOOKUP[i]); + } + + static final int INVSQ2EXP_LOOKUP_MIN=-32; + static final int INVSQ2EXP_LOOKUP_MAX=32; + static final float[] INVSQ2EXP_LOOKUP= {65536.f, 46340.95001f, 32768.f, + 23170.47501f, 16384.f, 11585.2375f, 8192.f, 5792.618751f, 4096.f, + 2896.309376f, 2048.f, 1448.154688f, 1024.f, 724.0773439f, 512.f, + 362.038672f, 256.f, 181.019336f, 128.f, 90.50966799f, 64.f, 45.254834f, + 32.f, 22.627417f, 16.f, 11.3137085f, 8.f, 5.656854249f, 4.f, + 2.828427125f, 2.f, 1.414213562f, 1.f, 0.7071067812f, 0.5f, 0.3535533906f, + 0.25f, 0.1767766953f, 0.125f, 0.08838834765f, 0.0625f, 0.04419417382f, + 0.03125f, 0.02209708691f, 0.015625f, 0.01104854346f, 0.0078125f, + 0.005524271728f, 0.00390625f, 0.002762135864f, 0.001953125f, + 0.001381067932f, 0.0009765625f, 0.000690533966f, 0.00048828125f, + 0.000345266983f, 0.000244140625f, 0.0001726334915f, 0.0001220703125f, + 8.631674575e-05f, 6.103515625e-05f, 4.315837288e-05f, 3.051757812e-05f, + 2.157918644e-05f, 1.525878906e-05f,}; + + /* interpolated 1./sqrt(p) where .5 <= p < 1. */ + static float invsq2explook(int a){ + return INVSQ2EXP_LOOKUP[a-INVSQ2EXP_LOOKUP_MIN]; + } + + static final int FROMdB_LOOKUP_SZ=35; + static final int FROMdB2_LOOKUP_SZ=32; + static final int FROMdB_SHIFT=5; + static final int FROMdB2_SHIFT=3; + static final int FROMdB2_MASK=31; + static final float[] FROMdB_LOOKUP= {1.f, 0.6309573445f, 0.3981071706f, + 0.2511886432f, 0.1584893192f, 0.1f, 0.06309573445f, 0.03981071706f, + 0.02511886432f, 0.01584893192f, 0.01f, 0.006309573445f, 0.003981071706f, + 0.002511886432f, 0.001584893192f, 0.001f, 0.0006309573445f, + 0.0003981071706f, 0.0002511886432f, 0.0001584893192f, 0.0001f, + 6.309573445e-05f, 3.981071706e-05f, 2.511886432e-05f, 1.584893192e-05f, + 1e-05f, 6.309573445e-06f, 3.981071706e-06f, 2.511886432e-06f, + 1.584893192e-06f, 1e-06f, 6.309573445e-07f, 3.981071706e-07f, + 2.511886432e-07f, 1.584893192e-07f,}; + static final float[] FROMdB2_LOOKUP= {0.9928302478f, 0.9786445908f, + 0.9646616199f, 0.9508784391f, 0.9372921937f, 0.92390007f, 0.9106992942f, + 0.8976871324f, 0.8848608897f, 0.8722179097f, 0.8597555737f, + 0.8474713009f, 0.835362547f, 0.8234268041f, 0.8116616003f, 0.8000644989f, + 0.7886330981f, 0.7773650302f, 0.7662579617f, 0.755309592f, 0.7445176537f, + 0.7338799116f, 0.7233941627f, 0.7130582353f, 0.7028699885f, + 0.6928273125f, 0.6829281272f, 0.6731703824f, 0.6635520573f, + 0.6540711597f, 0.6447257262f, 0.6355138211f,}; + + /* interpolated lookup based fromdB function, domain -140dB to 0dB only */ + static float fromdBlook(float a){ + int i=(int)(a*((float)(-(1<=(FROMdB_LOOKUP_SZ<>>FROMdB_SHIFT]*FROMdB2_LOOKUP[i&FROMdB2_MASK]); + } + +} diff --git a/common/src/main/java/eu/midnightdust/picturesign_theora/ogv/jorbis/Lpc.java b/common/src/main/java/eu/midnightdust/picturesign_theora/ogv/jorbis/Lpc.java new file mode 100644 index 0000000..29fe87d --- /dev/null +++ b/common/src/main/java/eu/midnightdust/picturesign_theora/ogv/jorbis/Lpc.java @@ -0,0 +1,188 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* JOrbis + * Copyright (C) 2000 ymnk, JCraft,Inc. + * + * Written by: 2000 ymnk + * + * Many thanks to + * Monty and + * The XIPHOPHORUS Company http://www.xiph.org/ . + * JOrbis has been based on their awesome works, Vorbis codec. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public License + * as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +package eu.midnightdust.picturesign_theora.ogv.jorbis; + +class Lpc{ + // en/decode lookups + Drft fft=new Drft();; + + int ln; + int m; + + // Autocorrelation LPC coeff generation algorithm invented by + // N. Levinson in 1947, modified by J. Durbin in 1959. + + // Input : n elements of time doamin data + // Output: m lpc coefficients, excitation energy + + static float lpc_from_data(float[] data, float[] lpc, int n, int m){ + float[] aut=new float[m+1]; + float error; + int i, j; + + // autocorrelation, p+1 lag coefficients + + j=m+1; + while(j--!=0){ + float d=0; + for(i=j; i + * + * Many thanks to + * Monty and + * The XIPHOPHORUS Company http://www.xiph.org/ . + * JOrbis has been based on their awesome works, Vorbis codec. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public License + * as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +package eu.midnightdust.picturesign_theora.ogv.jorbis; + +/* + function: LSP (also called LSF) conversion routines + + The LSP generation code is taken (with minimal modification) from + "On the Computation of the LSP Frequencies" by Joseph Rothweiler + , available at: + + http://www2.xtdl.com/~rothwlr/lsfpaper/lsfpage.html + ********************************************************************/ + +class Lsp{ + + static final float M_PI=(float)(3.1415926539); + + static void lsp_to_curve(float[] curve, int[] map, int n, int ln, + float[] lsp, int m, float amp, float ampoffset){ + int i; + float wdel=M_PI/ln; + for(i=0; i=0x7f800000||(ix==0)){ + // 0,inf,nan + } + else{ + if(ix<0x00800000){ // subnormal + q*=3.3554432000e+07; // 0x4c000000 + hx=Float.floatToIntBits(q); + ix=0x7fffffff&hx; + qexp=-25; + } + qexp+=((ix>>>23)-126); + hx=(hx&0x807fffff)|0x3f000000; + q=Float.intBitsToFloat(hx); + } + + q=Lookup.fromdBlook(amp*Lookup.invsqlook(q)*Lookup.invsq2explook(qexp+m) + -ampoffset); + + do{ + curve[i++]*=q; + } + while(i + * + * Many thanks to + * Monty and + * The XIPHOPHORUS Company http://www.xiph.org/ . + * JOrbis has been based on their awesome works, Vorbis codec. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public License + * as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +package eu.midnightdust.picturesign_theora.ogv.jorbis; + +import eu.midnightdust.picturesign_theora.ogv.jogg.*; + +class Mapping0 extends FuncMapping{ + static int seq=0; + + void free_info(Object imap){ + }; + + void free_look(Object imap){ + } + + Object look(VorbisDspState vd, InfoMode vm, Object m){ + //System.err.println("Mapping0.look"); + VorbisInfo vi=vd.vi; + LookMapping0 look=new LookMapping0(); + InfoMapping0 info=look.map=(InfoMapping0)m; + look.mode=vm; + + look.time_look=new Object[info.submaps]; + look.floor_look=new Object[info.submaps]; + look.residue_look=new Object[info.submaps]; + + look.time_func=new FuncTime[info.submaps]; + look.floor_func=new FuncFloor[info.submaps]; + look.residue_func=new FuncResidue[info.submaps]; + + for(int i=0; i1){ + opb.write(1, 1); + opb.write(info.submaps-1, 4); + } + else{ + opb.write(0, 1); + } + + if(info.coupling_steps>0){ + opb.write(1, 1); + opb.write(info.coupling_steps-1, 8); + for(int i=0; i1){ + for(int i=0; i=vi.channels + ||testA>=vi.channels){ + //goto err_out; + info.free(); + return (null); + } + } + } + + if(opb.read(2)>0){ /* 2,3:reserved */ + info.free(); + return (null); + } + + if(info.submaps>1){ + for(int i=0; i=info.submaps){ + info.free(); + return (null); + } + } + } + + for(int i=0; i=vi.times){ + info.free(); + return (null); + } + info.floorsubmap[i]=opb.read(8); + if(info.floorsubmap[i]>=vi.floors){ + info.free(); + return (null); + } + info.residuesubmap[i]=opb.read(8); + if(info.residuesubmap[i]>=vi.residues){ + info.free(); + return (null); + } + } + return info; + } + + float[][] pcmbundle=null; + int[] zerobundle=null; + int[] nonzero=null; + Object[] floormemo=null; + + synchronized int inverse(VorbisBlock vb, Object l){ + VorbisDspState vd=vb.vd; + VorbisInfo vi=vd.vi; + LookMapping0 look=(LookMapping0)l; + InfoMapping0 info=look.map; + InfoMode mode=look.mode; + int n=vb.pcmend=vi.blocksizes[vb.W]; + + float[] window=vd.window[vb.W][vb.lW][vb.nW][mode.windowtype]; + if(pcmbundle==null||pcmbundle.length=0; i--){ + float[] pcmM=vb.pcm[info.coupling_mag[i]]; + float[] pcmA=vb.pcm[info.coupling_ang[i]]; + + for(int j=0; j0){ + if(ang>0){ + pcmM[j]=mag; + pcmA[j]=mag-ang; + } + else{ + pcmA[j]=mag; + pcmM[j]=mag+ang; + } + } + else{ + if(ang>0){ + pcmM[j]=mag; + pcmA[j]=mag+ang; + } + else{ + pcmA[j]=mag; + pcmM[j]=mag-ang; + } + } + } + } + + // /* compute and apply spectral envelope */ + + for(int i=0; i + * + * Many thanks to + * Monty and + * The XIPHOPHORUS Company http://www.xiph.org/ . + * JOrbis has been based on their awesome works, Vorbis codec. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public License + * as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +package eu.midnightdust.picturesign_theora.ogv.jorbis; + +class Mdct{ + + int n; + int log2n; + + float[] trig; + int[] bitrev; + + float scale; + + void init(int n){ + bitrev=new int[n/4]; + trig=new float[n+n/4]; + + log2n=(int)Math.rint(Math.log(n)/Math.log(2)); + this.n=n; + + int AE=0; + int AO=1; + int BE=AE+n/2; + int BO=BE+1; + int CE=BE+n/2; + int CO=CE+1; + // trig lookups... + for(int i=0; i>>j!=0; j++) + if(((msb>>>j)&i)!=0) + acc|=1<>>1; + int n4=n>>>2; + int n8=n>>>3; + + // rotate + step 1 + { + int inO=1; + int xO=0; + int A=n2; + + int i; + for(i=0; i>>(i+2); + int k1=1<<(i+3); + int wbase=n2-2; + + A=0; + float[] temp; + + for(int r=0; r<(k0>>>2); r++){ + int w1=wbase; + w2=w1-(k0>>1); + float AEv=trig[A], wA; + float AOv=trig[A+1], wB; + wbase-=2; + + k0++; + for(int s=0; s<(2< + * + * Many thanks to + * Monty and + * The XIPHOPHORUS Company http://www.xiph.org/ . + * JOrbis has been based on their awesome works, Vorbis codec. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public License + * as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +package eu.midnightdust.picturesign_theora.ogv.jorbis; + +// psychoacoustic setup +class PsyInfo{ + int athp; + int decayp; + int smoothp; + int noisefitp; + int noisefit_subblock; + float noisefit_threshdB; + + float ath_att; + + int tonemaskp; + float[] toneatt_125Hz=new float[5]; + float[] toneatt_250Hz=new float[5]; + float[] toneatt_500Hz=new float[5]; + float[] toneatt_1000Hz=new float[5]; + float[] toneatt_2000Hz=new float[5]; + float[] toneatt_4000Hz=new float[5]; + float[] toneatt_8000Hz=new float[5]; + + int peakattp; + float[] peakatt_125Hz=new float[5]; + float[] peakatt_250Hz=new float[5]; + float[] peakatt_500Hz=new float[5]; + float[] peakatt_1000Hz=new float[5]; + float[] peakatt_2000Hz=new float[5]; + float[] peakatt_4000Hz=new float[5]; + float[] peakatt_8000Hz=new float[5]; + + int noisemaskp; + float[] noiseatt_125Hz=new float[5]; + float[] noiseatt_250Hz=new float[5]; + float[] noiseatt_500Hz=new float[5]; + float[] noiseatt_1000Hz=new float[5]; + float[] noiseatt_2000Hz=new float[5]; + float[] noiseatt_4000Hz=new float[5]; + float[] noiseatt_8000Hz=new float[5]; + + float max_curve_dB; + + float attack_coeff; + float decay_coeff; + + void free(){ + } +} diff --git a/common/src/main/java/eu/midnightdust/picturesign_theora/ogv/jorbis/PsyLook.java b/common/src/main/java/eu/midnightdust/picturesign_theora/ogv/jorbis/PsyLook.java new file mode 100644 index 0000000..aa6be5e --- /dev/null +++ b/common/src/main/java/eu/midnightdust/picturesign_theora/ogv/jorbis/PsyLook.java @@ -0,0 +1,42 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* JOrbis + * Copyright (C) 2000 ymnk, JCraft,Inc. + * + * Written by: 2000 ymnk + * + * Many thanks to + * Monty and + * The XIPHOPHORUS Company http://www.xiph.org/ . + * JOrbis has been based on their awesome works, Vorbis codec. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public License + * as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +package eu.midnightdust.picturesign_theora.ogv.jorbis; + +class PsyLook{ + int n; + PsyInfo vi; + + float[][][] tonecurves; + float[][] peakatt; + float[][][] noisecurves; + + float[] ath; + int[] octave; + + void init(PsyInfo vi, int n, int rate){ + } +} diff --git a/common/src/main/java/eu/midnightdust/picturesign_theora/ogv/jorbis/Residue0.java b/common/src/main/java/eu/midnightdust/picturesign_theora/ogv/jorbis/Residue0.java new file mode 100644 index 0000000..ee683f5 --- /dev/null +++ b/common/src/main/java/eu/midnightdust/picturesign_theora/ogv/jorbis/Residue0.java @@ -0,0 +1,330 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* JOrbis + * Copyright (C) 2000 ymnk, JCraft,Inc. + * + * Written by: 2000 ymnk + * + * Many thanks to + * Monty and + * The XIPHOPHORUS Company http://www.xiph.org/ . + * JOrbis has been based on their awesome works, Vorbis codec. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public License + * as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +package eu.midnightdust.picturesign_theora.ogv.jorbis; + +import eu.midnightdust.picturesign_theora.ogv.jogg.*; + +class Residue0 extends FuncResidue{ + void pack(Object vr, OggBuffer opb){ + InfoResidue0 info=(InfoResidue0)vr; + int acc=0; + opb.write(info.begin, 24); + opb.write(info.end, 24); + + opb.write(info.grouping-1, 24); /* residue vectors to group and + code with a partitioned book */ + opb.write(info.partitions-1, 6); /* possible partition choices */ + opb.write(info.groupbook, 8); /* group huffman book */ + + /* secondstages is a bitmask; as encoding progresses pass by pass, a + bitmask of one indicates this partition class has bits to write + this pass */ + for(int j=0; j3){ + /* yes, this is a minor hack due to not thinking ahead */ + opb.write(i, 3); + opb.write(1, 1); + opb.write(i>>>3, 5); + } + else{ + opb.write(i, 4); /* trailing zero */ + } + acc+=Util.icount(i); + } + for(int j=0; j=vi.books){ + free_info(info); + return (null); + } + + for(int j=0; j=vi.books){ + free_info(info); + return (null); + } + } + return (info); + } + + Object look(VorbisDspState vd, InfoMode vm, Object vr){ + InfoResidue0 info=(InfoResidue0)vr; + LookResidue0 look=new LookResidue0(); + int acc=0; + int dim; + int maxstage=0; + look.info=info; + look.map=vm.mapping; + + look.parts=info.partitions; + look.fullbooks=vd.fullbooks; + look.phrasebook=vd.fullbooks[info.groupbook]; + + dim=look.phrasebook.dim; + + look.partbooks=new int[look.parts][]; + + for(int j=0; jmaxstage) + maxstage=stages; + look.partbooks[j]=new int[stages]; + for(int k=0; k + * + * Many thanks to + * Monty and + * The XIPHOPHORUS Company http://www.xiph.org/ . + * JOrbis has been based on their awesome works, Vorbis codec. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public License + * as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +package eu.midnightdust.picturesign_theora.ogv.jorbis; + +class Residue1 extends Residue0{ + + int inverse(VorbisBlock vb, Object vl, float[][] in, int[] nonzero, int ch){ + int used=0; + for(int i=0; i + * + * Many thanks to + * Monty and + * The XIPHOPHORUS Company http://www.xiph.org/ . + * JOrbis has been based on their awesome works, Vorbis codec. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public License + * as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +package eu.midnightdust.picturesign_theora.ogv.jorbis; + +class Residue2 extends Residue0{ + + int inverse(VorbisBlock vb, Object vl, float[][] in, int[] nonzero, int ch){ + int i=0; + for(i=0; i + * + * Many thanks to + * Monty and + * The XIPHOPHORUS Company http://www.xiph.org/ . + * JOrbis has been based on their awesome works, Vorbis codec. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public License + * as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +package eu.midnightdust.picturesign_theora.ogv.jorbis; + +import eu.midnightdust.picturesign_theora.ogv.jogg.*; + +class StaticCodeBook{ + int dim; // codebook dimensions (elements per vector) + int entries; // codebook entries + int[] lengthlist; // codeword lengths in bits + + // mapping + int maptype; // 0=none + // 1=implicitly populated values from map column + // 2=listed arbitrary values + + // The below does a linear, single monotonic sequence mapping. + int q_min; // packed 32 bit float; quant value 0 maps to minval + int q_delta; // packed 32 bit float; val 1 - val 0 == delta + int q_quant; // bits: 0 < quant <= 16 + int q_sequencep; // bitflag + + // additional information for log (dB) mapping; the linear mapping + // is assumed to actually be values in dB. encodebias is used to + // assign an error weight to 0 dB. We have two additional flags: + // zeroflag indicates if entry zero is to represent -Inf dB; negflag + // indicates if we're to represent negative linear values in a + // mirror of the positive mapping. + + int[] quantlist; // map == 1: (int)(entries/dim) element column map + // map == 2: list of dim*entries quantized entry vals + + StaticCodeBook(){ + } + + int pack(OggBuffer opb){ + int i; + boolean ordered=false; + + opb.write(0x564342, 24); + opb.write(dim, 16); + opb.write(entries, 24); + + // pack the codewords. There are two packings; length ordered and + // length random. Decide between the two now. + + for(i=1; i_last){ + for(int j=_last; j<_this; j++){ + opb.write(i-count, Util.ilog(entries-count)); + count=i; + } + } + } + opb.write(i-count, Util.ilog(entries-count)); + } + else{ + // length random. Again, we don't code the codeword itself, just + // the length. This time, though, we have to encode each length + opb.write(0, 1); // unordered + + // algortihmic mapping has use for 'unused entries', which we tag + // here. The algorithmic mapping happens as usual, but the unused + // entry has no codeword. + for(i=0; ientries/c->dim) quantized values for + // building a full value list algorithmically (square lattice) + quantvals=maptype1_quantvals(); + break; + case 2: + // every value (c->entries*c->dim total) specified explicitly + quantvals=entries*dim; + break; + } + + // quantized values + for(i=0; ibim <= b->entries + // treat the above as an initial guess + while(true){ + int acc=1; + int acc1=1; + for(int i=0; ientries){ + return (vals); + } + else{ + if(acc>entries){ + vals--; + } + else{ + vals++; + } + } + } + } + + void clear(){ + } + + // unpack the quantized list of values for encode/decode + // we need to deal with two map types: in map type 1, the values are + // generated algorithmically (each column of the vector counts through + // the values in the quant vector). in map type 2, all the values came + // in in an explicit list. Both value lists must be unpacked + float[] unquantize(){ + + if(maptype==1||maptype==2){ + int quantvals; + float mindel=float32_unpack(q_min); + float delta=float32_unpack(q_delta); + float[] r=new float[entries*dim]; + + // maptype 1 and 2 both use a quantized value vector, but + // different sizes + switch(maptype){ + case 1: + // most of the time, entries%dimensions == 0, but we need to be + // well defined. We define that the possible vales at each + // scalar is values == entries/dim. If entries%dim != 0, we'll + // have 'too few' values (values*dim "+val+" | ");} + val=Math.abs(val)*delta+mindel+last; + if(q_sequencep!=0) + last=val; + r[j*dim+k]=val; + //if((j*dim+k)==0){System.err.println(" $ r[0] -> "+r[0]+" | ");} + } + } + //System.err.println("\nr[0]="+r[0]); + } + return (r); + } + return (null); + } + + // 32 bit float (not IEEE; nonnormalized mantissa + + // biased exponent) : neeeeeee eeemmmmm mmmmmmmm mmmmmmmm + // Why not IEEE? It's just not that important here. + + static final int VQ_FEXP=10; + static final int VQ_FMAN=21; + static final int VQ_FEXP_BIAS=768; // bias toward values smaller than 1. + + // doesn't currently guard under/overflow + static long float32_pack(float val){ + int sign=0; + int exp; + int mant; + if(val<0){ + sign=0x80000000; + val=-val; + } + exp=(int)Math.floor(Math.log(val)/Math.log(2)); + mant=(int)Math.rint(Math.pow(val, (VQ_FMAN-1)-exp)); + exp=(exp+VQ_FEXP_BIAS)<>>VQ_FMAN; + if((val&0x80000000)!=0) + mant=-mant; + return (ldexp(mant, ((int)exp)-(VQ_FMAN-1)-VQ_FEXP_BIAS)); + } + + static float ldexp(float foo, int e){ + return (float)(foo*Math.pow(2, e)); + } +} diff --git a/common/src/main/java/eu/midnightdust/picturesign_theora/ogv/jorbis/Time0.java b/common/src/main/java/eu/midnightdust/picturesign_theora/ogv/jorbis/Time0.java new file mode 100644 index 0000000..798c2bf --- /dev/null +++ b/common/src/main/java/eu/midnightdust/picturesign_theora/ogv/jorbis/Time0.java @@ -0,0 +1,52 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* JOrbis + * Copyright (C) 2000 ymnk, JCraft,Inc. + * + * Written by: 2000 ymnk + * + * Many thanks to + * Monty and + * The XIPHOPHORUS Company http://www.xiph.org/ . + * JOrbis has been based on their awesome works, Vorbis codec. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public License + * as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +package eu.midnightdust.picturesign_theora.ogv.jorbis; + +import eu.midnightdust.picturesign_theora.ogv.jogg.*; + +class Time0 extends FuncTime{ + void pack(Object i, OggBuffer opb){ + } + + Object unpack(VorbisInfo vi, OggBuffer opb){ + return ""; + } + + Object look(VorbisDspState vd, InfoMode mi, Object i){ + return ""; + } + + void free_info(Object i){ + } + + void free_look(Object i){ + } + + int inverse(VorbisBlock vb, Object i, float[] in, float[] out){ + return 0; + } +} diff --git a/common/src/main/java/eu/midnightdust/picturesign_theora/ogv/jorbis/Util.java b/common/src/main/java/eu/midnightdust/picturesign_theora/ogv/jorbis/Util.java new file mode 100644 index 0000000..56dd875 --- /dev/null +++ b/common/src/main/java/eu/midnightdust/picturesign_theora/ogv/jorbis/Util.java @@ -0,0 +1,30 @@ +package eu.midnightdust.picturesign_theora.ogv.jorbis; + +class Util{ + static int ilog(int v){ + int ret=0; + while(v!=0){ + ret++; + v>>>=1; + } + return (ret); + } + + static int ilog2(int v){ + int ret=0; + while(v>1){ + ret++; + v>>>=1; + } + return (ret); + } + + static int icount(int v){ + int ret=0; + while(v!=0){ + ret+=(v&1); + v>>>=1; + } + return (ret); + } +} diff --git a/common/src/main/java/eu/midnightdust/picturesign_theora/ogv/jorbis/VorbisBlock.java b/common/src/main/java/eu/midnightdust/picturesign_theora/ogv/jorbis/VorbisBlock.java new file mode 100644 index 0000000..4bef330 --- /dev/null +++ b/common/src/main/java/eu/midnightdust/picturesign_theora/ogv/jorbis/VorbisBlock.java @@ -0,0 +1,128 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* JOrbis + * Copyright (C) 2000 ymnk, JCraft,Inc. + * + * Written by: 2000 ymnk + * + * Many thanks to + * Monty and + * The XIPHOPHORUS Company http://www.xiph.org/ . + * JOrbis has been based on their awesome works, Vorbis codec. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public License + * as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +package eu.midnightdust.picturesign_theora.ogv.jorbis; + +import eu.midnightdust.picturesign_theora.ogv.jogg.*; + +public class VorbisBlock{ + ///necessary stream state for linking to the framing abstraction + float[][] pcm=new float[0][]; // this is a pointer into local storage + OggBuffer opb=new OggBuffer(); + + int lW; + int W; + int nW; + int pcmend; + int mode; + + int eofflag; + long granulepos; + long sequence; + VorbisDspState vd; // For read-only access of configuration + + // bitmetrics for the frame + int glue_bits; + int time_bits; + int floor_bits; + int res_bits; + + public VorbisBlock(VorbisDspState vd){ + this.vd=vd; + if(vd.analysisp!=0){ + opb.writeinit(); + } + } + + public void init(VorbisDspState vd){ + this.vd=vd; + } + + public int clear(){ + if(vd!=null){ + if(vd.analysisp!=0){ + opb.writeclear(); + } + } + return (0); + } + + public int synthesis(OggPacket op){ + VorbisInfo vi=vd.vi; + + // first things first. Make sure decode is ready + opb.readinit(op.packet_base, op.packet, op.bytes); + + // Check the packet type + if(opb.read(1)!=0){ + // Oops. This is not an audio data packet + return (-1); + } + + // read our mode and pre/post windowsize + int _mode=opb.read(vd.modebits); + if(_mode==-1) + return (-1); + + mode=_mode; + W=vi.mode_param[mode].blockflag; + if(W!=0){ + lW=opb.read(1); + nW=opb.read(1); + if(nW==-1) + return (-1); + } + else{ + lW=0; + nW=0; + } + + // more setup + granulepos=op.granulepos; + sequence=op.packetno-3; // first block is third packet + eofflag=op.e_o_s; + + // alloc pcm passback storage + pcmend=vi.blocksizes[W]; + if(pcm.length + * + * Many thanks to + * Monty and + * The XIPHOPHORUS Company http://www.xiph.org/ . + * JOrbis has been based on their awesome works, Vorbis codec. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public License + * as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +package eu.midnightdust.picturesign_theora.ogv.jorbis; + +import eu.midnightdust.picturesign_theora.ogv.jogg.*; + +// the comments are not part of vorbis_info so that vorbis_info can be +// static storage +public class VorbisComment{ + private static byte[] _vorbis="vorbis".getBytes(); + private static byte[] _vendor="Xiphophorus libVorbis I 20000508".getBytes(); + + private static final int OV_EIMPL=-130; + + // unlimited user comment fields. + public byte[][] user_comments; + public int[] comment_lengths; + public int comments; + public byte[] vendor; + + public void init(){ + user_comments=null; + comments=0; + vendor=null; + } + + public void add(String comment){ + add(comment.getBytes()); + } + + private void add(byte[] comment){ + byte[][] foo=new byte[comments+2][]; + if(user_comments!=null){ + System.arraycopy(user_comments, 0, foo, 0, comments); + } + user_comments=foo; + + int[] goo=new int[comments+2]; + if(comment_lengths!=null){ + System.arraycopy(comment_lengths, 0, goo, 0, comments); + } + comment_lengths=goo; + + byte[] bar=new byte[comment.length+1]; + System.arraycopy(comment, 0, bar, 0, comment.length); + user_comments[comments]=bar; + comment_lengths[comments]=comment.length; + comments++; + user_comments[comments]=null; + } + + public void add_tag(String tag, String contents){ + if(contents==null) + contents=""; + add(tag+"="+contents); + } + + static boolean tagcompare(byte[] s1, byte[] s2, int n){ + int c=0; + byte u1, u2; + while(c=u1&&u1>='A') + u1=(byte)(u1-'A'+'a'); + if('Z'>=u2&&u2>='A') + u2=(byte)(u2-'A'+'a'); + if(u1!=u2){ + return false; + } + c++; + } + return true; + } + + public String query(String tag){ + return query(tag, 0); + } + + public String query(String tag, int count){ + int foo=query(tag.getBytes(), count); + if(foo==-1) + return null; + byte[] comment=user_comments[foo]; + for(int i=0; i + * + * Many thanks to + * Monty and + * The XIPHOPHORUS Company http://www.xiph.org/ . + * JOrbis has been based on their awesome works, Vorbis codec. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public License + * as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +package eu.midnightdust.picturesign_theora.ogv.jorbis; + +public class VorbisDspState{ + static final float M_PI=3.1415926539f; + static final int VI_TRANSFORMB=1; + static final int VI_WINDOWB=1; + + int analysisp; + VorbisInfo vi; + int modebits; + + float[][] pcm; + int pcm_storage; + int pcm_current; + int pcm_returned; + + float[] multipliers; + int envelope_storage; + int envelope_current; + + int eofflag; + + int lW; + int W; + int nW; + int centerW; + + long granulepos; + long sequence; + + long glue_bits; + long time_bits; + long floor_bits; + long res_bits; + + // local lookup storage + float[][][][][] window; // block, leadin, leadout, type + Object[][] transform; + CodeBook[] fullbooks; + // backend lookups are tied to the mode, not the backend or naked mapping + Object[] mode; + + // local storage, only used on the encoding side. This way the + // application does not need to worry about freeing some packets' + // memory and not others'; packet storage is always tracked. + // Cleared next call to a _dsp_ function + byte[] header; + byte[] header1; + byte[] header2; + + public VorbisDspState(){ + transform=new Object[2][]; + window=new float[2][][][][]; + window[0]=new float[2][][][]; + window[0][0]=new float[2][][]; + window[0][1]=new float[2][][]; + window[0][0][0]=new float[2][]; + window[0][0][1]=new float[2][]; + window[0][1][0]=new float[2][]; + window[0][1][1]=new float[2][]; + window[1]=new float[2][][][]; + window[1][0]=new float[2][][]; + window[1][1]=new float[2][][]; + window[1][0][0]=new float[2][]; + window[1][0][1]=new float[2][]; + window[1][1][0]=new float[2][]; + window[1][1][1]=new float[2][]; + } + + static float[] window(int type, int window, int left, int right){ + float[] ret=new float[window]; + switch(type){ + case 0: + // The 'vorbis window' (window 0) is sin(sin(x)*sin(x)*2pi) + { + int leftbegin=window/4-left/2; + int rightbegin=window-window/4-right/2; + + for(int i=0; ivi.blocksizes[1]/2&&pcm_returned>8192){ + // don't shift too much; we need to have a minimum PCM buffer of + // 1/2 long block + + int shiftPCM=centerW-vi.blocksizes[1]/2; + shiftPCM=(pcm_returnedpcm_storage){ + // expand the storage + pcm_storage=endW+vi.blocksizes[1]; + for(int i=0; igranulepos. + // + // This is not foolproof! It will be confused if we begin + // decoding at the last page after a seek or hole. In that case, + // we don't have a starting point to judge where the last frame + // is. For this reason, vorbisfile will always try to make sure + // it reads the last two marked pages in proper sequence + + if(granulepos==-1){ + granulepos=vb.granulepos; + } + else{ + granulepos+=(_centerW-centerW); + if(vb.granulepos!=-1&&granulepos!=vb.granulepos){ + if(granulepos>vb.granulepos&&vb.eofflag!=0){ + // partial last frame. Strip the padding off + _centerW-=(granulepos-vb.granulepos); + }// else{ Shouldn't happen *unless* the bitstream is out of + // spec. Either way, believe the bitstream } + granulepos=vb.granulepos; + } + } + + // Update, cleanup + + centerW=_centerW; + pcm_current=endW; + if(vb.eofflag!=0) + eofflag=1; + } + return (0); + } + + // pcm==NULL indicates we just want the pending samples, no more + public int synthesis_pcmout(float[][][] _pcm, int[] index){ + if(pcm_returnedcenterW) + return (-1); + pcm_returned+=bytes; + return (0); + } + + public void clear(){ + } +} diff --git a/common/src/main/java/eu/midnightdust/picturesign_theora/ogv/jorbis/VorbisFile.java b/common/src/main/java/eu/midnightdust/picturesign_theora/ogv/jorbis/VorbisFile.java new file mode 100644 index 0000000..66cbea3 --- /dev/null +++ b/common/src/main/java/eu/midnightdust/picturesign_theora/ogv/jorbis/VorbisFile.java @@ -0,0 +1,1398 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* JOrbis + * Copyright (C) 2000 ymnk, JCraft,Inc. + * + * Written by: 2000 ymnk + * + * Many thanks to + * Monty and + * The XIPHOPHORUS Company http://www.xiph.org/ . + * JOrbis has been based on their awesome works, Vorbis codec. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public License + * as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +package eu.midnightdust.picturesign_theora.ogv.jorbis; + +import java.io.InputStream; + +import eu.midnightdust.picturesign_theora.ogv.jogg.*; + +import java.io.IOException; + +public class VorbisFile{ + static final int CHUNKSIZE=8500; + static final int SEEK_SET=0; + static final int SEEK_CUR=1; + static final int SEEK_END=2; + + static final int OV_FALSE=-1; + static final int OV_EOF=-2; + static final int OV_HOLE=-3; + + static final int OV_EREAD=-128; + static final int OV_EFAULT=-129; + static final int OV_EIMPL=-130; + static final int OV_EINVAL=-131; + static final int OV_ENOTVORBIS=-132; + static final int OV_EBADHEADER=-133; + static final int OV_EVERSION=-134; + static final int OV_ENOTAUDIO=-135; + static final int OV_EBADPACKET=-136; + static final int OV_EBADLINK=-137; + static final int OV_ENOSEEK=-138; + + InputStream datasource; + boolean seekable=false; + long offset; + long end; + + OggSyncState oy=new OggSyncState(); + + int links; + long[] offsets; + long[] dataoffsets; + int[] serialnos; + long[] pcmlengths; + VorbisInfo[] vi; + VorbisComment[] vc; + + // Decoding working state local storage + long pcm_offset; + boolean decode_ready=false; + + int current_serialno; + int current_link; + + float bittrack; + float samptrack; + + OggStreamState os=new OggStreamState(); // take physical pages, weld into a logical + // stream of packets + VorbisDspState vd=new VorbisDspState(); // central working state for + // the packet->PCM decoder + VorbisBlock vb=new VorbisBlock(vd); // local working space for packet->PCM decode + + //ov_callbacks callbacks; + + public VorbisFile(String file) throws JOrbisException{ + super(); + InputStream is=null; + try{ + is=new SeekableInputStream(file); + int ret=open(is, null, 0); + if(ret==-1){ + throw new JOrbisException("VorbisFile: open return -1"); + } + } + catch(Exception e){ + throw new JOrbisException("VorbisFile: "+e.toString()); + } + finally{ + if(is!=null){ + try{ + is.close(); + } + catch(IOException e){ + e.printStackTrace(); + } + } + } + } + + public VorbisFile(InputStream is, byte[] initial, int ibytes) + throws JOrbisException{ + super(); + int ret=open(is, initial, ibytes); + if(ret==-1){ + } + } + + private int get_data(){ + int index=oy.buffer(CHUNKSIZE); + byte[] buffer=oy.data; + int bytes=0; + try{ + bytes=datasource.read(buffer, index, CHUNKSIZE); + } + catch(Exception e){ + return OV_EREAD; + } + oy.wrote(bytes); + if(bytes==-1){ + bytes=0; + } + return bytes; + } + + private void seek_helper(long offst){ + fseek(datasource, offst, SEEK_SET); + this.offset=offst; + oy.reset(); + } + + private int get_next_page(OggPage page, long boundary){ + if(boundary>0) + boundary+=offset; + while(true){ + int more; + if(boundary>0&&offset>=boundary) + return OV_FALSE; + more=oy.pageseek(page); + if(more<0){ + offset-=more; + } + else{ + if(more==0){ + if(boundary==0) + return OV_FALSE; + int ret=get_data(); + if(ret==0) + return OV_EOF; + if(ret<0) + return OV_EREAD; + } + else{ + int ret=(int)offset; //!!! + offset+=more; + return ret; + } + } + } + } + + private int get_prev_page(OggPage page) throws JOrbisException{ + long begin=offset; //!!! + int ret; + int offst=-1; + while(offst==-1){ + begin-=CHUNKSIZE; + if(begin<0) + begin=0; + seek_helper(begin); + while(offset=0) + next=ret; + } + else{ + searched=ret+page.header_len+page.body_len; + } + } + seek_helper(next); + ret=get_next_page(page, -1); + if(ret==OV_EREAD) + return OV_EREAD; + + if(searched>=end||ret==-1){ + links=m+1; + offsets=new long[m+2]; + offsets[m+1]=searched; + } + else{ + ret=bisect_forward_serialno(next, offset, end, page.serialno(), m+1); + if(ret==OV_EREAD) + return OV_EREAD; + } + offsets[m]=begin; + return 0; + } + + // uses the local ogg_stream storage in vf; this is important for + // non-streaming input sources + int fetch_headers(VorbisInfo vi, VorbisComment vc, int[] serialno, OggPage og_ptr){ + OggPage og=new OggPage(); + OggPacket op=new OggPacket(); + int ret; + + if(og_ptr==null){ + ret=get_next_page(og, CHUNKSIZE); + if(ret==OV_EREAD) + return OV_EREAD; + if(ret<0) + return OV_ENOTVORBIS; + og_ptr=og; + } + + if(serialno!=null) + serialno[0]=og_ptr.serialno(); + + os.init(og_ptr.serialno()); + + // extract the initial header from the first page and verify that the + // Ogg bitstream is in fact Vorbis data + + vi.init(); + vc.init(); + + int i=0; + while(i<3){ + os.pagein(og_ptr); + while(i<3){ + int result=os.packetout(op); + if(result==0) + break; + if(result==-1){ + vi.clear(); + vc.clear(); + os.clear(); + return -1; + } + if(vi.synthesis_headerin(vc, op)!=0){ + vi.clear(); + vc.clear(); + os.clear(); + return -1; + } + i++; + } + if(i<3) + if(get_next_page(og_ptr, 1)<0){ + vi.clear(); + vc.clear(); + os.clear(); + return -1; + } + } + return 0; + } + + // last step of the OggVorbis_File initialization; get all the + // vorbis_info structs and PCM positions. Only called by the seekable + // initialization (local stream storage is hacked slightly; pay + // attention to how that's done) + void prefetch_all_headers(VorbisInfo first_i, VorbisComment first_c, int dataoffset) + throws JOrbisException{ + OggPage og=new OggPage(); + int ret; + + vi=new VorbisInfo[links]; + vc=new VorbisComment[links]; + dataoffsets=new long[links]; + pcmlengths=new long[links]; + serialnos=new int[links]; + + for(int i=0; i0){ + // got a packet. process it + granulepos=op.granulepos; + if(vb.synthesis(op)==0){ // lazy check for lazy + // header handling. The + // header packets aren't + // audio, so if/when we + // submit them, + // vorbis_synthesis will + // reject them + // suck in the synthesis data and track bitrate + { + int oldsamples=vd.synthesis_pcmout(null, null); + vd.synthesis_blockin(vb); + samptrack+=vd.synthesis_pcmout(null, null)-oldsamples; + bittrack+=op.bytes*8; + } + + // update the pcm offset. + if(granulepos!=-1&&op.e_o_s==0){ + int link=(seekable ? current_link : 0); + int samples; + // this packet has a pcm_offset on it (the last packet + // completed on a page carries the offset) After processing + // (above), we know the pcm position of the *last* sample + // ready to be returned. Find the offset of the *first* + // + // As an aside, this trick is inaccurate if we begin + // reading anew right at the last page; the end-of-stream + // granulepos declares the last frame in the stream, and the + // last packet of the last page may be a partial frame. + // So, we need a previous granulepos from an in-sequence page + // to have a reference point. Thus the !op.e_o_s clause above + + samples=vd.synthesis_pcmout(null, null); + granulepos-=samples; + for(int i=0; i=links) + return (-1); + if(!seekable&&i!=0) + return (bitrate(0)); + if(i<0){ + long bits=0; + for(int j=0; j0){ + return vi[i].bitrate_nominal; + } + else{ + if(vi[i].bitrate_upper>0){ + if(vi[i].bitrate_lower>0){ + return (vi[i].bitrate_upper+vi[i].bitrate_lower)/2; + } + else{ + return vi[i].bitrate_upper; + } + } + return (-1); + } + } + } + } + + // returns the actual bitrate since last call. returns -1 if no + // additional data to offer since last call (or at beginning of stream) + public int bitrate_instant(){ + int _link=(seekable ? current_link : 0); + if(samptrack==0) + return (-1); + int ret=(int)(bittrack/samptrack*vi[_link].rate+.5); + bittrack=0.f; + samptrack=0.f; + return (ret); + } + + public int serialnumber(int i){ + if(i>=links) + return (-1); + if(!seekable&&i>=0) + return (serialnumber(-1)); + if(i<0){ + return (current_serialno); + } + else{ + return (serialnos[i]); + } + } + + // returns: total raw (compressed) length of content if i==-1 + // raw (compressed) length of that logical bitstream for i==0 to n + // -1 if the stream is not seekable (we can't know the length) + + public long raw_total(int i){ + if(!seekable||i>=links) + return (-1); + if(i<0){ + long acc=0; // bug? + for(int j=0; j=links) + return (-1); + if(i<0){ + long acc=0; + for(int j=0; j=links) + return (-1); + if(i<0){ + float acc=0; + for(int j=0; joffsets[links]){ + //goto seek_error; + pcm_offset=-1; + decode_clear(); + return -1; + } + + // clear out decoding machine state + pcm_offset=-1; + decode_clear(); + + // seek + seek_helper(pos); + + // we need to make sure the pcm_offset is set. We use the + // _fetch_packet helper to process one packet with readp set, then + // call it until it returns '0' with readp not set (the last packet + // from a page has the 'granulepos' field set, and that's how the + // helper updates the offset + + switch(process_packet(1)){ + case 0: + // oh, eof. There are no packets remaining. Set the pcm offset to + // the end of file + pcm_offset=pcm_total(-1); + return (0); + case -1: + // error! missing data or invalid bitstream structure + //goto seek_error; + pcm_offset=-1; + decode_clear(); + return -1; + default: + // all OK + break; + } + while(true){ + switch(process_packet(0)){ + case 0: + // the offset is set. If it's a bogus bitstream with no offset + // information, it's not but that's not our fault. We still run + // gracefully, we're just missing the offset + return (0); + case -1: + // error! missing data or invalid bitstream structure + //goto seek_error; + pcm_offset=-1; + decode_clear(); + return -1; + default: + // continue processing packets + break; + } + } + + // seek_error: + // dump the machine so we're in a known state + //pcm_offset=-1; + //decode_clear(); + //return -1; + } + + // seek to a sample offset relative to the decompressed pcm stream + // returns zero on success, nonzero on failure + + public int pcm_seek(long pos){ + int link=-1; + long total=pcm_total(-1); + + if(!seekable) + return (-1); // don't dump machine if we can't seek + if(pos<0||pos>total){ + //goto seek_error; + pcm_offset=-1; + decode_clear(); + return -1; + } + + // which bitstream section does this pcm offset occur in? + for(link=links-1; link>=0; link--){ + total-=pcmlengths[link]; + if(pos>=total) + break; + } + + // search within the logical bitstream for the page with the highest + // pcm_pos preceeding (or equal to) pos. There is a danger here; + // missing pages or incorrect frame number information in the + // bitstream could make our task impossible. Account for that (it + // would be an error condition) + { + long target=pos-total; + long end=offsets[link+1]; + long begin=offsets[link]; + int best=(int)begin; + + OggPage og=new OggPage(); + while(begin=pos){ + //goto seek_error; + pcm_offset=-1; + decode_clear(); + return -1; + } + if(pos>pcm_total(-1)){ + //goto seek_error; + pcm_offset=-1; + decode_clear(); + return -1; + } + + // discard samples until we reach the desired position. Crossing a + // logical bitstream boundary with abandon is OK. + while(pcm_offsettarget) + samples=target; + vd.synthesis_read(samples); + pcm_offset+=samples; + + if(samplestime_total){ + //goto seek_error; + pcm_offset=-1; + decode_clear(); + return -1; + } + + // which bitstream section does this time offset occur in? + for(link=links-1; link>=0; link--){ + pcm_total-=pcmlengths[link]; + time_total-=time_total(link); + if(seconds>=time_total) + break; + } + + // enough information to convert time offset to pcm offset + { + long target=(long)(pcm_total+(seconds-time_total)*vi[link].rate); + return (pcm_seek(target)); + } + + //seek_error: + // dump machine so we're in a known state + //pcm_offset=-1; + //decode_clear(); + //return -1; + } + + // tell the current stream offset cursor. Note that seek followed by + // tell will likely not give the set offset due to caching + public long raw_tell(){ + return (offset); + } + + // return PCM offset (sample) of next PCM sample to be read + public long pcm_tell(){ + return (pcm_offset); + } + + // return time offset (seconds) of next PCM sample to be read + public float time_tell(){ + // translate time to PCM position and call pcm_seek + + int link=-1; + long pcm_total=0; + float time_total=0.f; + + if(seekable){ + pcm_total=pcm_total(-1); + time_total=time_total(-1); + + // which bitstream section does this time offset occur in? + for(link=links-1; link>=0; link--){ + pcm_total-=pcmlengths[link]; + time_total-=time_total(link); + if(pcm_offset>=pcm_total) + break; + } + } + + return ((float)time_total+(float)(pcm_offset-pcm_total)/vi[link].rate); + } + + // link: -1) return the vorbis_info struct for the bitstream section + // currently being decoded + // 0-n) to request information for a specific bitstream section + // + // In the case of a non-seekable bitstream, any call returns the + // current bitstream. NULL in the case that the machine is not + // initialized + + public VorbisInfo getInfo(int link){ + if(seekable){ + if(link<0){ + if(decode_ready){ + return vi[current_link]; + } + else{ + return null; + } + } + else{ + if(link>=links){ + return null; + } + else{ + return vi[link]; + } + } + } + else{ + if(decode_ready){ + return vi[0]; + } + else{ + return null; + } + } + } + + public VorbisComment getComment(int link){ + if(seekable){ + if(link<0){ + if(decode_ready){ + return vc[current_link]; + } + else{ + return null; + } + } + else{ + if(link>=links){ + return null; + } + else{ + return vc[link]; + } + } + } + else{ + if(decode_ready){ + return vc[0]; + } + else{ + return null; + } + } + } + + int host_is_big_endian(){ + return 1; + // short pattern = 0xbabe; + // unsigned char *bytewise = (unsigned char *)&pattern; + // if (bytewise[0] == 0xba) return 1; + // assert(bytewise[0] == 0xbe); + // return 0; + } + + // up to this point, everything could more or less hide the multiple + // logical bitstream nature of chaining from the toplevel application + // if the toplevel application didn't particularly care. However, at + // the point that we actually read audio back, the multiple-section + // nature must surface: Multiple bitstream sections do not necessarily + // have to have the same number of channels or sampling rate. + // + // read returns the sequential logical bitstream number currently + // being decoded along with the PCM data in order that the toplevel + // application can take action on channel/sample rate changes. This + // number will be incremented even for streamed (non-seekable) streams + // (for seekable streams, it represents the actual logical bitstream + // index within the physical bitstream. Note that the accessor + // functions above are aware of this dichotomy). + // + // input values: buffer) a buffer to hold packed PCM data for return + // length) the byte length requested to be placed into buffer + // bigendianp) should the data be packed LSB first (0) or + // MSB first (1) + // word) word size for output. currently 1 (byte) or + // 2 (16 bit short) + // + // return values: -1) error/hole in data + // 0) EOF + // n) number of bytes of PCM actually returned. The + // below works on a packet-by-packet basis, so the + // return length is not related to the 'length' passed + // in, just guaranteed to fit. + // + // *section) set to the logical bitstream number + + int read(byte[] buffer, int length, int bigendianp, int word, int sgned, + int[] bitstream){ + int host_endian=host_is_big_endian(); + int index=0; + + while(true){ + if(decode_ready){ + float[][] pcm; + float[][][] _pcm=new float[1][][]; + int[] _index=new int[getInfo(-1).channels]; + int samples=vd.synthesis_pcmout(_pcm, _index); + pcm=_pcm[0]; + if(samples!=0){ + // yay! proceed to pack data into the byte buffer + int channels=getInfo(-1).channels; + int bytespersample=word*channels; + if(samples>length/bytespersample) + samples=length/bytespersample; + + // a tight loop to pack each size + { + int val; + if(word==1){ + int off=(sgned!=0 ? 0 : 128); + for(int j=0; j127) + val=127; + else if(val<-128) + val=-128; + buffer[index++]=(byte)(val+off); + } + } + } + else{ + int off=(sgned!=0 ? 0 : 32768); + + if(host_endian==bigendianp){ + if(sgned!=0){ + for(int i=0; i32767) + val=32767; + else if(val<-32768) + val=-32768; + buffer[dest]=(byte)(val>>>8); + buffer[dest+1]=(byte)(val); + dest+=channels*2; + } + } + } + else{ + for(int i=0; i32767) + val=32767; + else if(val<-32768) + val=-32768; + buffer[dest]=(byte)((val+off)>>>8); + buffer[dest+1]=(byte)(val+off); + dest+=channels*2; + } + } + } + } + else if(bigendianp!=0){ + for(int j=0; j32767) + val=32767; + else if(val<-32768) + val=-32768; + val+=off; + buffer[index++]=(byte)(val>>>8); + buffer[index++]=(byte)val; + } + } + } + else{ + //int val; + for(int j=0; j32767) + val=32767; + else if(val<-32768) + val=-32768; + val+=off; + buffer[index++]=(byte)val; + buffer[index++]=(byte)(val>>>8); + } + } + } + } + } + + vd.synthesis_read(samples); + pcm_offset+=samples; + if(bitstream!=null) + bitstream[0]=current_link; + return (samples*bytespersample); + } + } + + // suck in another packet + switch(process_packet(1)){ + case 0: + return (0); + case -1: + return -1; + default: + break; + } + } + } + + public VorbisInfo[] getInfo(){ + return vi; + } + + public VorbisComment[] getComment(){ + return vc; + } + + public void close() throws java.io.IOException{ + datasource.close(); + } + + class SeekableInputStream extends InputStream{ + java.io.RandomAccessFile raf=null; + final String mode="r"; + + SeekableInputStream(String file) throws java.io.IOException{ + raf=new java.io.RandomAccessFile(file, mode); + } + + public int read() throws java.io.IOException{ + return raf.read(); + } + + public int read(byte[] buf) throws java.io.IOException{ + return raf.read(buf); + } + + public int read(byte[] buf, int s, int len) throws java.io.IOException{ + return raf.read(buf, s, len); + } + + public long skip(long n) throws java.io.IOException{ + return (long)(raf.skipBytes((int)n)); + } + + public long getLength() throws java.io.IOException{ + return raf.length(); + } + + public long tell() throws java.io.IOException{ + return raf.getFilePointer(); + } + + public int available() throws java.io.IOException{ + return (raf.length()==raf.getFilePointer()) ? 0 : 1; + } + + public void close() throws java.io.IOException{ + raf.close(); + } + + public synchronized void mark(int m){ + } + + public synchronized void reset() throws java.io.IOException{ + } + + public boolean markSupported(){ + return false; + } + + public void seek(long pos) throws java.io.IOException{ + raf.seek(pos); + } + } + +} diff --git a/common/src/main/java/eu/midnightdust/picturesign_theora/ogv/jorbis/VorbisInfo.java b/common/src/main/java/eu/midnightdust/picturesign_theora/ogv/jorbis/VorbisInfo.java new file mode 100644 index 0000000..ec1e087 --- /dev/null +++ b/common/src/main/java/eu/midnightdust/picturesign_theora/ogv/jorbis/VorbisInfo.java @@ -0,0 +1,470 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* JOrbis + * Copyright (C) 2000 ymnk, JCraft,Inc. + * + * Written by: 2000 ymnk + * + * Many thanks to + * Monty and + * The XIPHOPHORUS Company http://www.xiph.org/ . + * JOrbis has been based on their awesome works, Vorbis codec. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public License + * as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +package eu.midnightdust.picturesign_theora.ogv.jorbis; + +import eu.midnightdust.picturesign_theora.ogv.jogg.*; + +public class VorbisInfo{ + private static final int OV_EBADPACKET=-136; + private static final int OV_ENOTAUDIO=-135; + + private static byte[] _vorbis="vorbis".getBytes(); + private static final int VI_TIMEB=1; + // private static final int VI_FLOORB=1; + private static final int VI_FLOORB=2; + // private static final int VI_RESB=1; + private static final int VI_RESB=3; + private static final int VI_MAPB=1; + private static final int VI_WINDOWB=1; + + public int version; + public int channels; + public int rate; + + // The below bitrate declarations are *hints*. + // Combinations of the three values carry the following implications: + // + // all three set to the same value: + // implies a fixed rate bitstream + // only nominal set: + // implies a VBR stream that averages the nominal bitrate. No hard + // upper/lower limit + // upper and or lower set: + // implies a VBR bitstream that obeys the bitrate limits. nominal + // may also be set to give a nominal rate. + // none set: + // the coder does not care to speculate. + + int bitrate_upper; + int bitrate_nominal; + int bitrate_lower; + + // Vorbis supports only short and long blocks, but allows the + // encoder to choose the sizes + + int[] blocksizes=new int[2]; + + // modes are the primary means of supporting on-the-fly different + // blocksizes, different channel mappings (LR or mid-side), + // different residue backends, etc. Each mode consists of a + // blocksize flag and a mapping (along with the mapping setup + + int modes; + int maps; + int times; + int floors; + int residues; + int books; + int psys; // encode only + + InfoMode[] mode_param=null; + + int[] map_type=null; + Object[] map_param=null; + + int[] time_type=null; + Object[] time_param=null; + + int[] floor_type=null; + Object[] floor_param=null; + + int[] residue_type=null; + Object[] residue_param=null; + + StaticCodeBook[] book_param=null; + + PsyInfo[] psy_param=new PsyInfo[64]; // encode only + + // for block long/sort tuning; encode only + int envelopesa; + float preecho_thresh; + float preecho_clamp; + + // used by synthesis, which has a full, alloced vi + public void init(){ + rate=0; + } + + public void clear(){ + for(int i=0; ibook_param)free(vi->book_param); + book_param=null; + + for(int i=0; i=VI_TIMEB){ + clear(); + return (-1); + } + time_param[i]=FuncTime.time_P[time_type[i]].unpack(this, opb); + if(time_param[i]==null){ + clear(); + return (-1); + } + } + + // floor backend settings + floors=opb.read(6)+1; + if(floor_type==null||floor_type.length!=floors) + floor_type=new int[floors]; + if(floor_param==null||floor_param.length!=floors) + floor_param=new Object[floors]; + + for(int i=0; i=VI_FLOORB){ + clear(); + return (-1); + } + + floor_param[i]=FuncFloor.floor_P[floor_type[i]].unpack(this, opb); + if(floor_param[i]==null){ + clear(); + return (-1); + } + } + + // residue backend settings + residues=opb.read(6)+1; + + if(residue_type==null||residue_type.length!=residues) + residue_type=new int[residues]; + + if(residue_param==null||residue_param.length!=residues) + residue_param=new Object[residues]; + + for(int i=0; i=VI_RESB){ + clear(); + return (-1); + } + residue_param[i]=FuncResidue.residue_P[residue_type[i]].unpack(this, opb); + if(residue_param[i]==null){ + clear(); + return (-1); + } + } + + // map backend settings + maps=opb.read(6)+1; + if(map_type==null||map_type.length!=maps) + map_type=new int[maps]; + if(map_param==null||map_param.length!=maps) + map_param=new Object[maps]; + for(int i=0; i=VI_MAPB){ + clear(); + return (-1); + } + map_param[i]=FuncMapping.mapping_P[map_type[i]].unpack(this, opb); + if(map_param[i]==null){ + clear(); + return (-1); + } + } + + // mode settings + modes=opb.read(6)+1; + if(mode_param==null||mode_param.length!=modes) + mode_param=new InfoMode[modes]; + for(int i=0; i=VI_WINDOWB) + ||(mode_param[i].transformtype>=VI_WINDOWB) + ||(mode_param[i].mapping>=maps)){ + clear(); + return (-1); + } + } + + if(opb.read(1)!=1){ + clear(); + return (-1); + } + + return (0); + } + + // The Vorbis header is in three packets; the initial small packet in + // the first page that identifies basic parameters, a second packet + // with bitstream comments and a third packet that holds the + // codebook. + + public int synthesis_headerin(VorbisComment vc, OggPacket op){ + OggBuffer opb=new OggBuffer(); + + if(op!=null){ + opb.readinit(op.packet_base, op.packet, op.bytes); + + // Which of the three types of header is this? + // Also verify header-ness, vorbis + { + byte[] buffer=new byte[6]; + int packtype=opb.read(8); + opb.read(buffer, 6); + if(buffer[0]!='v'||buffer[1]!='o'||buffer[2]!='r'||buffer[3]!='b' + ||buffer[4]!='i'||buffer[5]!='s'){ + // not a vorbis header + return (-1); + } + switch(packtype){ + case 0x01: // least significant *bit* is read first + if(op.b_o_s==0){ + // Not the initial packet + return (-1); + } + if(rate!=0){ + // previously initialized info header + return (-1); + } + return (unpack_info(opb)); + case 0x03: // least significant *bit* is read first + if(rate==0){ + // um... we didn't get the initial header + return (-1); + } + return (vc.unpack(opb)); + case 0x05: // least significant *bit* is read first + if(rate==0||vc.vendor==null){ + // um... we didn;t get the initial header or comments yet + return (-1); + } + return (unpack_books(opb)); + default: + // Not a valid vorbis header type + //return(-1); + break; + } + } + } + return (-1); + } + + // pack side + int pack_info(OggBuffer opb){ + // preamble + opb.write(0x01, 8); + opb.write(_vorbis); + + // basic information about the stream + opb.write(0x00, 32); + opb.write(channels, 8); + opb.write(rate, 32); + + opb.write(bitrate_upper, 32); + opb.write(bitrate_nominal, 32); + opb.write(bitrate_lower, 32); + + opb.write(Util.ilog2(blocksizes[0]), 4); + opb.write(Util.ilog2(blocksizes[1]), 4); + opb.write(1, 1); + return (0); + } + + int pack_books(OggBuffer opb){ + opb.write(0x05, 8); + opb.write(_vorbis); + + // books + opb.write(books-1, 8); + for(int i=0; i1){ + modebits++; + v>>>=1; + } + + /* read our mode and pre/post windowsize */ + mode=opb.read(modebits); + } + if(mode==-1) + return (OV_EBADPACKET); + return (blocksizes[mode_param[mode].blockflag]); + } + + @Override +public String toString(){ + return "version:"+version+", channels:"+channels + +", rate:"+rate+", bitrate:"+bitrate_upper + +","+bitrate_nominal+","+bitrate_lower; + } +} diff --git a/common/src/main/java/eu/midnightdust/picturesign_theora/util/TheoraMediaHandler.java b/common/src/main/java/eu/midnightdust/picturesign_theora/util/TheoraMediaHandler.java new file mode 100644 index 0000000..d69fe35 --- /dev/null +++ b/common/src/main/java/eu/midnightdust/picturesign_theora/util/TheoraMediaHandler.java @@ -0,0 +1,93 @@ +package eu.midnightdust.picturesign_theora.util; + +import eu.midnightdust.picturesign.util.MediaHandler; +import eu.midnightdust.picturesign_theora.ogv.Video; +import eu.midnightdust.picturesign_theora.ogv.VideoManager; +import net.minecraft.client.MinecraftClient; +import net.minecraft.sound.SoundCategory; +import net.minecraft.util.Identifier; +import net.minecraft.util.math.BlockPos; +import org.lwjgl.opengl.GL11; + +public class TheoraMediaHandler extends MediaHandler { + public static final VideoManager manager = new VideoManager(); + private Video player; + private String url; + + public TheoraMediaHandler(Identifier id, BlockPos pos) { + super(id, pos); + mediaHandlers.put(id, this); + } + @Override + public void setVolume(int volume) { + player.setVolume((int) (volume * MinecraftClient.getInstance().options.getSoundVolume(SoundCategory.MASTER))); + } + + @Override + public void closePlayer() { + manager.closePlayer(id); + if (player != null) { + player.stop(); + player.destroy(); + } + //mediaPlayers.remove(id); + player = null; + } + @Override + public void stop() { + player.stop(); + isDeactivated = true; + } + @Override + public boolean isStopped() { + return player.isStopped(); + } + @Override + public void restart() { + player.play(url); + } + + @Override + public void play(String url, boolean isVideo) { + System.out.println("Playing: " + url); + this.player = manager.getOrCreate(id); + mediaHandlers.put(id, this); + //if (player.isBroken()) return; + player.play(url); + this.url = url; + this.playbackStarted = true; + } + @Override + public boolean hasMedia() { + return player != null && player.hasMedia(); + } + @Override + public void setRepeat(boolean value) { + player.setRepeat(true); + } + @Override + public long getTime() { + return player.getTime(); + } + @Override + public void setTime(long value) { + player.setTime(value); + } + @Override + public int getTexture() { + if (player != null) { + int tex = player.getTextureId(); + if (GL11.glIsTexture(tex)) return tex; + } + return -1; + } + @Override + public boolean isReady() { + return true; + } + @Override + public boolean isWorking() { + return mediaHandlers.containsKey(id) && mediaHandlers.get(id) instanceof TheoraMediaHandler theoraMediaHandler + && theoraMediaHandler.player != null; + } +} diff --git a/common/src/main/resources/picturesign-theora.mixins.json b/common/src/main/resources/picturesign-theora.mixins.json new file mode 100755 index 0000000..0c17212 --- /dev/null +++ b/common/src/main/resources/picturesign-theora.mixins.json @@ -0,0 +1,11 @@ +{ + "required": true, + "package": "eu.midnightdust.picturesign_theora.mixin", + "compatibilityLevel": "JAVA_17", + "client": [ + "MixinMinecraftClient" + ], + "injectors": { + "defaultRequire": 1 + } +} \ No newline at end of file diff --git a/fabric/build.gradle b/fabric/build.gradle new file mode 100644 index 0000000..252d3c1 --- /dev/null +++ b/fabric/build.gradle @@ -0,0 +1,63 @@ +plugins { + id 'com.github.johnrengelman.shadow' + //id "me.shedaniel.unified-publishing" +} + +architectury { + platformSetupLoomIde() + fabric() +} + +loom { +} + +configurations { + common + shadowCommon // Don't use shadow from the shadow plugin since it *excludes* files. + compileClasspath.extendsFrom common + runtimeClasspath.extendsFrom common + developmentFabric.extendsFrom common + archivesBaseName = rootProject.archives_base_name + "-fabric" +} + +dependencies { + modImplementation "net.fabricmc:fabric-loader:${rootProject.fabric_loader_version}" + modApi "net.fabricmc.fabric-api:fabric-api:${rootProject.fabric_api_version}" + modImplementation include ("maven.modrinth:midnightlib:${rootProject.midnightlib_version}-fabric") + modImplementation ("maven.modrinth:picturesign:${rootProject.picturesign_version}-fabric") + + common(project(path: ":common", configuration: "namedElements")) { transitive false } + shadowCommon(project(path: ":common", configuration: "transformProductionFabric")) { transitive false } +} + +processResources { + inputs.property "version", project.version + + filesMatching("fabric.mod.json") { + expand "version": project.version + } +} + +shadowJar { + exclude "architectury.common.json" + + configurations = [project.configurations.shadowCommon] + archiveClassifier = "dev-shadow" +} + +remapJar { + input.set shadowJar.archiveFile + dependsOn shadowJar +} + +sourcesJar { + def commonSources = project(":common").sourcesJar + dependsOn commonSources + from commonSources.archiveFile.map { zipTree(it) } +} + +components.java { + withVariantsFromConfiguration(project.configurations.shadowRuntimeElements) { + skip() + } +} \ No newline at end of file diff --git a/fabric/src/main/java/eu/midnightdust/picturesign_theora/fabric/PictureSignTheoraClientFabric.java b/fabric/src/main/java/eu/midnightdust/picturesign_theora/fabric/PictureSignTheoraClientFabric.java new file mode 100755 index 0000000..10cf634 --- /dev/null +++ b/fabric/src/main/java/eu/midnightdust/picturesign_theora/fabric/PictureSignTheoraClientFabric.java @@ -0,0 +1,11 @@ +package eu.midnightdust.picturesign_theora.fabric; + +import eu.midnightdust.picturesign_theora.PictureSignTheoraClient; +import net.fabricmc.api.ClientModInitializer; + +public class PictureSignTheoraClientFabric implements ClientModInitializer { + @Override + public void onInitializeClient() { + PictureSignTheoraClient.init(); + } +} diff --git a/fabric/src/main/resources/fabric.mod.json b/fabric/src/main/resources/fabric.mod.json new file mode 100755 index 0000000..3290205 --- /dev/null +++ b/fabric/src/main/resources/fabric.mod.json @@ -0,0 +1,34 @@ +{ + "schemaVersion": 1, + "id": "picturesign-theora", + "version": "${version}", + + "name": "PictureSign: Theora", + "description": "Adds support for Theora, Vorbis and OGG to PictureSign", + "authors": [ + "unascribed", + "Motschen" + ], + "contact": { + "homepage": "https://www.midnightdust.eu/", + "sources": "https://github.com/TeamMidnightDust/PictureSignTheora", + "issues": "https://github.com/TeamMidnightDust/PictureSignTheora/issues" + }, + + "license": "MIT", + "icon": "assets/picturesign/icon.png", + + "environment": "client", + "entrypoints": { + "client": [ + "eu.midnightdust.picturesign_theora.fabric.PictureSignTheoraClientFabric" + ] + }, + + "depends": { + "picturesign": "*" + }, + "mixins": [ + "picturesign-theora.mixins.json" + ] +} diff --git a/gradle.properties b/gradle.properties new file mode 100755 index 0000000..7361d13 --- /dev/null +++ b/gradle.properties @@ -0,0 +1,24 @@ +org.gradle.jvmargs=-Xmx2048M + +minecraft_version=1.21 +yarn_mappings=1.21+build.2 +enabled_platforms=fabric,neoforge + +archives_base_name=picturesign-theora +mod_version=1.0.0 +maven_group=eu.midnightdust +release_type=release +curseforge_id= +modrinth_id= + +midnightlib_version=1.5.7 +picturesign_version=2.0.1 + +fabric_loader_version=0.15.11 +fabric_api_version=0.100.1+1.21 + +neoforge_version=21.0.14-beta +yarn_mappings_patch_neoforge_version = 1.21+build.4 + +quilt_loader_version=0.19.0-beta.18 +quilt_fabric_api_version=7.0.1+0.83.0-1.20 diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar new file mode 100755 index 0000000000000000000000000000000000000000..e6441136f3d4ba8a0da8d277868979cfbc8ad796 GIT binary patch literal 43453 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 yu?e%!_ptzGB|8GrCfu%p?(_ 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
NnX)EH+Nua)3Y(c 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 '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +# This is normally unused +# shellcheck disable=SC2034 +APP_BASE_NAME=${0##*/} +# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) +APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + if ! command -v java >/dev/null 2>&1 + then + die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Collect all arguments for the java command: +# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# and any embedded shellness will be escaped. +# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be +# treated as '${Hostname}' itself on the command line. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat new file mode 100755 index 0000000..7101f8e --- /dev/null +++ b/gradlew.bat @@ -0,0 +1,92 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%"=="" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if %ERRORLEVEL% equ 0 goto execute + +echo. 1>&2 +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. 1>&2 +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if %ERRORLEVEL% equ 0 goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/neoforge/build.gradle b/neoforge/build.gradle new file mode 100644 index 0000000..694f183 --- /dev/null +++ b/neoforge/build.gradle @@ -0,0 +1,76 @@ +plugins { + id 'com.github.johnrengelman.shadow' + //id "me.shedaniel.unified-publishing" +} + +repositories { + maven { + name = 'NeoForged' + url = 'https://maven.neoforged.net/releases' + } +} + + +architectury { + platformSetupLoomIde() + neoForge() +} + +loom { + accessWidenerPath = project(":common").loom.accessWidenerPath +} + +configurations { + common { + canBeResolved = true + canBeConsumed = false + } + compileClasspath.extendsFrom common + runtimeClasspath.extendsFrom common + developmentNeoForge.extendsFrom common + + // Files in this configuration will be bundled into your mod using the Shadow plugin. + // Don't use the `shadow` configuration from the plugin itself as it's meant for excluding files. + shadowBundle { + canBeResolved = true + canBeConsumed = false + } + archivesBaseName = rootProject.archives_base_name + "-neoforge" +} + +dependencies { + neoForge "net.neoforged:neoforge:$rootProject.neoforge_version" + modImplementation include ("maven.modrinth:midnightlib:${rootProject.midnightlib_version}-neoforge") + + common(project(path: ':common', configuration: 'namedElements')) { transitive false } + shadowBundle project(path: ':common', configuration: 'transformProductionNeoForge') +} + +processResources { + inputs.property 'version', project.version + + filesMatching('META-INF/neoforge.mods.toml') { + expand version: project.version + } +} + +shadowJar { + configurations = [project.configurations.shadowBundle] + archiveClassifier = 'dev-shadow' +} + +remapJar { + input.set shadowJar.archiveFile +} + +sourcesJar { + def commonSources = project(":common").sourcesJar + dependsOn commonSources + from commonSources.archiveFile.map { zipTree(it) } +} + +components.java { + withVariantsFromConfiguration(project.configurations.shadowRuntimeElements) { + skip() + } +} \ No newline at end of file diff --git a/neoforge/gradle.properties b/neoforge/gradle.properties new file mode 100644 index 0000000..2914393 --- /dev/null +++ b/neoforge/gradle.properties @@ -0,0 +1 @@ +loom.platform=neoforge \ No newline at end of file diff --git a/neoforge/src/main/java/eu/midnightdust/picturesign_theora/neoforge/PictureSignClientNeoForge.java b/neoforge/src/main/java/eu/midnightdust/picturesign_theora/neoforge/PictureSignClientNeoForge.java new file mode 100644 index 0000000..14ae687 --- /dev/null +++ b/neoforge/src/main/java/eu/midnightdust/picturesign_theora/neoforge/PictureSignClientNeoForge.java @@ -0,0 +1,13 @@ +package eu.midnightdust.picturesign_theora.neoforge; + +import eu.midnightdust.picturesign_theora.PictureSignTheoraClient; +import net.neoforged.api.distmarker.Dist; +import net.neoforged.fml.common.Mod; + +@SuppressWarnings("all") +@Mod(value = "picturesign-theora", dist = Dist.CLIENT) +public class PictureSignClientNeoForge { + public PictureSignClientNeoForge() { + PictureSignTheoraClient.init(); + } +} \ No newline at end of file diff --git a/neoforge/src/main/resources/META-INF/neoforge.mods.toml b/neoforge/src/main/resources/META-INF/neoforge.mods.toml new file mode 100644 index 0000000..290e184 --- /dev/null +++ b/neoforge/src/main/resources/META-INF/neoforge.mods.toml @@ -0,0 +1,38 @@ +modLoader = "javafml" +loaderVersion = "[2,)" +#issueTrackerURL = "" +license = "MIT License" + +[[mods]] +modId = "picturesign-theora" +version = "${version}" +displayName = "PictureSign: Theora" +logoFile = "icon.png" +authors = "unascribed, Motschen" +description = ''' +Adds support for Theora, Vorbis and OGG to PictureSign +''' + +[[mixins]] +config = "picturesign-theora.mixins.json" + +[[dependencies.visualoverhaul]] +modId = "neoforge" +mandatory = true +versionRange = "[21.0,)" +ordering = "NONE" +side = "BOTH" + +[[dependencies.visualoverhaul]] +modId = "minecraft" +mandatory = true +versionRange = "[1.21,)" +ordering = "NONE" +side = "BOTH" + +[[dependencies.visualoverhaul]] +modId = "picturesign" +mandatory = true +versionRange = "[1.0,)" +ordering = "AFTER" +side = "BOTH" diff --git a/neoforge/src/main/resources/icon.png b/neoforge/src/main/resources/icon.png new file mode 100755 index 0000000000000000000000000000000000000000..2a0d83a361bce056d105e4ed6f07d9a206dd091d GIT binary patch literal 13409 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