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
This commit is contained in:
parent
548434f85c
commit
0ac010ba3f
|
@ -43,89 +43,28 @@ def image_resize(url, options) do
|
||||||
def video_framegrab(url) do
|
def video_framegrab(url) do
|
||||||
with executable when is_binary(executable) <- System.find_executable("ffmpeg"),
|
with executable when is_binary(executable) <- System.find_executable("ffmpeg"),
|
||||||
{:ok, env} <- HTTP.get(url, [], pool: :media),
|
{:ok, env} <- HTTP.get(url, [], pool: :media),
|
||||||
{:ok, fifo_path} <- mkfifo(),
|
{:ok, pid} <- StringIO.open(env.body) do
|
||||||
args = [
|
body_stream = IO.binstream(pid, 1)
|
||||||
"-y",
|
|
||||||
|
Exile.stream!(
|
||||||
|
[
|
||||||
|
executable,
|
||||||
"-i",
|
"-i",
|
||||||
fifo_path,
|
"pipe:0",
|
||||||
"-vframes",
|
"-vframes",
|
||||||
"1",
|
"1",
|
||||||
"-f",
|
"-f",
|
||||||
"mjpeg",
|
"mjpeg",
|
||||||
"-loglevel",
|
"pipe:1"
|
||||||
"error",
|
],
|
||||||
"-"
|
input: body_stream,
|
||||||
] do
|
ignore_epipe: true,
|
||||||
run_fifo(fifo_path, env, executable, args)
|
stderr: :disable
|
||||||
|
)
|
||||||
|
|> Enum.into(<<>>)
|
||||||
else
|
else
|
||||||
nil -> {:error, {:ffmpeg, :command_not_found}}
|
nil -> {:error, {:ffmpeg, :command_not_found}}
|
||||||
{:error, _} = error -> error
|
{:error, _} = error -> error
|
||||||
end
|
end
|
||||||
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
|
end
|
||||||
|
|
1
mix.exs
1
mix.exs
|
@ -184,6 +184,7 @@ defp deps do
|
||||||
{:vix, "~> 0.26.0"},
|
{:vix, "~> 0.26.0"},
|
||||||
{:elixir_make, "~> 0.7.7", override: true},
|
{:elixir_make, "~> 0.7.7", override: true},
|
||||||
{:blurhash, "~> 0.1.0", hex: :rinpatch_blurhash},
|
{:blurhash, "~> 0.1.0", hex: :rinpatch_blurhash},
|
||||||
|
{:exile, "~> 0.8.0"},
|
||||||
|
|
||||||
## dev & test
|
## dev & test
|
||||||
{:ex_doc, "~> 0.22", only: :dev, runtime: false},
|
{:ex_doc, "~> 0.22", only: :dev, runtime: false},
|
||||||
|
|
1
mix.lock
1
mix.lock
|
@ -46,6 +46,7 @@
|
||||||
"ex_doc": {:hex, :ex_doc, "0.29.4", "6257ecbb20c7396b1fe5accd55b7b0d23f44b6aa18017b415cb4c2b91d997729", [:mix], [{:earmark_parser, "~> 1.4.31", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.14", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1", [hex: :makeup_erlang, repo: "hexpm", optional: false]}], "hexpm", "2c6699a737ae46cb61e4ed012af931b57b699643b24dabe2400a8168414bc4f5"},
|
"ex_doc": {:hex, :ex_doc, "0.29.4", "6257ecbb20c7396b1fe5accd55b7b0d23f44b6aa18017b415cb4c2b91d997729", [:mix], [{:earmark_parser, "~> 1.4.31", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.14", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1", [hex: :makeup_erlang, repo: "hexpm", optional: false]}], "hexpm", "2c6699a737ae46cb61e4ed012af931b57b699643b24dabe2400a8168414bc4f5"},
|
||||||
"ex_machina": {:hex, :ex_machina, "2.7.0", "b792cc3127fd0680fecdb6299235b4727a4944a09ff0fa904cc639272cd92dc7", [:mix], [{:ecto, "~> 2.2 or ~> 3.0", [hex: :ecto, repo: "hexpm", optional: true]}, {:ecto_sql, "~> 3.0", [hex: :ecto_sql, repo: "hexpm", optional: true]}], "hexpm", "419aa7a39bde11894c87a615c4ecaa52d8f107bbdd81d810465186f783245bf8"},
|
"ex_machina": {:hex, :ex_machina, "2.7.0", "b792cc3127fd0680fecdb6299235b4727a4944a09ff0fa904cc639272cd92dc7", [:mix], [{:ecto, "~> 2.2 or ~> 3.0", [hex: :ecto, repo: "hexpm", optional: true]}, {:ecto_sql, "~> 3.0", [hex: :ecto_sql, repo: "hexpm", optional: true]}], "hexpm", "419aa7a39bde11894c87a615c4ecaa52d8f107bbdd81d810465186f783245bf8"},
|
||||||
"ex_syslogger": {:hex, :ex_syslogger, "1.5.2", "72b6aa2d47a236e999171f2e1ec18698740f40af0bd02c8c650bf5f1fd1bac79", [:mix], [{:poison, ">= 1.5.0", [hex: :poison, repo: "hexpm", optional: true]}, {:syslog, "~> 1.1.0", [hex: :syslog, repo: "hexpm", optional: false]}], "hexpm", "ab9fab4136dbc62651ec6f16fa4842f10cf02ab4433fa3d0976c01be99398399"},
|
"ex_syslogger": {:hex, :ex_syslogger, "1.5.2", "72b6aa2d47a236e999171f2e1ec18698740f40af0bd02c8c650bf5f1fd1bac79", [:mix], [{:poison, ">= 1.5.0", [hex: :poison, repo: "hexpm", optional: true]}, {:syslog, "~> 1.1.0", [hex: :syslog, repo: "hexpm", optional: false]}], "hexpm", "ab9fab4136dbc62651ec6f16fa4842f10cf02ab4433fa3d0976c01be99398399"},
|
||||||
|
"exile": {:hex, :exile, "0.8.0", "7287f76add343c5dcf5e5b46066ebb93b02b7e643cce7a1a1c5eab3e7d9eb5d8", [:make, :mix], [{:elixir_make, "~> 0.6", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm", "63309c36590f3a524f513255601404e6672be4c8bea0cc4420fbae02e7ffffac"},
|
||||||
"expo": {:hex, :expo, "0.4.1", "1c61d18a5df197dfda38861673d392e642649a9cef7694d2f97a587b2cfb319b", [:mix], [], "hexpm", "2ff7ba7a798c8c543c12550fa0e2cbc81b95d4974c65855d8d15ba7b37a1ce47"},
|
"expo": {:hex, :expo, "0.4.1", "1c61d18a5df197dfda38861673d392e642649a9cef7694d2f97a587b2cfb319b", [:mix], [], "hexpm", "2ff7ba7a798c8c543c12550fa0e2cbc81b95d4974c65855d8d15ba7b37a1ce47"},
|
||||||
"fast_html": {:hex, :fast_html, "2.0.5", "c61760340606c1077ff1f196f17834056cb1dd3d5cb92a9f2cabf28bc6221c3c", [:make, :mix], [{:elixir_make, "~> 0.4", [hex: :elixir_make, repo: "hexpm", optional: false]}, {:nimble_pool, "~> 0.2.0", [hex: :nimble_pool, repo: "hexpm", optional: false]}], "hexpm", "605f4f4829443c14127694ebabb681778712ceecb4470ec32aa31012330e6506"},
|
"fast_html": {:hex, :fast_html, "2.0.5", "c61760340606c1077ff1f196f17834056cb1dd3d5cb92a9f2cabf28bc6221c3c", [:make, :mix], [{:elixir_make, "~> 0.4", [hex: :elixir_make, repo: "hexpm", optional: false]}, {:nimble_pool, "~> 0.2.0", [hex: :nimble_pool, repo: "hexpm", optional: false]}], "hexpm", "605f4f4829443c14127694ebabb681778712ceecb4470ec32aa31012330e6506"},
|
||||||
"fast_sanitize": {:hex, :fast_sanitize, "0.2.3", "67b93dfb34e302bef49fec3aaab74951e0f0602fd9fa99085987af05bd91c7a5", [:mix], [{:fast_html, "~> 2.0", [hex: :fast_html, repo: "hexpm", optional: false]}, {:plug, "~> 1.8", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "e8ad286d10d0386e15d67d0ee125245ebcfbc7d7290b08712ba9013c8c5e56e2"},
|
"fast_sanitize": {:hex, :fast_sanitize, "0.2.3", "67b93dfb34e302bef49fec3aaab74951e0f0602fd9fa99085987af05bd91c7a5", [:mix], [{:fast_html, "~> 2.0", [hex: :fast_html, repo: "hexpm", optional: false]}, {:plug, "~> 1.8", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "e8ad286d10d0386e15d67d0ee125245ebcfbc7d7290b08712ba9013c8c5e56e2"},
|
||||||
|
|
Loading…
Reference in New Issue