private InputStream getInputStream(
      String urlStr, Format outputFormat, ContentDescriptor outputContentDescriptor)
      throws Exception {
    final ProcessorModel processorModel =
        new ProcessorModel(
            new MediaLocator(urlStr),
            outputFormat == null ? null : new Format[] {outputFormat},
            outputContentDescriptor);

    final Processor processor = Manager.createRealizedProcessor(processorModel);

    final DataSource ds = processor.getDataOutput();

    final DataSink[] streamDataSinkHolder = new DataSink[] {null};
    // connect the data output of the processor to a StreamDataSink, which
    // will make the data available to PipedInputStream, which we return.
    final PipedInputStream in =
        new PipedInputStream() {
          // override close to clean up everything when the media has been
          // served.
          @Override
          public void close() throws IOException {
            super.close();
            logger.fine("Closed input stream");
            logger.fine("Stopping processor");
            processor.stop();
            logger.fine("Closing processor");
            processor.close();
            logger.fine("Deallocating processor");
            processor.deallocate();
            if (streamDataSinkHolder[0] != null) {
              logger.fine("Closing StreamDataSink");
              streamDataSinkHolder[0].close();
            }
          }
        };
    final PipedOutputStream out = new PipedOutputStream(in);
    final DataSink streamDataSink = new StreamDataSink(out);
    streamDataSinkHolder[0] = streamDataSink;

    streamDataSink.setSource(ds);
    streamDataSink.open();
    streamDataSink.start();

    logger.info("Starting processor");
    processor.start();

    // TODO: if there is an error, make sure we clean up.
    // for example, if the client breaks the connection.
    // we need a controller listener to listen for errors.

    return in;
  }
Exemple #2
0
  /**
   * Finds the <tt>ReceiveStreamDesc</tt> with a particular <tt>DataSink</tt>
   *
   * @param dataSink The <tt>DataSink</tt> to match.
   * @return the <tt>ReceiveStreamDesc</tt> with a particular <tt>DataSink</tt>, or <tt>null</tt>.
   */
  private ReceiveStreamDesc findReceiveStream(DataSink dataSink) {
    if (dataSink == null) return null;

    synchronized (receiveStreams) {
      for (ReceiveStreamDesc r : receiveStreams) if (dataSink.equals(r.dataSink)) return r;
    }

    return null;
  }
Exemple #3
0
 /** Stops the transmission if already started */
 public void stop() {
   synchronized (this) {
     if (processor != null) {
       processor.stop();
       processor.close();
       processor = null;
       rtptransmitter.close();
       rtptransmitter = null;
     }
   }
 }
  /** Create the DataSink. */
  DataSink createDataSink(Processor p, MediaLocator outML) {
    DataSource ds;

    if ((ds = p.getDataOutput()) == null) {
      System.err.println(
          "Something is really wrong: the processor does not have an output DataSource");
      return null;
    }

    DataSink dsink;

    try {
      System.err.println("- Create DataSink for: " + outML);
      dsink = Manager.createDataSink(ds, outML);
      dsink.open();
    } catch (Exception e) {
      System.err.println("Cannot create the DataSink: " + e);
      return null;
    }

    return dsink;
  }
Exemple #5
0
  // Creates an RTP transmit data sink. This is the easiest way to create
  // an RTP transmitter. The other way is to use the RTPSessionManager API.
  // Using an RTP session manager gives you more control if you wish to
  // fine tune your transmission and set other parameters.
  private String createTransmitter() {
    // Create a media locator for the RTP data sink.
    // For example:
    //    rtp://129.130.131.132:42050/video
    String rtpURL = "rtp://" + ipAddress + ":" + port + "/video";
    MediaLocator outputLocator = new MediaLocator(rtpURL);

    // Create a data sink, open it and start transmission. It will wait
    // for the processor to start sending data. So we need to start the
    // output data source of the processor. We also need to start the
    // processor itself, which is done after this method returns.
    try {
      rtptransmitter = Manager.createDataSink(dataOutput, outputLocator);
      rtptransmitter.open();
      rtptransmitter.start();
      dataOutput.start();
    } catch (MediaException me) {
      return "Couldn't create RTP data sink";
    } catch (IOException ioe) {
      return "Couldn't create RTP data sink";
    }

    return null;
  }
