【问题标题】:Pipe a HTTP response管道 HTTP 响应
【发布时间】:2014-09-21 07:24:28
【问题描述】:

如何像在 NodeJS 中一样通过管道传输 HTTP 响应。这是我在 NodeJS 中使用的 sn-p:

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

如何在 Go 中实现相同的结果?

我正在尝试将来自 HTTP 的音频流通过管道传输到 FFmpeg 进程中,以便它即时对其进行转换并将转换后的文件返回给客户端。

到目前为止,这里的每个人都清楚我的源代码:

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

错误:2014/07/29 23:04:02 获取:不支持的协议方案“”

【问题讨论】:

  • 您确定您的 http 客户端会同时发送和接收吗?大多数人不会这样做,一旦缓冲区全部填满,您就会阻塞。
  • @JimB 音频来自另一个来源。我只需要在服务器上处理它并将 ffmpeg 响应发送回客户端。你说的还适用吗?
  • 啊,我明白了(对节点不熟悉,所以我并没有仔细研究那个请求)。我以为客户正在发送源。这很简单,必须尝试什么?
  • 好的,我已经编辑了我的答案以显示我到目前为止的进度@JimB
  • 好的,谢谢。我显然又误解了:/

标签: ffmpeg go martini


【解决方案1】:

这是使用标准 http 处理程序函数的可能答案。我没有直接测试它的程序,但它确实可以使用一些作为代理的简单 shell 命令。

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

【讨论】:

  • 那不行,youtube-dl 只是打印一个 url,这就是为什么他需要在steamLink 的输出上使用http.Get
  • 感谢@OneOfOne ...我还没有破解这个问题!我假设“管道输出”就是这个意思。
  • 也花了我一段时间,我只是熟悉youtube-dl
  • 哈哈我很抱歉让大家感到困惑。我认为我们真的很接近。唯一缺少的是http.Get()。对此的响应需要通过管道传送到 cmdFF.Stdin。
  • 我开始做其余的代码,最后我得到了一个错误。我已经更新了我原来的帖子。 @JimB
【解决方案2】:

http.Request.Body 是一个io.ReadCloser,因此您可以将其通过管道传输到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?
}

//编辑我误解了这个问题,但是答案仍然存在,http.Get 版本:

http.Response.Bodyio.ReadCloser 就像 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:

应该工作,根据马提尼文档,但我再次强烈建议学习使用ServeMux或至少使用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()

【讨论】:

  • Handler 不是用来服务 HTTP 的,是用来做服务器的吗?我不需要使用 net/http 来请求一个 URL,然后将其正文通过管道传输到 cmd.Stdin 中吗?
  • 我误读了这个问题,我以为您想在服务器上的 http 处理程序中执行此操作,但是同样的事情,使用 http.Get 返回一个 http.Response 也有 .Body,我将更新示例。
  • 如果您有时间,能否简要添加您推荐的执行此操作的方式。
猜你喜欢
  • 1970-01-01
  • 2014-04-14
  • 1970-01-01
  • 1970-01-01
  • 2021-06-24
  • 2019-07-25
  • 2017-01-15
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多