Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

resolve issue #1955 (Can not play vorbis audio on Android API 31+) #1956

Merged
merged 7 commits into from
Feb 21, 2023
Original file line number Diff line number Diff line change
Expand Up @@ -110,13 +110,18 @@ static int FileDesc_seek(void *datasource, ogg_int64_t offset, int whence)
wrapper->current = actual_offset;
}

static int FileDesc_close(void *datasource)
static int FileDesc_clear(void *datasource)
{
FileDescWrapper* wrapper = (FileDescWrapper*)datasource;

LOGI("FD close");

return close(wrapper->fd);
LOGI("Clear resources -- delegating closure to the Android ParcelFileDescriptor");

/* release the file descriptor wrapper buffer */
free(wrapper);

wrapper = NULL;

return 0;
}

static long FileDesc_tell(void *datasource)
Expand All @@ -139,7 +144,7 @@ static long FileDesc_tell(void *datasource)
static ov_callbacks FileDescCallbacks = {
FileDesc_read,
FileDesc_seek,
FileDesc_close,
FileDesc_clear,
FileDesc_tell
};

Expand All @@ -157,10 +162,10 @@ static jfieldID nvf_field_bitRate;
static jfieldID nvf_field_totalBytes;
static jfieldID nvf_field_duration;

JNIEXPORT void JNICALL Java_com_jme3_audio_plugins_NativeVorbisFile_nativeInit
JNIEXPORT void JNICALL Java_com_jme3_audio_plugins_NativeVorbisFile_preInit
(JNIEnv *env, jclass clazz)
{
LOGI("nativeInit");
LOGI("preInit");

nvf_field_ovf = (*env)->GetFieldID(env, clazz, "ovf", "Ljava/nio/ByteBuffer;");;
nvf_field_seekable = (*env)->GetFieldID(env, clazz, "seekable", "Z");
Expand All @@ -171,7 +176,7 @@ JNIEXPORT void JNICALL Java_com_jme3_audio_plugins_NativeVorbisFile_nativeInit
nvf_field_duration = (*env)->GetFieldID(env, clazz, "duration", "F");
}

JNIEXPORT void JNICALL Java_com_jme3_audio_plugins_NativeVorbisFile_open
JNIEXPORT void JNICALL Java_com_jme3_audio_plugins_NativeVorbisFile_init
(JNIEnv *env, jobject nvf, jint fd, jlong off, jlong len)
{
LOGI("open: fd = %d, off = %lld, len = %lld", fd, off, len);
pavly-gerges marked this conversation as resolved.
Show resolved Hide resolved
Expand Down Expand Up @@ -246,7 +251,7 @@ JNIEXPORT void JNICALL Java_com_jme3_audio_plugins_NativeVorbisFile_seekTime
}
}

JNIEXPORT jint JNICALL Java_com_jme3_audio_plugins_NativeVorbisFile_read
JNIEXPORT jint JNICALL Java_com_jme3_audio_plugins_NativeVorbisFile_readIntoArray
(JNIEnv *env, jobject nvf, jbyteArray buf, jint off, jint len)
{
int bitstream = -1;
Expand Down Expand Up @@ -288,7 +293,7 @@ JNIEXPORT jint JNICALL Java_com_jme3_audio_plugins_NativeVorbisFile_read
return result;
}

JNIEXPORT void JNICALL Java_com_jme3_audio_plugins_NativeVorbisFile_readFully
JNIEXPORT void JNICALL Java_com_jme3_audio_plugins_NativeVorbisFile_readIntoBuffer
(JNIEnv *env, jobject nvf, jobject buf)
{
int bitstream = -1;
Expand Down Expand Up @@ -330,19 +335,19 @@ JNIEXPORT void JNICALL Java_com_jme3_audio_plugins_NativeVorbisFile_readFully
}
}