Exemple #6
0
  /**
   * Implements {@link ControllerListener#controllerUpdate(ControllerEvent)}. Handles events from
   * the <tt>Processor</tt>s that this instance uses to transcode media.
   *
   * @param ev the event to handle.
   */
  public void controllerUpdate(ControllerEvent ev) {
    if (ev == null || ev.getSourceController() == null) {
      return;
    }

    Processor processor = (Processor) ev.getSourceController();
    ReceiveStreamDesc desc = findReceiveStream(processor);

    if (desc == null) {
      logger.warn("Event from an orphaned processor, ignoring: " + ev);
      return;
    }

    if (ev instanceof ConfigureCompleteEvent) {
      if (logger.isInfoEnabled()) {
        logger.info(
            "Configured processor for ReceiveStream ssrc="
                + desc.ssrc
                + " ("
                + desc.format
                + ")"
                + " "
                + System.currentTimeMillis());
      }

      boolean audio = desc.format instanceof AudioFormat;

      if (audio) {
        ContentDescriptor cd = processor.setContentDescriptor(AUDIO_CONTENT_DESCRIPTOR);
        if (!AUDIO_CONTENT_DESCRIPTOR.equals(cd)) {
          logger.error(
              "Failed to set the Processor content "
                  + "descriptor to "
                  + AUDIO_CONTENT_DESCRIPTOR
                  + ". Actual result: "
                  + cd);
          removeReceiveStream(desc, false);
          return;
        }
      }

      for (TrackControl track : processor.getTrackControls()) {
        Format trackFormat = track.getFormat();

        if (audio) {
          final long ssrc = desc.ssrc;
          SilenceEffect silenceEffect;
          if (Constants.OPUS_RTP.equals(desc.format.getEncoding())) {
            silenceEffect = new SilenceEffect(48000);
          } else {
            // We haven't tested that the RTP timestamps survive
            // the journey through the chain when codecs other than
            // opus are in use, so for the moment we rely on FMJ's
            // timestamps for non-opus formats.
            silenceEffect = new SilenceEffect();
          }

          silenceEffect.setListener(
              new SilenceEffect.Listener() {
                boolean first = true;

                @Override
                public void onSilenceNotInserted(long timestamp) {
                  if (first) {
                    first = false;
                    // send event only
                    audioRecordingStarted(ssrc, timestamp);
                  } else {
                    // change file and send event
                    resetRecording(ssrc, timestamp);
                  }
                }
              });
          desc.silenceEffect = silenceEffect;
          AudioLevelEffect audioLevelEffect = new AudioLevelEffect();
          audioLevelEffect.setAudioLevelListener(
              new SimpleAudioLevelListener() {
                @Override
                public void audioLevelChanged(int level) {
                  activeSpeakerDetector.levelChanged(ssrc, level);
                }
              });

          try {
            // We add an effect, which will insert "silence" in
            // place of lost packets.
            track.setCodecChain(new Codec[] {silenceEffect, audioLevelEffect});
          } catch (UnsupportedPlugInException upie) {
            logger.warn("Failed to insert silence effect: " + upie);
            // But do go on, a recording without extra silence is
            // better than nothing ;)
          }
        } else {
          // transcode vp8/rtp to vp8 (i.e. depacketize vp8)
          if (trackFormat.matches(vp8RtpFormat)) track.setFormat(vp8Format);
          else {
            logger.error("Unsupported track format: " + trackFormat + " for ssrc=" + desc.ssrc);
            // we currently only support vp8
            removeReceiveStream(desc, false);
            return;
          }
        }
      }

      processor.realize();
    } else if (ev instanceof RealizeCompleteEvent) {
      desc.dataSource = processor.getDataOutput();

      long ssrc = desc.ssrc;
      boolean audio = desc.format instanceof AudioFormat;
      String suffix = audio ? AUDIO_FILENAME_SUFFIX : VIDEO_FILENAME_SUFFIX;

      // XXX '\' on windows?
      String filename = getNextFilename(path + "/" + ssrc, suffix);
      desc.filename = filename;

      DataSink dataSink;
      if (audio) {
        try {
          dataSink = Manager.createDataSink(desc.dataSource, new MediaLocator("file:" + filename));
        } catch (NoDataSinkException ndse) {
          logger.error("Could not create DataSink: " + ndse);
          removeReceiveStream(desc, false);
          return;
        }

      } else {
        dataSink = new WebmDataSink(filename, desc.dataSource);
      }

      if (logger.isInfoEnabled())
        logger.info(
            "Created DataSink ("
                + dataSink
                + ") for SSRC="
                + ssrc
                + ". Output filename: "
                + filename);
      try {
        dataSink.open();
      } catch (IOException e) {
        logger.error("Failed to open DataSink (" + dataSink + ") for" + " SSRC=" + ssrc + ": " + e);
        removeReceiveStream(desc, false);
        return;
      }

      if (!audio) {
        final WebmDataSink webmDataSink = (WebmDataSink) dataSink;
        webmDataSink.setSsrc(ssrc);
        webmDataSink.setEventHandler(eventHandler);
        webmDataSink.setKeyFrameControl(
            new KeyFrameControlAdapter() {
              @Override
              public boolean requestKeyFrame(boolean urgent) {
                return requestFIR(webmDataSink);
              }
            });
      }

      try {
        dataSink.start();
      } catch (IOException e) {
        logger.error(
            "Failed to start DataSink (" + dataSink + ") for" + " SSRC=" + ssrc + ". " + e);
        removeReceiveStream(desc, false);
        return;
      }

      if (logger.isInfoEnabled()) logger.info("Started DataSink for SSRC=" + ssrc);

      desc.dataSink = dataSink;

      processor.start();
    } else if (logger.isDebugEnabled()) {
      logger.debug(
          "Unhandled ControllerEvent from the Processor for ssrc=" + desc.ssrc + ": " + ev);
    }
  }
