diff --git a/java/org/cef/CefClient.java b/java/org/cef/CefClient.java index cb3a58c7..d2d956de 100644 --- a/java/org/cef/CefClient.java +++ b/java/org/cef/CefClient.java @@ -22,10 +22,7 @@ import org.cef.callback.CefPrintDialogCallback; import org.cef.callback.CefPrintJobCallback; import org.cef.handler.*; -import org.cef.misc.BoolRef; -import org.cef.misc.CefAudioParameters; -import org.cef.misc.CefPrintSettings; -import org.cef.misc.StringRef; +import org.cef.misc.*; import org.cef.network.CefRequest; import org.cef.network.CefRequest.TransitionType; import org.cef.network.CefResponse; @@ -815,7 +812,7 @@ public void onAudioStreamStarted(CefBrowser browser, CefAudioParameters params, } @Override - public void onAudioStreamPacket(CefBrowser browser, float[] data, int frames, long pts) { + public void onAudioStreamPacket(CefBrowser browser, DataPointer data, int frames, long pts) { if (audioHandler_ != null) audioHandler_.onAudioStreamPacket(browser, data, frames, pts); } diff --git a/java/org/cef/handler/CefAudioHandler.java b/java/org/cef/handler/CefAudioHandler.java index 79fda7cf..5c2fcdd0 100644 --- a/java/org/cef/handler/CefAudioHandler.java +++ b/java/org/cef/handler/CefAudioHandler.java @@ -6,6 +6,7 @@ import org.cef.browser.CefBrowser; import org.cef.misc.CefAudioParameters; +import org.cef.misc.DataPointer; /** * Implement this interface to handle events related to audio playing. @@ -16,7 +17,7 @@ public interface CefAudioHandler { void onAudioStreamStarted(CefBrowser browser, CefAudioParameters params, int channels); - void onAudioStreamPacket(CefBrowser browser, float[] data, int frames, long pts); + void onAudioStreamPacket(CefBrowser browser, DataPointer data, int frames, long pts); void onAudioStreamStopped(CefBrowser browser); diff --git a/java/org/cef/handler/CefAudioHandlerAdapter.java b/java/org/cef/handler/CefAudioHandlerAdapter.java index 500e5c52..14188ba7 100644 --- a/java/org/cef/handler/CefAudioHandlerAdapter.java +++ b/java/org/cef/handler/CefAudioHandlerAdapter.java @@ -6,6 +6,7 @@ import org.cef.browser.CefBrowser; import org.cef.misc.CefAudioParameters; +import org.cef.misc.DataPointer; /** * Implement this interface to handle events related to audio playing. @@ -19,7 +20,7 @@ public boolean getAudioParameters(CefBrowser browser, CefAudioParameters params) public void onAudioStreamStarted(CefBrowser browser, CefAudioParameters params, int channels) { } - public void onAudioStreamPacket(CefBrowser browser, float[] data, int frames, long pts) { + public void onAudioStreamPacket(CefBrowser browser, DataPointer data, int frames, long pts) { } public void onAudioStreamStopped(CefBrowser browser) { diff --git a/java/org/cef/misc/DataPointer.java b/java/org/cef/misc/DataPointer.java new file mode 100644 index 00000000..19134fc6 --- /dev/null +++ b/java/org/cef/misc/DataPointer.java @@ -0,0 +1,85 @@ +package org.cef.misc; + +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; +import java.nio.ByteBuffer; + +public class DataPointer { + private final long address; + private ByteBuffer dataBuffer; + boolean initialized = false; + int alignment = 3; + + public DataPointer(long address) { + this.address = address; + } + + public DataPointer forCapacity(int capacity) { + try { + dataBuffer = (ByteBuffer) memByteBuffer.invoke(address, capacity); + initialized = true; + return this; + } catch (Throwable err) { + throw new RuntimeException("Failed to invoke memByteBuffer?", err); + } + } + + public DataPointer withAlignment(int alignment) { + this.alignment = alignment; + return this; + } + + public long getAddress() { + return address; + } + + public DataPointer getData(int offset) { + if (!initialized) throw new RuntimeException("DataPoint#forCapacity must be called before the data can be accessed."); + return new DataPointer(dataBuffer.getLong(offset << alignment)); + } + + public long getLong(int offset) { + if (!initialized) throw new RuntimeException("DataPoint#forCapacity must be called before the data can be accessed."); + return dataBuffer.getLong(offset << alignment); + } + + public int getInt(int offset) { + if (!initialized) throw new RuntimeException("DataPoint#forCapacity must be called before the data can be accessed."); + return dataBuffer.getInt(offset << alignment); + } + + public short getShort(int offset) { + if (!initialized) throw new RuntimeException("DataPoint#forCapacity must be called before the data can be accessed."); + return dataBuffer.getShort(offset << alignment); + } + + public byte getByte(int offset) { + if (!initialized) throw new RuntimeException("DataPoint#forCapacity must be called before the data can be accessed."); + return dataBuffer.get(offset << alignment); + } + + public double getDouble(int offset) { + if (!initialized) throw new RuntimeException("DataPoint#forCapacity must be called before the data can be accessed."); + return dataBuffer.getDouble(offset << alignment); + } + + public float getFloat(int offset) { + if (!initialized) throw new RuntimeException("DataPoint#forCapacity must be called before the data can be accessed."); + return dataBuffer.getFloat(offset << alignment); + } + + // TODO: ideally we'd just directly depend on lwjgl, since we require it for GLFW anyway + private static final MethodHandle memByteBuffer; + + static { + MethodHandles.Lookup lookup = MethodHandles.lookup(); + try { + Class clz = Class.forName("org.lwjgl.system.MemoryUtil", false, lookup.lookupClass().getClassLoader()); + memByteBuffer = lookup.findStatic(clz, "memByteBuffer", MethodType.methodType(ByteBuffer.class, new Class[]{Long.TYPE, Integer.TYPE})); + } catch (Throwable err) { + System.err.println("Could not find LWJGL MemoryUtil's memByteBuffer method.\nAre you using LWJGL 3.x?"); + throw new RuntimeException(err); + } + } +} diff --git a/native/audio_handler.cpp b/native/audio_handler.cpp index 3e7a62b6..045cda4a 100644 --- a/native/audio_handler.cpp +++ b/native/audio_handler.cpp @@ -11,7 +11,7 @@ AudioHandler::AudioHandler(JNIEnv* env, jobject handler) : handle_(env, handler) {} -jobject jniParams(ScopedJNIEnv env, const CefAudioParameters& params) { +jobject jniParams(ScopedJNIEnv env, jclass clsProps, const CefAudioParameters& params) { jclass cls = env->FindClass("org/cef/misc/CefChannelLayout"); if (cls == nullptr) { // std::cout << "Could not find class 0"; @@ -24,7 +24,7 @@ jobject jniParams(ScopedJNIEnv env, const CefAudioParameters& params) { } jobject layout = env->CallStaticObjectMethod(cls, getLayout, (int) params.channel_layout); - cls = env->FindClass("org/cef/misc/CefAudioParameters"); + cls = clsProps; if (cls == nullptr) { // std::cout << "Could not find class 1"; return nullptr; @@ -39,6 +39,11 @@ jobject jniParams(ScopedJNIEnv env, const CefAudioParameters& params) { return parameters; } +jobject jniParams(ScopedJNIEnv env, const CefAudioParameters& params) { + jclass cls = env->FindClass("org/cef/misc/CefAudioParameters"); + return jniParams(env, cls, params); +} + bool AudioHandler::GetAudioParameters(CefRefPtr browser, CefAudioParameters& params) { ScopedJNIEnv env; @@ -48,10 +53,12 @@ bool AudioHandler::GetAudioParameters(CefRefPtr browser, ScopedJNIBrowser jbrowser(env, browser); jboolean jreturn = JNI_FALSE; + jclass cls = env->FindClass("org/cef/misc/CefAudioParameters"); + jobject paramsJni = jniParams(env, cls, params); JNI_CALL_METHOD(env, handle_, "getAudioParameters", "(Lorg/cef/browser/CefBrowser;Lorg/cef/misc/CefAudioParameters;)Z", Boolean, - jreturn, jbrowser.get(), jniParams(env, params)); + jreturn, jbrowser.get(), paramsJni); return (jreturn != JNI_FALSE); } @@ -76,17 +83,12 @@ void AudioHandler::OnAudioStreamPacket(CefRefPtr browser, const floa ScopedJNIBrowser jbrowser(env, browser); - // TODO: this should truthfully be using a float buffer, but I'm not yet sure how to do that from JNI - jfloatArray jArray = env->NewFloatArray(frames); - int size = frames; - jfloat* fill = (jfloat*) malloc(size * sizeof(jfloat)); - for (int i = 0; i < size; i++) - fill[i] = data[0][i]; - env->SetFloatArrayRegion(jArray, 0, size, fill); + ScopedJNIObjectLocal dataPtr( + env, NewJNIObject(env, "org/cef/misc/DataPointer", "(J)V", (jlong) data)); JNI_CALL_VOID_METHOD(env, handle_, "onAudioStreamPacket", - "(Lorg/cef/browser/CefBrowser;[FIJ)V", - jbrowser.get(), jArray, frames, (long long) pts); + "(Lorg/cef/browser/CefBrowser;Lorg/cef/misc/DataPointer;IJ)V", + jbrowser.get(), dataPtr.get(), frames, (long long) pts); } void AudioHandler::OnAudioStreamStopped(CefRefPtr browser) {