猿问

runtime/cgo: pthread_create failed: 资源暂时不可用

我目前有这个功能,通过 ffmpeg 管道传输多个 youtubedl 命令,然后将 ffmpeg 的输出管道传输到 HTTP 客户端。


func pipeThruFfmpegToMp4(vi *VideoInfo, rw web.ResponseWriter) error {

    var ffmpeg *exec.Cmd

    ffmpeg = exec.Command(

        "ffmpeg",

        "-i", "-",

        "-i", "pipe:3",

        "-c:v", "copy", "-c:a", "copy",

        "-preset", "veryfast",

        "-metadata", fmt.Sprintf(`title=%s`, vi.GetTitle()),

        "-movflags", "frag_keyframe+empty_moov",

        "-f", "mp4",

        "-")



    youtubevideo := exec.Command(YoutubeDLPath, "-c", "-f", fmt.Sprintf("%s/bestvideo[ext=mp4]/bestvideo/best", vi.GetFormat()), "--no-cache-dir", "--restrict-filenames", "--hls-prefer-native", "-o", "-", fmt.Sprintf("%s", vi.GetVideoUrl()))

    fmt.Println(youtubevideo)


    youtube := exec.Command(YoutubeDLPath, "-c", "-f", "bestaudio[ext=m4a]/bestaudio/best", "--no-cache-dir", "--restrict-filenames", "--hls-prefer-native", "-o", "-", fmt.Sprintf("%s", vi.GetVideoUrl()))

    fmt.Println(youtube)


    var ytvbuf, ytbuf, ffbuf bytes.Buffer

    youtubevideo.Stderr = &ytvbuf

    youtube.Stderr = &ytbuf

    ffmpeg.Stderr = &ffbuf


    video, err := youtubevideo.StdoutPipe()

    if err != nil {

        log.Printf("pipeThruFfmpegToMp4: %v\n", err)

        return err

    }


    pipe3, err := youtube.StdoutPipe()

    if err != nil {

        log.Printf("pipeThruFfmpegToMp4: %v\n", err)

        return err

    }


    ffmpeg.Stdin = video

    ffmpeg.ExtraFiles = []*os.File{pipe3.(*os.File)}

    ffmpeg.Stdout = rw


    // Headers sent, no turning back now

    rw.Header().Set("Content-Type", "video/mp4")

    rw.Header().Set("Content-Disposition", fmt.Sprintf("attachment;filename=\"%s.mp4\"", vi.GetSlug()))

    rw.Flush()


    ffmpeg.Start()

    youtubevideo.Start()

    youtube.Start()

    ffmpeg.Wait()

    youtubevideo.Wait()

    youtube.Wait()


问题是一切运行正常,但过了一会儿,内存开始填满服务器,直到它出现错误runtime/cgo: pthread_create failed: Resource temporarily unavailable


我不确定这是否是内存泄漏,或者 youtube-dl 的任一实例是否未正确关闭,或者 ffmpeg 是否未正确关闭并且随着程序运行更多而消耗越来越多的内存,直到程序因此错误而崩溃


一只甜甜圈
浏览 1218回答 2
2回答

慕标琳琳

我有时会遇到问题,您的代码不会终止youtube-dl进程。视频被转换,但过程被保留。作为第一个指标,观察进程计数,如下所示:ps | grep youtube-dl | wc -l可以让子进程超时,对于一个子进程,这看起来像这样:package mainimport (&nbsp; &nbsp; "bytes"&nbsp; &nbsp; "fmt"&nbsp; &nbsp; "os/exec"&nbsp; &nbsp; "time")func main() {&nbsp; &nbsp; // Sleep is used, so we can control how long it runs.&nbsp; &nbsp; cmd := exec.Command("sleep", "2")&nbsp; &nbsp; // Use a bytes.Buffer to get the output&nbsp; &nbsp; var buf bytes.Buffer&nbsp; &nbsp; cmd.Stdout = &buf&nbsp; &nbsp; cmd.Start()&nbsp; &nbsp; // Use a channel to signal completion so we can use a select statement&nbsp; &nbsp; done := make(chan error)&nbsp; &nbsp; go func() { done <- cmd.Wait() }()&nbsp; &nbsp; // Start a timer, should be higher for your video conversion,&nbsp;&nbsp; &nbsp; // I suggest you use a value that fits both your videos and server capabilities&nbsp; &nbsp; timeout := time.After(2 * time.Second)&nbsp; &nbsp; // The select statement allows us to execute based on which channel&nbsp; &nbsp; // we get a message from first.&nbsp; &nbsp; select {&nbsp; &nbsp; case <-timeout:&nbsp; &nbsp; &nbsp; &nbsp; // Timeout happened first, kill the process and print a message.&nbsp; &nbsp; &nbsp; &nbsp; cmd.Process.Kill()&nbsp; &nbsp; &nbsp; &nbsp; fmt.Println("Command timed out")&nbsp; &nbsp; case err := <-done:&nbsp; &nbsp; &nbsp; &nbsp; // Command completed before timeout. Print output and error if it exists.&nbsp; &nbsp; &nbsp; &nbsp; fmt.Println("Output:", buf.String())&nbsp; &nbsp; &nbsp; &nbsp; if err != nil {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; fmt.Println("Non-zero exit code:", err)&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; }}在你的情况下,你可以更换&nbsp; &nbsp; ffmpeg.Start()&nbsp; &nbsp; youtubevideo.Start()&nbsp; &nbsp; youtube.Start()&nbsp; &nbsp; ffmpeg.Wait()&nbsp; &nbsp; youtubevideo.Wait()&nbsp; &nbsp; youtube.Wait()和&nbsp; &nbsp; ffmpeg.Start()&nbsp; &nbsp; youtubevideo.Start()&nbsp; &nbsp; youtube.Start()&nbsp; &nbsp; commands := 3&nbsp; &nbsp; done := make(chan error)&nbsp; &nbsp; go func() { done <- ffmpeg.Wait() }()&nbsp; &nbsp; go func() { done <- youtubevideo.Wait() }()&nbsp; &nbsp; go func() { done <- youtube.Wait() }()&nbsp; &nbsp; timeout := time.After(1 * time.Hour)Loop:&nbsp; &nbsp; for {&nbsp; &nbsp; &nbsp; &nbsp; select {&nbsp; &nbsp; &nbsp; &nbsp; case <-timeout:&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ffmpeg.Process.Kill()&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; youtubevideo.Process.Kill()&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; youtube.Process.Kill()&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; log.Println("Conversion timed out")&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; break Loop&nbsp; &nbsp; &nbsp; &nbsp; case err := <-done:&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if err != nil {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; log.Println("Non-zero exit code:", err)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; commands = commands - 1&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if commands == 0 {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; break Loop&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; }

富国沪深

因此,经过一些调试并在凯文的回答的帮助下。成功后好像youtube-dl没有关闭。鉴于如果ffmpeg完成,那么让youtube-dl实例运行没有任何意义,并且由于Wait()函数正在等待youtube-dl完成,但它似乎从未发送完成信号,所以我继续并不得不添加一个 kill 来杀死子子进程。所以我不得不添加改变这个&nbsp; &nbsp; ffmpeg.Start()&nbsp; &nbsp; youtubevideo.Start()&nbsp; &nbsp; youtube.Start()&nbsp; &nbsp; ffmpeg.Wait()&nbsp; &nbsp; youtubevideo.Wait()&nbsp; &nbsp; youtube.Wait()对此&nbsp; &nbsp; youtube.SysProcAttr = &syscall.SysProcAttr{Setpgid: true}&nbsp; &nbsp; youtubevideo.SysProcAttr = &syscall.SysProcAttr{Setpgid: true}&nbsp; &nbsp; ffmpeg.Start()&nbsp; &nbsp; youtubevideo.Start()&nbsp; &nbsp; youtube.Start()&nbsp; &nbsp; ffmpeg.Wait()&nbsp; &nbsp; syscall.Kill(-youtube.Process.Pid, syscall.SIGKILL)&nbsp; &nbsp; syscall.Kill(-youtubevideo.Process.Pid, syscall.SIGKILL)&nbsp; &nbsp; youtubevideo.Wait()&nbsp; &nbsp; youtube.Wait()请注意,因为它似乎产生了某种子子youtube.Process.Kill()进程,实际上并没有杀死该进程,这就是我必须使用的原因syscall,而且Setpgid: true
随时随地看视频慕课网APP

相关分类

Go
我要回答