Streaming porcelain response to ffmpeg to file download

Hey guys -

I wrote this post as a question… but as I was writing I just got it to work so this is more a code snippet in case anyone is interested.

I want a user to access a route with some parameters, and depending on the parameters, adjust ffmpeg filters and allow the user to download the file as it’s being processed by ffmpeg.

I also didn’t want to create any temp files.

I’m setting the headers and sending a chunked/streamed response with this code:

    conn =
      conn
      |> put_resp_header("content-type", "video/mp4")
      |> put_resp_header("content-disposition", "attachment; filename=video.mp4")
      |> send_chunked(200)

    porc = Porcelain.spawn_shell("ffmpeg -i input.avi -f mp4 -movflags frag_keyframe+empty_moov pipe:", out: :stream)

    Enum.reduce_while(porc.out, conn, fn (chunk, conn) ->
     case Plug.Conn.chunk(conn, chunk) do
         {:ok, conn} ->
           {:cont, conn}
         {:error, :closed} ->
           {:halt, conn}
       end
     end)
  end

No error handling yet but in the happy case it works.

9 Likes

This is great, thanks for sharing!

1 Like

awesome… I just might use this to create timelapse movies on the fly (some day)… thanks!

2 Likes

I have written whole project like that (but with erlexec instead of Porcelain):

2 Likes