JNIEXPORT void JNICALL Java_com_jme3_audio_plugins_NativeVorbisFile_close
JNIEXPORT void JNICALL Java_com_jme3_audio_plugins_NativeVorbisFile_clearResources
(JNIEnv *env, jobject nvf)
{
LOGI("close");
LOGI("clearResources");

jobject ovfBuf = (*env)->GetObjectField(env, nvf, nvf_field_ovf);
OggVorbis_File* ovf = (OggVorbis_File*) (*env)->GetDirectBufferAddress(env, ovfBuf);
FileDescWrapper* wrapper = (FileDescWrapper*) ovf->datasource;
wrapper->env = env;

/* release the ovf resources */
ov_clear(ovf);

free(wrapper);
/* release the ovf buffer */
free(ovf);
ovf = NULL;
/* destroy the java reference object */
(*env)->SetObjectField(env, nvf, nvf_field_ovf, NULL);
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,49 @@
/*
* Copyright (c) 2009-2023 jMonkeyEngine
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.jme3.audio.plugins;

import java.io.IOException;
import java.nio.ByteBuffer;

/**
* Represents the android implementation for the native <a href="https://xiph.org"/> vorbis file decoder.
* This decoder initializes an OggVorbis_File from an already opened file designated by the {@link NativeVorbisFile#fd}.
* <br/>
*
* Code by Kirill Vainer.
* <br/>
*
* Modified by pavl_g.
*/
public class NativeVorbisFile {

public int fd;
Expand All @@ -16,22 +57,69 @@ public class NativeVorbisFile {

static {
System.loadLibrary("decodejme");
nativeInit();
preInit();
}

public NativeVorbisFile(int fd, long off, long len) throws IOException {
open(fd, off, len);
/**
* Initializes an ogg vorbis native file from a file descriptor [fd] of an already opened file.
*
* @param fd an integer representing the file descriptor
* @param offset an integer indicating the start of the buffer
* @param length an integer indicating the end of the buffer
* @throws IOException in cases of a failure to initialize the vorbis file
*/
public NativeVorbisFile(int fd, long offset, long length) throws IOException {
init(fd, offset, length);
}

private native void open(int fd, long off, long len) throws IOException;

/**
* Seeks to a playback time relative to the decompressed pcm (Pulse-code modulation) stream.
*
* @param time the playback seek time
* @throws IOException if the seek is not successful
*/
public native void seekTime(double time) throws IOException;

public native int read(byte[] buf, int off, int len) throws IOException;
/**
* Reads the vorbis file into a primitive byte buffer [buf] with an [offset] indicating the read start and a [length] indicating the read end on the output buffer.
pavly-gerges marked this conversation as resolved.
Show resolved Hide resolved
* This is used for reading a particular chunk of data into a primitive array.
*
* @param buffer a primitive byte buffer to read the data into it
* @param offset an integer representing the offset or the start of the data read
* @param length an integer representing the end of the data read
* @return the number of the read bytes, (-1) if the reading has failed indicating an EOF,
* returns (0) if the reading has failed or the primitive [buffer] passed is null
* @throws IOException if the library has failed to read the file into the [out] buffer
* or if the java primitive byte array [buffer] is inaccessible
*/
public native int readIntoArray(byte[] buffer, int offset, int length) throws IOException;

/**
* Reads the vorbis file into a direct {@link java.nio.ByteBuffer}, starting from offset [0] till the buffer end on the output buffer.
*
* @param out a reference to the output direct buffer
* @throws IOException if a premature EOF is encountered before reaching the end of the buffer
* or if the library has failed to read the file into the [out] buffer
*/
public native void readIntoBuffer(ByteBuffer out) throws IOException;

public native void readFully(ByteBuffer out) throws IOException;
/**
* Clears the native resources and destroys the buffer {@link NativeVorbisFile#ovf} reference.
*/
public native void clearResources();

public native void close();
/**
* Prepares the java fields for the native environment.
*/
private static native void preInit();

public static native void nativeInit();
/**
* Initializes an ogg vorbis native file from a file descriptor [fd] of an already opened file.
*
* @param fd an integer representing the file descriptor
* @param offset an integer representing the start of the buffer
* @param length an integer representing the length of the buffer
* @throws IOException in cases of a failure to initialize the vorbis file
*/
private native void init(int fd, long offset, long length) throws IOException;
}
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,12 @@ public int read() throws IOException {

@Override
public int read(byte[] buf) throws IOException {
return file.read(buf, 0, buf.length);
return file.readIntoArray(buf, 0, buf.length);
}

@Override
public int read(byte[] buf, int off, int len) throws IOException {
return file.read(buf, off, len);
return file.readIntoArray(buf, off, len);
}

@Override
Expand All @@ -57,7 +57,7 @@ public void setTime(float time) {

@Override
public void close() throws IOException {
file.close();
file.clearResources();
afd.close();
}
}
Expand All @@ -71,14 +71,14 @@ private static AudioBuffer loadBuffer(AssetInfo assetInfo) throws IOException {
int fd = afd.getParcelFileDescriptor().getFd();
file = new NativeVorbisFile(fd, afd.getStartOffset(), afd.getLength());
ByteBuffer data = BufferUtils.createByteBuffer(file.totalBytes);
file.readFully(data);
file.readIntoBuffer(data);
AudioBuffer ab = new AudioBuffer();
ab.setupFormat(file.channels, 16, file.sampleRate);
ab.updateData(data);
return ab;
} finally {
if (file != null) {
file.close();
file.clearResources();
}
if (afd != null) {
afd.close();
Expand Down Expand Up @@ -107,7 +107,7 @@ private static AudioStream loadStream(AssetInfo assetInfo) throws IOException {
} finally {
if (!success) {
if (file != null) {
file.close();
file.clearResources();
}
if (afd != null) {
afd.close();
Expand Down