views:

77

answers:

1

EDIT Updated code with solution

I need to transcode amr to mp3, so i wrote a gstreamer pipeline in gstreamer-java. It looks like this:

src ! amrparse ! amrnbdec ! lamemp3enc ! sink 

(actually built with the java API, of course), i start the transcode with

Bus.connect(EOS, fn(){Gst.quit();});

setState(PLAYING);
Gst.main();

It works fine, except the time it takes is equal to the audio length, which is not acceptable. The equivalent

gst-launch 

transcodes at machine speed.

So how do I need to setup the pipeline, to get machine speed transcoding?

Here is the full source, for people fluent with clojure

(ns audio
  (:import [org.gstreamer Gst Pipeline Bin Element ElementFactory State
                          StateChangeReturn Bus$EOS Bus$ERROR Bus$STATE_CHANGED]
    [org.gstreamer.io InputStreamSrc OutputStreamSink]
    [java.io InputStream OutputStream])
 (:use clojure.contrib.logging))

(Gst/init)
(defn transcode [^InputStream in ^OutputStream out]
  (let [id (gensym (quote transcode))
        src (InputStreamSrc. in (str "in stream " id))
        dec0 (ElementFactory/make "amrparse" (str "amr parser " id))
        dec1 (ElementFactory/make "amrnbdec" (str "amr decoder " id))
        enc (doto (ElementFactory/make "lamemp3enc" (str "mp3 encoder " id))
               (.set "mono" true)
               (.set "target" 0)
               (.set "quality" 2))
        out (doto (OutputStreamSink. out (str "out stream " id))
               (.setSync false))
        pipe (doto (Pipeline. (str "transcoder pipe " id))
                (.add src)
                (.add dec0)
                (.add dec1)
                (.add enc)
                (.add out))
        clean (fn []
                (.setState src nil)
                (.setState dec0 nil)
                (.setState dec1 nil)
                (.setState enc nil)
                (.setState out nil)
                (.setState pipe nil))]
    (prn "starting transcode " id)
    (.link src dec0)
    (.link dec0 dec1)
    (.link dec1 enc)
    (.link enc out)

    (doto (.getBus pipe)
      (.connect 
        (reify Bus$EOS
          (endOfStream [this src]
            (prn "Bus finished " src)
            (clean)
            (Gst/quit))))
      (.connect
        (reify Bus$ERROR
          (errorMessage [this src code msg]
          (prn "Bus Error " src code msg)
          (clean)
          (Gst/quit))))
      (.connect 
        (reify Bus$STATE_CHANGED
          (stateChanged [this src old now pending]
          (prn "Bus State change " src old now pending)))))
    (.setState pipe State/PLAYING)
    (Gst/main)))
+3  A: 

Try .setSync(false) for your output. In normal gstreamer a synchronous stream tries to keep track of the time, while an asynchronous stream goes as fast as it can. Maybe your output stream is trying to work in real time.

joeforker
Perfect! This was just what I was looking for. Now I can revert the Shell Script hack :)Thanks a ton!Can you tell me where one would look up this information?And why only the Output Stream blocks, but not e.g. the Input Stream or the Pipeline itself?
Bendlas
The output usually sets the rate (the sound card) for the whole pipeline. For whatever reason the Java bindings set this to true, perhaps because it's a generic io stream not necessarily a file, but `gst-launch ... ! filesink` would go fast with sync automatically false. Don't know where to look this up besides guessing at what gst-inspect properties are for.
joeforker