aboutsummaryrefslogtreecommitdiffstats
path: root/src/jogl/classes
diff options
context:
space:
mode:
Diffstat (limited to 'src/jogl/classes')
-rw-r--r--src/jogl/classes/com/jogamp/opengl/util/av/AudioSink.java104
-rw-r--r--src/jogl/classes/jogamp/opengl/openal/av/ALAudioSink.java23
-rw-r--r--src/jogl/classes/jogamp/opengl/util/av/JavaSoundAudioSink.java4
-rw-r--r--src/jogl/classes/jogamp/opengl/util/av/NullAudioSink.java2
-rw-r--r--src/jogl/classes/jogamp/opengl/util/av/impl/FFMPEGMediaPlayer.java46
5 files changed, 127 insertions, 52 deletions
diff --git a/src/jogl/classes/com/jogamp/opengl/util/av/AudioSink.java b/src/jogl/classes/com/jogamp/opengl/util/av/AudioSink.java
index cacf8c5a4..2b8da8af9 100644
--- a/src/jogl/classes/com/jogamp/opengl/util/av/AudioSink.java
+++ b/src/jogl/classes/com/jogamp/opengl/util/av/AudioSink.java
@@ -36,6 +36,18 @@ import jogamp.opengl.Debug;
public interface AudioSink {
public static final boolean DEBUG = Debug.debug("AudioSink");
+ /** Default frame duration in millisecond, i.e. 1 frame per {@value} ms. */
+ public static final int DefaultFrameDuration = 32;
+
+ /** Initial audio queue size in milliseconds. {@value} ms, i.e. 16 frames per 32 ms. See {@link #init(AudioDataFormat, float, int, int, int)}.*/
+ public static final int DefaultInitialQueueSize = 16 * 32; // 512 ms
+ /** Audio queue grow size in milliseconds. {@value} ms, i.e. 16 frames per 32 ms. See {@link #init(AudioDataFormat, float, int, int, int)}.*/
+ public static final int DefaultQueueGrowAmount = 16 * 32; // 512 ms
+ /** Audio queue limit w/ video in milliseconds. {@value} ms, i.e. 96 frames per 32 ms. See {@link #init(AudioDataFormat, float, int, int, int)}.*/
+ public static final int DefaultQueueLimitWithVideo = 96 * 32; // 3072 ms
+ /** Audio queue limit w/o video in milliseconds. {@value} ms, i.e. 32 frames per 32 ms. See {@link #init(AudioDataFormat, float, int, int, int)}.*/
+ public static final int DefaultQueueLimitAudioOnly = 32 * 32; // 1024 ms
+
/** Specifies the audio data type. Currently only PCM is supported. */
public static enum AudioDataType { PCM };
@@ -66,21 +78,65 @@ public interface AudioSink {
public final boolean littleEndian;
/**
- * Returns the duration in milliseconds of the given byte count according
- * to {@link #sampleSize}, {@link #channelCount} and {@link #sampleRate}.
+ * Returns the byte size of the given milliseconds
+ * according to {@link #sampleSize}, {@link #channelCount} and {@link #sampleRate}.
*/
- public final int getDuration(int byteCount) {
+ public final int getByteSize(int millisecs) {
final int bytesPerSample = sampleSize >>> 3; // /8
- return byteCount / ( channelCount * bytesPerSample * ( sampleRate / 1000 ) );
+ return millisecs * ( channelCount * bytesPerSample * ( sampleRate / 1000 ) );
}
/**
- * Returns the byte count of the given milliseconds according
- * to {@link #sampleSize}, {@link #channelCount} and {@link #sampleRate}.
+ * Returns the duration in milliseconds of the given byte count
+ * according to {@link #sampleSize}, {@link #channelCount} and {@link #sampleRate}.
*/
- public final int getByteCount(int millisecs) {
+ public final int getBytesDuration(int byteCount) {
final int bytesPerSample = sampleSize >>> 3; // /8
- return millisecs * ( channelCount * bytesPerSample * ( sampleRate / 1000 ) );
+ return byteCount / ( channelCount * bytesPerSample * ( sampleRate / 1000 ) );
+ }
+
+ /**
+ * Returns the duration in milliseconds of the given and sample count per frame and channel
+ * according to the {@link #sampleRate}, i.e.
+ * <pre>
+ * ( 1000f * sampleCount ) / sampleRate
+ * </pre>
+ * @param sampleCount sample count per frame and channel
+ */
+ public final float getSamplesDuration(int sampleCount) {
+ return ( 1000f * (float) sampleCount ) / (float)sampleRate;
+ }
+
+ /**
+ * Returns the rounded frame count of the given milliseconds and frame duration.
+ * <pre>
+ * Math.max( 1, millisecs / frameDuration + 0.5f )
+ * </pre>
+ * <p>
+ * Note: <code>frameDuration</code> can be derived by <i>sample count per frame and channel</i>
+ * via {@link #getSamplesDuration(int)}.
+ * </p>
+ * @param millisecs time in milliseconds
+ * @param frameDuration duration per frame in milliseconds.
+ */
+ public final int getFrameCount(int millisecs, float frameDuration) {
+ return Math.max(1, (int) ( (float)millisecs / frameDuration + 0.5f ));
+ }
+
+ /**
+ * Returns the byte size of given sample count
+ * according to the {@link #sampleSize}, i.e.:
+ * <pre>
+ * sampleCount * ( sampleSize / 8 )
+ * </pre>
+ * <p>
+ * Note: To retrieve the byte size for all channels, you need to pre-multiply <code>sampleCount</code>
+ * with {@link #channelCount}.
+ * </p>
+ * @param sampleCount sample count
+ */
+ public final int getSamplesByteSize(int sampleCount) {
+ return sampleCount * ( sampleSize >>> 3 );
}
public String toString() {
@@ -175,12 +231,16 @@ public interface AudioSink {
* The {@link #DefaultFormat} <i>should be</i> supported by all implementations.
* </p>
* @param requestedFormat the requested {@link AudioDataFormat}.
- * @param initialFrameCount initial number of frames to queue in this sink
- * @param frameGrowAmount number of frames to grow queue if full
- * @param frameLimit maximum number of frames
- * @return if successful the chosen AudioDataFormat based on the <code>requestedFormat</code> and this sinks capabilities, otherwise <code>null</code>.
+ * @param frameDuration average or fixed frame duration in milliseconds
+ * helping a caching {@link AudioFrame} based implementation to determine the frame count in the queue.
+ * See {@link #DefaultFrameDuration}.
+ * @param initialQueueSize initial time in milliseconds to queue in this sink, see {@link #DefaultInitialQueueSize}.
+ * @param queueGrowAmount time in milliseconds to grow queue if full, see {@link #DefaultQueueGrowAmount}.
+ * @param queueLimit maximum time in milliseconds the queue can hold (and grow), see {@link #DefaultQueueLimitWithVideo} and {@link #DefaultQueueLimitAudioOnly}.
+ * @return if successful the chosen AudioDataFormat based on the <code>requestedFormat</code> and this sinks capabilities, otherwise <code>null</code>.
*/
- public AudioDataFormat initSink(AudioDataFormat requestedFormat, int initialFrameCount, int frameGrowAmount, int frameLimit);
+ public AudioDataFormat init(AudioDataFormat requestedFormat, float frameDuration,
+ int initialQueueSize, int queueGrowAmount, int queueLimit);
/**
* Returns true, if {@link #play()} has been requested <i>and</i> the sink is still playing,
@@ -207,7 +267,7 @@ public interface AudioSink {
/**
* Flush all queued buffers, implies {@link #pause()}.
* <p>
- * {@link #initSink(AudioDataFormat, int, int, int)} must be called first.
+ * {@link #init(AudioDataFormat, float, int, int, int)} must be called first.
* </p>
* @see #play()
* @see #pause()
@@ -220,17 +280,17 @@ public interface AudioSink {
/**
* Returns the number of allocated buffers as requested by
- * {@link #initSink(AudioDataFormat, int, int, int)}.
+ * {@link #init(AudioDataFormat, float, int, int, int)}.
*/
public int getFrameCount();
- /** @return the current enqueued frames count since {@link #initSink(AudioDataFormat, int, int, int)}. */
+ /** @return the current enqueued frames count since {@link #init(AudioDataFormat, float, int, int, int)}. */
public int getEnqueuedFrameCount();
/**
* Returns the current number of frames queued for playing.
* <p>
- * {@link #initSink(AudioDataFormat, int, int, int)} must be called first.
+ * {@link #init(AudioDataFormat, float, int, int, int)} must be called first.
* </p>
*/
public int getQueuedFrameCount();
@@ -238,7 +298,7 @@ public interface AudioSink {
/**
* Returns the current number of bytes queued for playing.
* <p>
- * {@link #initSink(AudioDataFormat, int, int, int)} must be called first.
+ * {@link #init(AudioDataFormat, float, int, int, int)} must be called first.
* </p>
*/
public int getQueuedByteCount();
@@ -246,7 +306,7 @@ public interface AudioSink {
/**
* Returns the current queued frame time in milliseconds for playing.
* <p>
- * {@link #initSink(AudioDataFormat, int, int, int)} must be called first.
+ * {@link #init(AudioDataFormat, float, int, int, int)} must be called first.
* </p>
*/
public int getQueuedTime();
@@ -259,7 +319,7 @@ public interface AudioSink {
/**
* Returns the current number of frames in the sink available for writing.
* <p>
- * {@link #initSink(AudioDataFormat, int, int, int)} must be called first.
+ * {@link #init(AudioDataFormat, float, int, int, int)} must be called first.
* </p>
*/
public int getFreeFrameCount();
@@ -270,7 +330,7 @@ public interface AudioSink {
* The data must comply with the chosen {@link AudioDataFormat} as returned by {@link #initSink(AudioDataFormat)}.
* </p>
* <p>
- * {@link #initSink(AudioDataFormat, int, int, int)} must be called first.
+ * {@link #init(AudioDataFormat, float, int, int, int)} must be called first.
* </p>
* @returns the enqueued internal {@link AudioFrame}, which may differ from the input <code>audioDataFrame</code>.
* @deprecated User shall use {@link #enqueueData(int, ByteBuffer, int)}, which allows implementation
@@ -284,7 +344,7 @@ public interface AudioSink {
* The data must comply with the chosen {@link AudioDataFormat} as returned by {@link #initSink(AudioDataFormat)}.
* </p>
* <p>
- * {@link #initSink(AudioDataFormat, int, int, int)} must be called first.
+ * {@link #init(AudioDataFormat, float, int, int, int)} must be called first.
* </p>
* @returns the enqueued internal {@link AudioFrame}.
*/
diff --git a/src/jogl/classes/jogamp/opengl/openal/av/ALAudioSink.java b/src/jogl/classes/jogamp/opengl/openal/av/ALAudioSink.java
index 5f98c3ad2..f9ca0c028 100644
--- a/src/jogl/classes/jogamp/opengl/openal/av/ALAudioSink.java
+++ b/src/jogl/classes/jogamp/opengl/openal/av/ALAudioSink.java
@@ -213,7 +213,8 @@ public class ALAudioSink implements AudioSink {
return "ALAudioSink[init "+initialized+", playRequested "+playRequested+", device "+deviceSpecifier+", ctx "+toHexString(ctxHash)+", alSource "+alSrcName+
", chosen "+chosenFormat+", alFormat "+toHexString(alFormat)+
", playSpeed "+playSpeed+", buffers[total "+alBuffersLen+", avail "+alFramesAvail.size()+", "+
- "queued["+alFramesPlaying.size()+", apts "+getPTS()+", "+getQueuedTime() + " ms, " + alBufferBytesQueued+" bytes]";
+ "queued["+alFramesPlaying.size()+", apts "+getPTS()+", "+getQueuedTime() + " ms, " + alBufferBytesQueued+" bytes], "+
+ "queue[g "+frameGrowAmount+", l "+frameLimit+"]";
}
public final String getPerfString() {
final int alBuffersLen = null != alBufferNames ? alBufferNames.length : 0;
@@ -226,7 +227,7 @@ public class ALAudioSink implements AudioSink {
}
@Override
- public final AudioDataFormat initSink(AudioDataFormat requestedFormat, int initialFrameCount, int frameGrowAmount, int frameLimit) {
+ public final AudioDataFormat init(AudioDataFormat requestedFormat, float frameDuration, int initialQueueSize, int queueGrowAmount, int queueLimit) {
if( !staticAvailable ) {
return null;
}
@@ -260,6 +261,10 @@ public class ALAudioSink implements AudioSink {
// Allocate buffers
destroyBuffers();
{
+ final float useFrameDuration = frameDuration > 1f ? frameDuration : AudioSink.DefaultFrameDuration;
+ final int initialFrameCount = requestedFormat.getFrameCount(
+ initialQueueSize > 0 ? initialQueueSize : AudioSink.DefaultInitialQueueSize, useFrameDuration);
+ // frameDuration, int initialQueueSize, int queueGrowAmount, int queueLimit) {
alBufferNames = new int[initialFrameCount];
al.alGenBuffers(initialFrameCount, alBufferNames, 0);
final int err = al.alGetError();
@@ -274,8 +279,10 @@ public class ALAudioSink implements AudioSink {
alFramesAvail = new LFRingbuffer<ALAudioFrame>(alFrames);
alFramesPlaying = new LFRingbuffer<ALAudioFrame>(ALAudioFrame[].class, initialFrameCount);
- this.frameGrowAmount = frameGrowAmount;
- this.frameLimit = frameLimit;
+ this.frameGrowAmount = requestedFormat.getFrameCount(
+ queueGrowAmount > 0 ? queueGrowAmount : AudioSink.DefaultQueueGrowAmount, useFrameDuration);
+ this.frameLimit = requestedFormat.getFrameCount(
+ queueLimit > 0 ? queueLimit : AudioSink.DefaultQueueLimitWithVideo, useFrameDuration);
}
} finally {
unlockContext();
@@ -423,9 +430,9 @@ public class ALAudioSink implements AudioSink {
if( wait && val[0] < releaseBufferLimes ) {
i++;
// clip wait at [2 .. 100] ms
- final int avgBufferDura = chosenFormat.getDuration( alBufferBytesQueued / alFramesPlaying.size() );
+ final int avgBufferDura = chosenFormat.getBytesDuration( alBufferBytesQueued / alFramesPlaying.size() );
final int sleep = Math.max(2, Math.min(100, releaseBufferLimes * avgBufferDura));
- if( DEBUG ) {
+ if( DEBUG || true ) {
System.err.println(getThreadName()+": ALAudioSink: Dequeue.wait["+i+"]: avgBufferDura "+avgBufferDura+", releaseBufferLimes "+releaseBufferLimes+", sleep "+sleep+" ms, playImpl "+isPlayingImpl1()+", processed "+val[0]+", "+this);
}
unlockContext();
@@ -512,7 +519,7 @@ public class ALAudioSink implements AudioSink {
throw new RuntimeException("ALError "+toHexString(alErr)+" while makeCurrent. "+this);
}
- final int duration = chosenFormat.getDuration(byteCount);
+ final int duration = chosenFormat.getBytesDuration(byteCount);
final boolean dequeueDone;
if( alFramesAvail.isEmpty() ) {
// try to dequeue first
@@ -738,7 +745,7 @@ public class ALAudioSink implements AudioSink {
if( !initialized || null == chosenFormat ) {
return 0;
}
- return chosenFormat.getDuration(alBufferBytesQueued);
+ return chosenFormat.getBytesDuration(alBufferBytesQueued);
}
@Override
diff --git a/src/jogl/classes/jogamp/opengl/util/av/JavaSoundAudioSink.java b/src/jogl/classes/jogamp/opengl/util/av/JavaSoundAudioSink.java
index dcf096f05..b46b64748 100644
--- a/src/jogl/classes/jogamp/opengl/util/av/JavaSoundAudioSink.java
+++ b/src/jogl/classes/jogamp/opengl/util/av/JavaSoundAudioSink.java
@@ -68,7 +68,7 @@ public class JavaSoundAudioSink implements AudioSink {
}
@Override
- public AudioDataFormat initSink(AudioDataFormat requestedFormat, int initialFrameCount, int frameGrowAmount, int frameLimit) {
+ public AudioDataFormat init(AudioDataFormat requestedFormat, float frameDuration, int initialQueueSize, int queueGrowAmount, int queueLimit) {
if( !staticAvailable ) {
return null;
}
@@ -178,7 +178,7 @@ public class JavaSoundAudioSink implements AudioSink {
@Override
public AudioFrame enqueueData(int pts, ByteBuffer bytes, int byteCount) {
- return enqueueData(new AudioDataFrame(pts, chosenFormat.getDuration(byteCount), bytes, byteCount));
+ return enqueueData(new AudioDataFrame(pts, chosenFormat.getBytesDuration(byteCount), bytes, byteCount));
}
@Override
diff --git a/src/jogl/classes/jogamp/opengl/util/av/NullAudioSink.java b/src/jogl/classes/jogamp/opengl/util/av/NullAudioSink.java
index af4b83bb6..067322819 100644
--- a/src/jogl/classes/jogamp/opengl/util/av/NullAudioSink.java
+++ b/src/jogl/classes/jogamp/opengl/util/av/NullAudioSink.java
@@ -33,7 +33,7 @@ public class NullAudioSink implements AudioSink {
}
@Override
- public AudioDataFormat initSink(AudioDataFormat requestedFormat, int initialFrameCount, int frameGrowAmount, int frameLimit) {
+ public AudioDataFormat init(AudioDataFormat requestedFormat, float frameDuration, int initialQueueSize, int queueGrowAmount, int queueLimit) {
return requestedFormat;
}
diff --git a/src/jogl/classes/jogamp/opengl/util/av/impl/FFMPEGMediaPlayer.java b/src/jogl/classes/jogamp/opengl/util/av/impl/FFMPEGMediaPlayer.java
index a800f2a31..e59a8849f 100644
--- a/src/jogl/classes/jogamp/opengl/util/av/impl/FFMPEGMediaPlayer.java
+++ b/src/jogl/classes/jogamp/opengl/util/av/impl/FFMPEGMediaPlayer.java
@@ -167,14 +167,10 @@ public class FFMPEGMediaPlayer extends GLMediaPlayerImpl {
// Audio
//
- /** Initial audio frame count, ALAudioSink may grow buffer! */
- private int initialAudioFrameCount = AV_DEFAULT_AFRAMES;
- private final int audioFrameGrowAmount = 8;
- private final int audioFrameLimitWithVideo = 64; // 128;
- private final int audioFrameLimitAudioOnly = 32; // 64;
private SampleFormat aSampleFmt = null;
private AudioSink.AudioDataFormat avChosenAudioFormat;
private AudioSink.AudioDataFormat sinkChosenAudioFormat;
+ private int audioSamplesPerFrameAndChannel = 0;
public FFMPEGMediaPlayer() {
if(!available) {
@@ -237,7 +233,7 @@ public class FFMPEGMediaPlayer extends GLMediaPlayerImpl {
if(null == audioSink) {
throw new GLException("AudioSink null");
}
- final int audioFrameLimit;
+ final int audioQueueLimit;
if( null != gl ) {
final GLContextImpl ctx = (GLContextImpl)gl.getContext();
AccessController.doPrivileged(new PrivilegedAction<Object>() {
@@ -250,12 +246,18 @@ public class FFMPEGMediaPlayer extends GLMediaPlayerImpl {
setGLFuncs0(moviePtr, procAddrGLTexSubImage2D, procAddrGLGetError, procAddrGLFlush, procAddrGLFinish);
return null;
} } );
- audioFrameLimit = audioFrameLimitWithVideo;
+ audioQueueLimit = AudioSink.DefaultQueueLimitWithVideo;
} else {
- audioFrameLimit = audioFrameLimitAudioOnly;
+ audioQueueLimit = AudioSink.DefaultQueueLimitAudioOnly;
}
-
- sinkChosenAudioFormat = audioSink.initSink(avChosenAudioFormat, initialAudioFrameCount, audioFrameGrowAmount, audioFrameLimit);
+ final float frameDuration;
+ if( audioSamplesPerFrameAndChannel > 0 ) {
+ frameDuration= avChosenAudioFormat.getSamplesDuration(audioSamplesPerFrameAndChannel);
+ } else {
+ frameDuration = AudioSink.DefaultFrameDuration;
+ }
+
+ sinkChosenAudioFormat = audioSink.init(avChosenAudioFormat, frameDuration, AudioSink.DefaultInitialQueueSize, AudioSink.DefaultQueueGrowAmount, audioQueueLimit);
if(DEBUG) {
System.err.println("initGL: p3 avChosen "+avChosenAudioFormat+", chosen "+sinkChosenAudioFormat);
}
@@ -263,7 +265,11 @@ public class FFMPEGMediaPlayer extends GLMediaPlayerImpl {
System.err.println("AudioSink "+audioSink.getClass().getName()+" does not support "+avChosenAudioFormat+", using Null");
audioSink.destroy();
audioSink = AudioSinkFactory.createNull();
- sinkChosenAudioFormat = audioSink.initSink(avChosenAudioFormat, initialAudioFrameCount, audioFrameGrowAmount, audioFrameLimit);
+ sinkChosenAudioFormat = audioSink.init(avChosenAudioFormat, 0, AudioSink.DefaultInitialQueueSize, AudioSink.DefaultQueueGrowAmount, audioQueueLimit);
+ }
+ if(DEBUG) {
+ System.err.println("initGL: p4 chosen "+sinkChosenAudioFormat);
+ System.err.println("initGL: "+audioSink);
}
if( null != gl ) {
@@ -303,14 +309,16 @@ public class FFMPEGMediaPlayer extends GLMediaPlayerImpl {
* @param tWd1
* @param tWd2
* @param audioFrameCount snooped audio-frame-count per video-frame, maybe 0
- * @param sampleFmt
- * @param sampleRate
- * @param channels
+ * @param audioSampleFmt
+ * @param audioSampleRate
+ * @param audioChannels
+ * @param audioSamplesPerFrameAndChannel in audio samples per frame and channel
*/
private void updateAttributes2(int pixFmt, int planes, int bitsPerPixel, int bytesPerPixelPerPlane,
int lSz0, int lSz1, int lSz2,
int tWd0, int tWd1, int tWd2, int tH,
- int audioFrameCount, int sampleFmt, int sampleRate, int channels) {
+ int audioFrameCount, int audioSampleFmt, int audioSampleRate,
+ int audioChannels, int audioSamplesPerFrameAndChannel) {
vPixelFmt = PixelFormat.valueOf(pixFmt);
vPlanes = planes;
vBitsPerPixel = bitsPerPixel;
@@ -340,8 +348,7 @@ public class FFMPEGMediaPlayer extends GLMediaPlayerImpl {
default: // FIXME: Add more formats !
throw new RuntimeException("Unsupported pixelformat: "+vPixelFmt);
}
- initialAudioFrameCount = audioFrameCount > 0 ? audioFrameCount : AV_DEFAULT_AFRAMES * 2;
- aSampleFmt = SampleFormat.valueOf(sampleFmt);
+ aSampleFmt = SampleFormat.valueOf(audioSampleFmt);
final int sampleSize;
final boolean signed, fixedP;
switch( aSampleFmt ) {
@@ -378,10 +385,11 @@ public class FFMPEGMediaPlayer extends GLMediaPlayerImpl {
default: // FIXME: Add more formats !
throw new RuntimeException("Unsupported sampleformat: "+aSampleFmt);
}
- avChosenAudioFormat = new AudioDataFormat(AudioDataType.PCM, sampleRate, sampleSize, channels, signed, fixedP, true /* littleEndian */);
+ avChosenAudioFormat = new AudioDataFormat(AudioDataType.PCM, audioSampleRate, sampleSize, audioChannels, signed, fixedP, true /* littleEndian */);
+ this.audioSamplesPerFrameAndChannel = audioSamplesPerFrameAndChannel;
if(DEBUG) {
- System.err.println("audio: fmt "+aSampleFmt+", "+avChosenAudioFormat+", aFrameCount "+audioFrameCount+" -> "+initialAudioFrameCount);
+ System.err.println("audio: fmt "+aSampleFmt+", "+avChosenAudioFormat+", aFrameSize/fc "+audioSamplesPerFrameAndChannel+", aFrameCount "+audioFrameCount);
System.err.println("video: fmt "+vPixelFmt+", planes "+vPlanes+", bpp "+vBitsPerPixel+"/"+vBytesPerPixelPerPlane);
for(int i=0; i<3; i++) {
System.err.println("video: "+i+": "+vTexWidth[i]+"/"+vLinesize[i]);