From 0ac010ba3fa41c9bd06565259de57f2a5b5bb8ad Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Mon, 22 Jan 2024 10:01:29 -0500 Subject: Replace custom fifo implementation with Exile This is for streaming media to ffmpeg thumbnailer. The existing implementation relies on undocumented behavior. Erlang open_port/2 does not officially support passing a string of a file path for opening. The specs clearly state you are to provide one of the following for open_port/2: {spawn, Command :: string() | binary()} | {spawn_driver, Command :: string() | binary()} | {spawn_executable, FileName :: file:name_all()} | {fd, In :: integer() >= 0, Out :: integer() >= 0} Our method technically works but is strongly discouraged as it can block the scheduler and dialyzer throws errors as it recognizes we're breaking the contract and some of the functions we wrote may never return. This is indirectly covered by the Erlang FAQ section "9.12 Why can't I open devices (e.g. a serial port) like normal files?" https://www.erlang.org/faq/problems#idm1127 --- lib/pleroma/helpers/media_helper.ex | 99 +++++++------------------------------ 1 file changed, 19 insertions(+), 80 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/helpers/media_helper.ex b/lib/pleroma/helpers/media_helper.ex index 07dfea55b..1a414b37f 100644 --- a/lib/pleroma/helpers/media_helper.ex +++ b/lib/pleroma/helpers/media_helper.ex @@ -43,89 +43,28 @@ defmodule Pleroma.Helpers.MediaHelper do def video_framegrab(url) do with executable when is_binary(executable) <- System.find_executable("ffmpeg"), {:ok, env} <- HTTP.get(url, [], pool: :media), - {:ok, fifo_path} <- mkfifo(), - args = [ - "-y", - "-i", - fifo_path, - "-vframes", - "1", - "-f", - "mjpeg", - "-loglevel", - "error", - "-" - ] do - run_fifo(fifo_path, env, executable, args) + {:ok, pid} <- StringIO.open(env.body) do + body_stream = IO.binstream(pid, 1) + + Exile.stream!( + [ + executable, + "-i", + "pipe:0", + "-vframes", + "1", + "-f", + "mjpeg", + "pipe:1" + ], + input: body_stream, + ignore_epipe: true, + stderr: :disable + ) + |> Enum.into(<<>>) else nil -> {:error, {:ffmpeg, :command_not_found}} {:error, _} = error -> error end end - - defp run_fifo(fifo_path, env, executable, args) do - pid = - Port.open({:spawn_executable, executable}, [ - :use_stdio, - :stream, - :exit_status, - :binary, - args: args - ]) - - fifo = Port.open(to_charlist(fifo_path), [:eof, :binary, :stream, :out]) - fix = Pleroma.Helpers.QtFastStart.fix(env.body) - true = Port.command(fifo, fix) - :erlang.port_close(fifo) - loop_recv(pid) - after - File.rm(fifo_path) - end - - defp mkfifo do - path = Path.join(System.tmp_dir!(), "pleroma-media-preview-pipe-#{Ecto.UUID.generate()}") - - case System.cmd("mkfifo", [path]) do - {_, 0} -> - spawn(fifo_guard(path)) - {:ok, path} - - {_, err} -> - {:error, {:fifo_failed, err}} - end - end - - defp fifo_guard(path) do - pid = self() - - fn -> - ref = Process.monitor(pid) - - receive do - {:DOWN, ^ref, :process, ^pid, _} -> - File.rm(path) - end - end - end - - defp loop_recv(pid) do - loop_recv(pid, <<>>) - end - - defp loop_recv(pid, acc) do - receive do - {^pid, {:data, data}} -> - loop_recv(pid, acc <> data) - - {^pid, {:exit_status, 0}} -> - {:ok, acc} - - {^pid, {:exit_status, status}} -> - {:error, status} - after - 5000 -> - :erlang.port_close(pid) - {:error, :timeout} - end - end end -- cgit v1.2.3