Frames | No Frames |
1: /* GstPipeline.java -- Represents a Gstreamer Pipeline. 2: Copyright (C) 2007 Free Software Foundation, Inc. 3: 4: This file is part of GNU Classpath. 5: 6: GNU Classpath is free software; you can redistribute it and/or modify 7: it under the terms of the GNU General Public License as published by 8: the Free Software Foundation; either version 2, or (at your option) 9: any later version. 10: 11: GNU Classpath is distributed in the hope that it will be useful, but 12: WITHOUT ANY WARRANTY; without even the implied warranty of 13: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14: General Public License for more details. 15: 16: You should have received a copy of the GNU General Public License 17: along with GNU Classpath; see the file COPYING. If not, write to the 18: Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 19: 02110-1301 USA. 20: 21: Linking this library statically or dynamically with other modules is 22: making a combined work based on this library. Thus, the terms and 23: conditions of the GNU General Public License cover the whole 24: combination. 25: 26: As a special exception, the copyright holders of this library give you 27: permission to link this library with independent modules to produce an 28: executable, regardless of the license terms of these independent 29: modules, and to copy and distribute the resulting executable under 30: terms of your choice, provided that you also meet, for each linked 31: independent module, the terms and conditions of the license of that 32: module. An independent module is a module which is not derived from 33: or based on this library. If you modify this library, you may extend 34: this exception to your version of the library, but you are not 35: obligated to do so. If you do not wish to do so, delete this 36: exception statement from your version. */ 37: 38: package gnu.javax.sound.sampled.gstreamer.lines; 39: 40: import java.io.FileOutputStream; 41: import java.io.IOException; 42: import java.util.prefs.Preferences; 43: 44: import javax.sound.sampled.LineUnavailableException; 45: 46: import gnu.classpath.Pointer; 47: 48: /** 49: * This class represent a GStreamer pipeline and is resposible to handle the 50: * flow of data to and from the GStreamer native backend. 51: * 52: * @author Mario Torre <neugens@limasoftware.net> 53: */ 54: public class GstPipeline 55: { 56: /* 57: * Implementation note: 58: * This class is at first a bit confusing as it serves as a gateway 59: * to a real filesystem named pipe. 60: * The pipelines is shared by the gstreamer backend and by the java code. 61: * If the operation we are performing is to play a given stream of bytes, 62: * we need to open the java side of the pipeline for writing, which is done 63: * in the prepareWrite method. At the same time, the native side of the code 64: * need to open the pipeline in read mode, to get access to the data, 65: * and hence, act as a source element. This is why you will see terms 66: * like "read" or "source" in methods that are used to write in the pipeline, 67: * in other words, each the native operation is the opposite of the java 68: * side operation. 69: * Opening the pipe to record audio data from the sound card works the same 70: * except that all the operation are inverted. 71: */ 72: 73: // These enums are used in the native code also, changes here must reflect 74: // changes in the native code. 75: public static enum State 76: { 77: PLAY, PAUSE, STOP, CLOSE 78: } 79: 80: private static final int READ = 0; 81: private static final int WRITE = 1; 82: private static final int QUEUED = 1; 83: 84: private static final String CAPACITY_KEY = "Capacity"; 85: 86: private static final Object [] lock = new Object[0]; 87: 88: /* 89: * Preference subsystem. We use this to store some system specific settings. 90: */ 91: protected Preferences prefs = 92: Preferences.userNodeForPackage(GstPipeline.class).node("GStreamer"); 93: 94: // used by the native code, stores the size of the named pipeline 95: // created by the operating system. 96: private long capacity = -1; 97: 98: /** Represents the playing state of this Line. */ 99: private State state = State.STOP; 100: 101: /** The name of the named pipe. */ 102: // Will be setup and filled in the native code. See the native library 103: // for details. 104: private String name = null; 105: 106: /** This is the named pipe that will be read by the gstreamer backend. */ 107: private FileOutputStream output = null; 108: 109: /** 110: * Defines if we are getting data from a sink pipe 111: * or writing to a source pipe. 112: */ 113: private boolean source = true; 114: 115: /** Indicate that we are ready to process audio data to/from the pipe. */ 116: private boolean ready = false; 117: 118: /** 119: * This is the native GStreamer Pipeline. 120: */ 121: // This field is used by the native code, so any change to it must be 122: // followed by similar changes in the native peer. 123: private Pointer pipeline = null; 124: 125: /** 126: * Creates a new GstPipeline with a capacity of 127: * {@link GstDataLine#DEFAULT_BUFFER_SIZE}. 128: * 129: * @see GstDataLine#DEFAULT_BUFFER_SIZE 130: */ 131: public GstPipeline() 132: { 133: this(GstDataLine.DEFAULT_BUFFER_SIZE); 134: } 135: 136: /** 137: * Creates a new GstPipeline with a capacity of bufferSize. 138: * @see GstDataLine#DEFAULT_BUFFER_SIZE 139: */ 140: public GstPipeline(int bufferSize) 141: { 142: // see if we need to detect the size of the named pipe or we can use 143: // an already computet default for this system. 144: // Note that this is very different from the bufferSize parameter, 145: // see below. 146: capacity = prefs.getLong(CAPACITY_KEY, -1); 147: if (capacity == -1) 148: { 149: synchronized (lock) 150: { 151: capacity = detect_pipe_size(); 152: } 153: 154: prefs.putLong(CAPACITY_KEY, capacity); 155: } 156: 157: // FIXME: bufferSize actually not used nor needed by the backend. 158: // Applications that expects a buffer of different size will be a 159: // bit disappointed by that.. 160: init_instance(); 161: 162: // need to remove the named pipe in case of abnormal termination 163: Runtime.getRuntime().addShutdownHook(new CleanPipeline()); 164: } 165: 166: /** 167: * Creates a source pipeline. A source pipeline is a pipe you send data for 168: * processing using the write method. 169: */ 170: public void createForWrite() throws LineUnavailableException 171: { 172: // create the named pipe 173: if (!create_named_pipe(this.pipeline)) 174: throw new LineUnavailableException("Unable to create filesystem pipe"); 175: 176: open_native_pipe(this.pipeline, READ); 177: prepareWrite(); 178: 179: this.source = true; 180: } 181: 182: /** 183: * @return the state 184: */ 185: public State getState() 186: { 187: return this.state; 188: } 189: 190: /** 191: * Closes this pipeline. 192: * Short hand for #setState(State.STOP). 193: */ 194: public void close() 195: { 196: setState(State.STOP); 197: } 198: 199: /** 200: * @param state the state to set 201: */ 202: public void setState(final State state) 203: { 204: int _state = -1; 205: switch (state) 206: { 207: case PLAY: 208: _state = 0; 209: break; 210: 211: case PAUSE: 212: _state = 1; 213: break; 214: 215: case STOP: case CLOSE: 216: _state = 2; 217: closePipe(); 218: break; 219: } 220: 221: if (set_state(pipeline, _state)) 222: GstPipeline.this.state = state; 223: } 224: 225: /** 226: * Return a reference to the GstPipeline native class as a Pointer object. 227: * This method is intended as an helper accessor and the returned pointer 228: * needs to be casted and used in the native code only. 229: * 230: * @return Pointer to the native GstPipeline class. 231: */ 232: public Pointer getNativeClass() 233: { 234: return this.pipeline; 235: } 236: 237: /** 238: * Write length bytes from the given buffer into this pipeline, 239: * starting at offset. 240: * This method block if the pipeline can't accept more data. 241: * 242: * @param buffer 243: * @param offset 244: * @param length 245: * @return 246: */ 247: public int write(byte[] buffer, int offset, int length) 248: { 249: if (this.state == State.STOP) 250: return -1; 251: else if (this.state == State.PAUSE) 252: return 0; 253: else if (!ready) 254: return -1; 255: 256: try 257: { 258: if (output != null) 259: { 260: output.write(buffer, offset, length); 261: return length; 262: } 263: return 0; 264: } 265: catch (Exception e) 266: { 267: /* nothing to do */ 268: } 269: 270: return -1; 271: } 272: 273: public int read(byte[] buffer, int offset, int length) 274: { 275: return 0; 276: } 277: 278: public int available() 279: { 280: if (this.source) 281: return available(this.pipeline, READ); 282: else 283: return available(this.pipeline, WRITE); 284: } 285: 286: /** 287: * Wait for remaining data to be enqueued in the pipeline. 288: */ 289: public void drain() 290: { 291: if (this.state == State.STOP) 292: return; 293: 294: try 295: { 296: // wait untill there is anymore data in the pipe 297: while (available(this.pipeline, QUEUED) > 0) 298: Thread.sleep(3000); 299: 300: // plus a bit to allow data to be processed 301: Thread.sleep(1000); 302: } 303: catch (InterruptedException e) 304: { 305: /* nothing to do*/ 306: } 307: } 308: 309: /** 310: * Flush all the data currently waiting to be processed. 311: */ 312: public void flush() 313: { 314: try 315: { 316: if (source) 317: this.output.flush(); 318: } 319: catch (IOException e) 320: { 321: /* nothing */ 322: } 323: } 324: 325: private void closePipe() 326: { 327: try 328: { 329: GstPipeline.this.flush(); 330: if (source) 331: GstPipeline.this.output.close(); 332: } 333: catch (IOException e) 334: { 335: /* nothing to do */ 336: } 337: } 338: 339: private void prepareWrite() 340: { 341: try 342: { 343: // if this is not completed for some reason, we will catch 344: // in the write method. As this call can block, we assume we will 345: // succeed and that the dataline can get data. 346: GstPipeline.this.ready = true; 347: GstPipeline.this.output = new FileOutputStream(name); 348: } 349: catch (Exception e) 350: { 351: GstPipeline.this.ready = false; 352: } 353: } 354: 355: /* ***** native ***** */ 356: 357: /** 358: * Initialize the native peer and enables the object cache. 359: * It is meant to be used by the static initializer. 360: */ 361: native private static final void init_id_cache(); 362: 363: /** 364: * Set the playing state of this pipeline. 365: */ 366: native private static final boolean set_state(Pointer pipeline, int state); 367: 368: /** 369: * Get the number of bytes currently available for reading or writing 370: * from the pipeline. 371: */ 372: native private static final int available(Pointer pipeline, int mode); 373: 374: /** 375: * Open the native pipeline with the given mode. 376: */ 377: native private static final void open_native_pipe(Pointer jpipeline, 378: int mode); 379: 380: /** 381: * Close the native pipeline. 382: */ 383: native private static final void close_native_pipe(Pointer jpipeline); 384: 385: /** 386: * Initialize the native peer and enables the object cache. 387: * It is meant to be used by the class constructor. 388: */ 389: native private final void init_instance(); 390: 391: /** 392: * Crates the named pipe used to pass data between the application code 393: * and gstreamer. 394: */ 395: native private final boolean create_named_pipe(Pointer jpipeline); 396: 397: /** 398: * Detect and return the size of the filesystem named pipe. 399: */ 400: native private final long detect_pipe_size(); 401: 402: private class CleanPipeline extends Thread 403: { 404: public void run() 405: { 406: GstPipeline.close_native_pipe(GstPipeline.this.pipeline); 407: } 408: } 409: 410: static 411: { 412: System.loadLibrary("gstreamerpeer"); //$NON-NLS-1$ 413: init_id_cache(); 414: } 415: }