aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorXerxes Rånby <[email protected]>2013-06-19 18:52:38 +0200
committerXerxes Rånby <[email protected]>2013-06-19 18:52:38 +0200
commit3bf564210e7dca2f5d6b47898c554f5762ac5282 (patch)
tree0a9551a08a094ac2ced330a10eb8c634e616bbde
parent2f85c5c0fa7518287d2584dc0a586064559c6827 (diff)
FFMPEGMediaPlayer: Add AudioSink interface.
Use ALAudioSink when available and fallback to JavaSoundAudioSink when JOAL are not found on classpath. Java Sound playback moved from FFMPEGMediaPlayer into JavaSoundAudioSink. Signed-off-by: Xerxes Rånby <[email protected]>
-rw-r--r--src/jogl/classes/jogamp/opengl/openal/av/ALAudioSink.java110
-rw-r--r--src/jogl/classes/jogamp/opengl/util/av/AudioSink.java11
-rw-r--r--src/jogl/classes/jogamp/opengl/util/av/JavaSoundAudioSink.java78
-rw-r--r--src/jogl/classes/jogamp/opengl/util/av/NullAudioSink.java18
-rw-r--r--src/jogl/classes/jogamp/opengl/util/av/impl/FFMPEGMediaPlayer.java73
5 files changed, 235 insertions, 55 deletions
diff --git a/src/jogl/classes/jogamp/opengl/openal/av/ALAudioSink.java b/src/jogl/classes/jogamp/opengl/openal/av/ALAudioSink.java
new file mode 100644
index 000000000..e7a957156
--- /dev/null
+++ b/src/jogl/classes/jogamp/opengl/openal/av/ALAudioSink.java
@@ -0,0 +1,110 @@
+package jogamp.opengl.openal.av;
+
+import java.util.Arrays;
+
+import javax.sound.sampled.AudioFormat;
+import javax.sound.sampled.AudioSystem;
+import javax.sound.sampled.DataLine;
+import javax.sound.sampled.SourceDataLine;
+
+import jogamp.opengl.util.av.AudioSink;
+
+import com.jogamp.openal.*;
+import com.jogamp.openal.util.*;
+
+public class ALAudioSink implements AudioSink {
+
+ static ALC alc;
+ static AL al;
+ static ALCdevice device;
+ static ALCcontext context;
+
+ // AudioFormat parameters
+ public static final int SAMPLE_RATE = 44100;
+ private static final int SAMPLE_SIZE = 16;
+ private static final int CHANNELS = 2;
+ private static final boolean SIGNED = true;
+ private static final boolean BIG_ENDIAN = false;
+
+ // Chunk of audio processed at one time
+ public static final int BUFFER_SIZE = 1000;
+ public static final int SAMPLES_PER_BUFFER = BUFFER_SIZE / 2;
+
+ // Sample time values
+ public static final double SAMPLE_TIME_IN_SECS = 1.0 / SAMPLE_RATE;
+ public static final double BUFFER_TIME_IN_SECS = SAMPLE_TIME_IN_SECS * SAMPLES_PER_BUFFER;
+
+ private static AudioFormat format;
+ private static DataLine.Info info;
+ private static SourceDataLine auline;
+ private static int bufferCount;
+ private static byte [] sampleData = new byte[BUFFER_SIZE];
+
+ private static boolean available = false;
+
+ static {
+
+ boolean joalFound = false;
+ try {
+ Class.forName("com.jogamp.openal.ALFactory");
+ joalFound = true;
+ } catch(ClassNotFoundException e){
+ // Joal not found on classpath
+ }
+
+ if(joalFound) {
+
+ alc = ALFactory.getALC();
+ al = ALFactory.getAL();
+ String deviceSpecifier;
+
+ // Get handle to default device.
+ device = alc.alcOpenDevice(null);
+ if (device == null) {
+ throw new ALException("Error opening default OpenAL device");
+ }
+
+ // Get the device specifier.
+ deviceSpecifier = alc.alcGetString(device, ALC.ALC_DEVICE_SPECIFIER);
+ if (deviceSpecifier == null) {
+ throw new ALException("Error getting specifier for default OpenAL device");
+ }
+
+ // Create audio context.
+ context = alc.alcCreateContext(device, null);
+ if (context == null) {
+ throw new ALException("Error creating OpenAL context");
+ }
+
+ // Set active context.
+ alc.alcMakeContextCurrent(context);
+
+ // Check for an error.
+ if (alc.alcGetError(device) != ALC.ALC_NO_ERROR) {
+ throw new ALException("Error making OpenAL context current");
+ }
+
+ System.out.println("OpenAL audio sink using device: " + deviceSpecifier);
+ available = true;
+ }
+ }
+
+ @Override
+ public boolean isDataAvailable(int data_size) {
+ return false;
+ }
+
+ @Override
+ public void writeData(byte[] sampleData, int data_size) {
+
+ }
+
+ @Override
+ public int getDataAvailable() {
+ return 0;
+ }
+
+ public static boolean isAvailable() {
+ return available;
+ }
+}
diff --git a/src/jogl/classes/jogamp/opengl/util/av/AudioSink.java b/src/jogl/classes/jogamp/opengl/util/av/AudioSink.java
new file mode 100644
index 000000000..fedead713
--- /dev/null
+++ b/src/jogl/classes/jogamp/opengl/util/av/AudioSink.java
@@ -0,0 +1,11 @@
+package jogamp.opengl.util.av;
+
+public interface AudioSink {
+
+ int getDataAvailable();
+
+ boolean isDataAvailable(int data_size);
+
+ void writeData(byte[] sampleData, int data_size);
+
+}
diff --git a/src/jogl/classes/jogamp/opengl/util/av/JavaSoundAudioSink.java b/src/jogl/classes/jogamp/opengl/util/av/JavaSoundAudioSink.java
new file mode 100644
index 000000000..0e2806322
--- /dev/null
+++ b/src/jogl/classes/jogamp/opengl/util/av/JavaSoundAudioSink.java
@@ -0,0 +1,78 @@
+package jogamp.opengl.util.av;
+
+import java.util.Arrays;
+
+import javax.sound.sampled.AudioFormat;
+import javax.sound.sampled.AudioSystem;
+import javax.sound.sampled.DataLine;
+import javax.sound.sampled.SourceDataLine;
+
+public class JavaSoundAudioSink implements AudioSink {
+
+ // AudioFormat parameters
+ public static final int SAMPLE_RATE = 44100;
+ private static final int SAMPLE_SIZE = 16;
+ private static final int CHANNELS = 2;
+ private static final boolean SIGNED = true;
+ private static final boolean BIG_ENDIAN = false;
+
+ // Chunk of audio processed at one time
+ public static final int BUFFER_SIZE = 1000;
+ public static final int SAMPLES_PER_BUFFER = BUFFER_SIZE / 2;
+
+ // Sample time values
+ public static final double SAMPLE_TIME_IN_SECS = 1.0 / SAMPLE_RATE;
+ public static final double BUFFER_TIME_IN_SECS = SAMPLE_TIME_IN_SECS * SAMPLES_PER_BUFFER;
+
+ private static AudioFormat format;
+ private static DataLine.Info info;
+ private static SourceDataLine auline;
+ private static int bufferCount;
+ private static byte [] sampleData = new byte[BUFFER_SIZE];
+
+ private static boolean available;
+
+ static {
+ // Create the audio format we wish to use
+ format = new AudioFormat(SAMPLE_RATE, SAMPLE_SIZE, CHANNELS, SIGNED, BIG_ENDIAN);
+
+ // Create dataline info object describing line format
+ info = new DataLine.Info(SourceDataLine.class, format);
+
+ // Clear buffer initially
+ Arrays.fill(sampleData, (byte) 0);
+ try{
+ // Get line to write data to
+ auline = (SourceDataLine) AudioSystem.getLine(info);
+ auline.open(format);
+ auline.start();
+ System.out.println("JavaSound audio sink");
+ available=true;
+ } catch (Exception e) {
+ available=false;
+ }
+ }
+
+ public void writeData(byte[] sampleData, int data_size) {
+ int written = 0;
+ int len;
+ while (data_size > 0) {
+ len = auline.write(sampleData, written, data_size);
+ data_size -= len;
+ written += len;
+ }
+ }
+
+ public int getDataAvailable() {
+ return auline.available();
+ }
+
+ public boolean isDataAvailable(int data_size) {
+ return auline.available()>=data_size;
+ }
+
+ public static boolean isAvailable() {
+ return available;
+ }
+
+}
diff --git a/src/jogl/classes/jogamp/opengl/util/av/NullAudioSink.java b/src/jogl/classes/jogamp/opengl/util/av/NullAudioSink.java
new file mode 100644
index 000000000..81259f4c5
--- /dev/null
+++ b/src/jogl/classes/jogamp/opengl/util/av/NullAudioSink.java
@@ -0,0 +1,18 @@
+package jogamp.opengl.util.av;
+
+public class NullAudioSink implements AudioSink {
+
+ @Override
+ public int getDataAvailable() {
+ return 0;
+ }
+
+ @Override
+ public boolean isDataAvailable(int data_size) {
+ return false;
+ }
+
+ @Override
+ public void writeData(byte[] sampleData, int data_size) {
+ }
+}
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 68914639d..972cf0642 100644
--- a/src/jogl/classes/jogamp/opengl/util/av/impl/FFMPEGMediaPlayer.java
+++ b/src/jogl/classes/jogamp/opengl/util/av/impl/FFMPEGMediaPlayer.java
@@ -38,7 +38,6 @@ import javax.media.opengl.GLException;
import java.util.Arrays;
import java.util.Queue;
-import javax.sound.sampled.*;
import com.jogamp.common.util.VersionNumber;
import com.jogamp.gluegen.runtime.ProcAddressTable;
@@ -50,6 +49,10 @@ import jogamp.opengl.GLContextImpl;
import jogamp.opengl.es1.GLES1ProcAddressTable;
import jogamp.opengl.es2.GLES2ProcAddressTable;
import jogamp.opengl.gl4.GL4bcProcAddressTable;
+import jogamp.opengl.util.av.AudioSink;
+import jogamp.opengl.util.av.JavaSoundAudioSink;
+import jogamp.opengl.util.av.NullAudioSink;
+import jogamp.opengl.openal.av.ALAudioSink;
import jogamp.opengl.util.av.EGLMediaPlayerImpl;
/***
@@ -109,28 +112,8 @@ public class FFMPEGMediaPlayer extends EGLMediaPlayerImpl {
// Count of zeroed buffers to return before switching to real sample provider
private static final int TEMP_BUFFER_COUNT = 20;
- // AudioFormat parameters
- public static final int SAMPLE_RATE = 44100;
- private static final int SAMPLE_SIZE = 16;
- private static final int CHANNELS = 2;
- private static final boolean SIGNED = true;
- private static final boolean BIG_ENDIAN = false;
-
- // Chunk of audio processed at one time
- public static final int BUFFER_SIZE = 1000;
- public static final int SAMPLES_PER_BUFFER = BUFFER_SIZE / 2;
-
- // Sample time values
- public static final double SAMPLE_TIME_IN_SECS = 1.0 / SAMPLE_RATE;
- public static final double BUFFER_TIME_IN_SECS = SAMPLE_TIME_IN_SECS * SAMPLES_PER_BUFFER;
-
// Instance data
- private static AudioFormat format;
- private static DataLine.Info info;
- private static SourceDataLine auline;
- private static int bufferCount;
- private static byte [] sampleData = new byte[BUFFER_SIZE];
-
+ private static AudioSink audioSink;
private static int maxAvailableAudio;
public static final VersionNumber avUtilVersion;
@@ -146,28 +129,15 @@ public class FFMPEGMediaPlayer extends EGLMediaPlayerImpl {
System.err.println("LIB_AV Util : "+avUtilVersion);
System.err.println("LIB_AV Format: "+avFormatVersion);
System.err.println("LIB_AV Codec : "+avCodecVersion);
- if(initIDs0()) {
- // init audio
- // Create the audio format we wish to use
- format = new AudioFormat(SAMPLE_RATE, SAMPLE_SIZE, CHANNELS, SIGNED, BIG_ENDIAN);
-
- // Create dataline info object describing line format
- info = new DataLine.Info(SourceDataLine.class, format);
-
- // Clear buffer initially
- Arrays.fill(sampleData, (byte) 0);
- try{
- // Get line to write data to
- auline = (SourceDataLine) AudioSystem.getLine(info);
- auline.open(format);
- auline.start();
- maxAvailableAudio = auline.available();
- available = true;
- } catch (LineUnavailableException e){
- maxAvailableAudio = 0;
- available = false;
- }
+ initIDs0();
+ available = true;
+ audioSink = new NullAudioSink();
+ if(ALAudioSink.isAvailable()) {
+ audioSink = new ALAudioSink();
+ } else if(JavaSoundAudioSink.isAvailable()) {
+ audioSink = new JavaSoundAudioSink();
}
+ maxAvailableAudio = audioSink.getDataAvailable();
} else {
avUtilVersion = null;
@@ -313,7 +283,7 @@ public class FFMPEGMediaPlayer extends EGLMediaPlayerImpl {
}
private void pumpAudio() {
- if(auline.available()==maxAvailableAudio){
+ if(audioSink.getDataAvailable()==maxAvailableAudio){
System.out.println("warning: audio buffer underrun");
}
while(audioFrameBuffer.peek()!=null){
@@ -327,19 +297,12 @@ public class FFMPEGMediaPlayer extends EGLMediaPlayerImpl {
System.err.println("s: pts-a "+a.audio_pts+", pts-d "+pts_d+", now_d "+now_d+", dt "+dt);
lastAudioTime = now;
- if( (dt<audio_dt_d ) && auline.available()>a.data_size ) {
+ if( (dt<audio_dt_d ) && audioSink.isDataAvailable(a.data_size)) {
audioFrameBuffer.poll(); /* remove first item from the queue */
- int written = 0;
- int len;
- int data_size = a.data_size;
- while (data_size > 0) {
- len = auline.write(a.sampleData, written, data_size);
- data_size -= len;
- written += len;
- }
+ audioSink.writeData(a.sampleData, a.data_size);
lastAudioPTS=a.audio_pts;
} else {
- break;
+ break;
}
}
}
@@ -543,7 +506,7 @@ public class FFMPEGMediaPlayer extends EGLMediaPlayerImpl {
lastVideoTime = now;
System.err.println("s: pts-v "+pts+", pts-d "+pts_d+", now_d "+now_d+", dt "+dt);
- if(dt>video_dt_d && dt<1000 && auline.available()<maxAvailableAudio-10000) {
+ if(dt>video_dt_d && dt<1000 && audioSink.getDataAvailable()<maxAvailableAudio-10000) {
try {
Thread.sleep(dt-video_dt_d);
} catch (InterruptedException e) { }