Exemple #7
0
  public static void main(String[] args) {

    // ---------------- CUT HERE START ----------------- //

    Format formats[] = new Format[2];
    formats[0] = new AudioFormat(AudioFormat.IMA4);
    formats[1] = new VideoFormat(VideoFormat.CINEPAK);
    FileTypeDescriptor outputType = new FileTypeDescriptor(FileTypeDescriptor.QUICKTIME);
    Processor p = null;

    try {
      p = Manager.createRealizedProcessor(new ProcessorModel(formats, outputType));
    } catch (IOException e) {
      System.exit(-1);
    } catch (NoProcessorException e) {
      System.exit(-1);
    } catch (CannotRealizeException e) {
      System.exit(-1);
    }
    // get the output of the processor
    DataSource source = p.getDataOutput();
    // create a File protocol MediaLocator with the location of the file to
    // which bits are to be written
    MediaLocator dest = new MediaLocator("file://foo.mov");
    // create a datasink to do the file writing & open the sink to make sure
    // we can write to it.
    DataSink filewriter = null;
    try {
      filewriter = Manager.createDataSink(source, dest);
      filewriter.open();
    } catch (NoDataSinkException e) {
      System.exit(-1);
    } catch (IOException e) {
      System.exit(-1);
    } catch (SecurityException e) {
      System.exit(-1);
    }
    // now start the filewriter and processor
    try {
      filewriter.start();
    } catch (IOException e) {
      System.exit(-1);
    }
    p.start();
    // stop and close the processor when done capturing...
    // close the datasink when EndOfStream event is received...

    // ----------------- CUT HERE END ---------------- //
    try {
      Thread.currentThread().sleep(4000);
    } catch (InterruptedException ie) {
    }
    p.stop();
    p.close();
    try {
      Thread.currentThread().sleep(1000);
    } catch (InterruptedException ie) {
    }
    filewriter.close();
    try {
      Thread.currentThread().sleep(4000);
    } catch (InterruptedException ie) {
    }

    System.exit(0);
  }
  /**
   * Given an array of input media locators and an output locator, this method will concatenate the
   * input media files to generate a single concatentated output.
   */
  public boolean doIt(MediaLocator inML[], MediaLocator outML) {
    // Guess the output content descriptor from the file extension.
    ContentDescriptor cd;

    if ((cd = fileExtToCD(outML.getRemainder())) == null) {
      System.err.println("Couldn't figure out from the file extension the type of output needed!");
      return false;
    }

    // Build the ProcInfo data structure for each processor.
    ProcInfo pInfo[] = new ProcInfo[inML.length];

    for (int i = 0; i < inML.length; i++) {
      pInfo[i] = new ProcInfo();
      pInfo[i].ml = inML[i];

      try {
        System.err.println("- Create processor for: " + inML[i]);
        pInfo[i].p = Manager.createProcessor(inML[i]);
      } catch (Exception e) {
        System.err.println("Yikes!  Cannot create a processor from the given url: " + e);
        return false;
      }
    }

    // Try to match the tracks from different processors.
    if (!matchTracks(pInfo, cd)) {
      System.err.println("Failed to match the tracks.");
      return false;
    }

    // Program each processors to perform the necessary transcoding
    // to concatenate the tracks.
    if (!buildTracks(pInfo)) {
      System.err.println("Failed to build processors for the inputs.");
      return false;
    }

    // Generate a super glue data source from the processors.
    SuperGlueDataSource ds = new SuperGlueDataSource(pInfo);

    // Create the processor to generate the final output.
    Processor p;
    try {
      p = Manager.createProcessor(ds);
    } catch (Exception e) {
      System.err.println("Failed to create a processor to concatenate the inputs.");
      return false;
    }

    p.addControllerListener(this);

    // Put the Processor into configured state.
    if (!waitForState(p, Processor.Configured)) {
      System.err.println("Failed to configure the processor.");
      return false;
    }

    // Set the output content descriptor on the final processor.
    System.err.println("- Set output content descriptor to: " + cd);
    if ((p.setContentDescriptor(cd)) == null) {
      System.err.println("Failed to set the output content descriptor on the processor.");
      return false;
    }

    // We are done with programming the processor. Let's just
    // realize it.
    if (!waitForState(p, Controller.Realized)) {
      System.err.println("Failed to realize the processor.");
      return false;
    }

    // Now, we'll need to create a DataSink.
    DataSink dsink;
    if ((dsink = createDataSink(p, outML)) == null) {
      System.err.println("Failed to create a DataSink for the given output MediaLocator: " + outML);
      return false;
    }

    dsink.addDataSinkListener(this);
    fileDone = false;

    System.err.println("- Start concatenation...");

    // OK, we can now start the actual concatenation.
    try {
      p.start();
      dsink.start();
    } catch (IOException e) {
      System.err.println("IO error during concatenation");
      return false;
    }

    // Wait for EndOfStream event.
    waitForFileDone();

    // Cleanup.
    try {
      dsink.close();
    } catch (Exception e) {
    }
    p.removeControllerListener(this);

    System.err.println("  ...done concatenation.");

    return true;
  }