Source for gnu.javax.sound.sampled.gstreamer.lines.GstPipeline

   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: }