Question

How do I pipe an HTTP response like in NodeJS. Here is the snippet I am using in NodeJS:

request({
  url: audio_file_url,
}).pipe(ffmpeg_process.stdin);

How can I achieve the same result in Go?

I am trying to pipe a audio stream from HTTP into an FFmpeg process so that it converts it on the fly and returns the converted file back to the client.

Just so its clear to everyone here is my source code so far:

func encodeAudio(w http.ResponseWriter, req *http.Request) {
    path, err := exec.LookPath("youtube-dl")
    if err != nil {
        log.Fatal("LookPath: ", err)
    }
    path_ff, err_ff := exec.LookPath("ffmpeg")
    if err != nil {
        log.Fatal("LookPath: ", err_ff)
    }

    streamLink := exec.Command(path,"-f", "140", "-g", "https://www.youtube.com/watch?v=VIDEOID")

    var out bytes.Buffer
    streamLink.Stdout = &out
    cmdFF := exec.Command(path_ff, "-i", "pipe:0", "-acodec", "libmp3lame", "-f", "mp3", "-")
    resp, err := http.Get(out.String())
    if err != nil {
        log.Fatal(err)
    }
    // pr, pw := io.Pipe()
    defer resp.Body.Close()
    cmdFF.Stdin = resp.Body
    cmdFF.Stdout = w
    streamLink.Run()
    //get ffmpeg running in another goroutine to receive data
    errCh := make(chan error, 1)
    go func() {
        errCh <- cmdFF.Run()
    }()

    // close the pipeline to signal the end of the stream
    // pw.Close()
    // pr.Close()

    // check for an error from ffmpeg
    if err := <-errCh; err != nil {
        // ff error
    }
}

Error: 2014/07/29 23:04:02 Get : unsupported protocol scheme ""

Was it helpful?

Solution

Here's a possible answer using a standard http handler function. I don't have the programs to test this directly, but it does work with some simple shell commands standing in as a proxy.

func encodeAudio(w http.ResponseWriter, req *http.Request) {

    streamLink := exec.Command("youtube-dl", "-f", "140", "-g", "https://www.youtube.com/watch?v=VIDEOID")
    out, err := streamLink.Output()
    if err != nil {
        log.Fatal(err)
    }

    cmdFF := exec.Command("ffmpeg", "-i", "pipe:0", "-acodec", "libmp3lame", "-f", "mp3", "-")
    resp, err := http.Get(string(out))
    if err != nil {
        log.Fatal(err)
    }

    defer resp.Body.Close()
    cmdFF.Stdin = resp.Body

    cmdFF.Stdout = w
    if err := cmdFF.Run(); err != nil {
        log.Fatal(err)
    }
}

OTHER TIPS

http.Request.Body is an io.ReadCloser, so you could pipe it into exec.Cmd.Stdin:

func Handler(rw http.ResponseWriter, req *http.Request) {
    cmd := exec.Command("ffmpeg", other, args, ...)
    cmd.Stdin = req.Body
    go func() {
        defer req.Body.Close()

        if err := cmd.Run(); err != nil {
            // do something
        }
    }()
    //redirect the user and check for progress?
}

//edit I misunderstood the question, however the answer still stands, the http.Get version:

http.Response.Body is an io.ReadCloser just like http.Request.Body.

func EncodeUrl(url, fn string) error {
    resp, err := http.Get(url)
    if err != nil {
        return err
    }
    defer resp.Body.Close()
    cmd := exec.Command("ffmpeg", ......, fn)
    cmd.Stdin = resp.Body
    return cmd.Run()
}

//edit2:

this should work, according to the martini documentation, but again, I highly recommend learning to use ServeMux or at least use Gorilla.

m := martini.Classic()
m.Get("/stream/:ytid", func(params martini.Params, rw http.ResponseWriter,
                            req *http.Request) string {
    ytid := params["ytid"]
    stream_link := exec.Command("youtube-dl","-f", "140", "-g", "https://www.youtube.com/watch?v=" + ytid)
    var out bytes.Buffer
    stream_link.Stdout = &out
    errr := stream_link.Run()
    if err != nil {
        log.Fatal(err)
    }
    log.Println("Link", out.String())

    cmd_ff := exec.Command("ffmpeg", "-i", "pipe:0", "-acodec", "libmp3lame", "-f", "mp3", "-")
    resp, err := http.Get(url)
    if err != nil {
        log.Fatal(err)
    }
    cmd_ff.Stdin = resp.Body
    go func() {
        defer resp.Body.Close()
        if err := cmd_ff.Run(); err != nil {
            log.Fatal(err)
        }
    }()
    return "Youtube ID: " + ytid
})
m.Run()
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top