philomena/lib/philomena/processors/webm.ex

197 lines
4.5 KiB
Elixir
Raw Normal View History

2019-11-26 01:06:40 +01:00
defmodule Philomena.Processors.Webm do
2019-11-26 03:57:47 +01:00
alias Philomena.Intensities
2019-11-26 01:06:40 +01:00
import Bitwise
2019-11-26 03:57:47 +01:00
def process(analysis, file, versions) do
dimensions = analysis.dimensions
duration = analysis.duration
preview = preview(duration, file)
palette = gif_palette(file)
2019-11-26 01:06:40 +01:00
2019-11-26 03:57:47 +01:00
{:ok, intensities} = Intensities.file(preview)
scaled = Enum.flat_map(versions, &scale_if_smaller(file, palette, duration, dimensions, &1))
2019-11-26 01:06:40 +01:00
2019-11-26 03:57:47 +01:00
%{
intensities: intensities,
thumbnails: scaled ++ [{:copy, preview, "rendered.png"}]
}
2019-11-26 01:06:40 +01:00
end
2019-11-26 03:57:47 +01:00
def post_process(_analysis, _file), do: %{}
2019-11-29 01:11:05 +01:00
def intensities(analysis, file) do
{:ok, intensities} = Intensities.file(preview(analysis.duration, file))
intensities
end
2019-11-26 03:57:47 +01:00
defp preview(duration, file) do
2019-11-26 01:06:40 +01:00
preview = Briefly.create!(extname: ".png")
{_output, 0} = System.cmd("mediathumb", [file, to_string(duration / 2), preview])
2019-11-26 03:57:47 +01:00
preview
end
defp scale_if_smaller(file, palette, _duration, dimensions, {:full, _target_dim}) do
2019-11-28 18:12:10 +01:00
{_webm, mp4} = scale_videos(file, palette, dimensions, dimensions)
2019-11-26 03:57:47 +01:00
[
2019-11-28 18:12:10 +01:00
{:symlink_original, "full.webm"},
2019-11-26 03:57:47 +01:00
{:copy, mp4, "full.mp4"}
]
2019-11-26 01:06:40 +01:00
end
2020-01-11 05:20:19 +01:00
defp scale_if_smaller(
file,
palette,
duration,
dimensions,
{thumb_name, {target_width, target_height}}
) do
2019-11-27 02:45:57 +01:00
{webm, mp4} = scale_videos(file, palette, dimensions, {target_width, target_height})
2019-11-26 03:57:47 +01:00
cond do
thumb_name in [:thumb, :thumb_small, :thumb_tiny] ->
gif = scale_gif(file, palette, duration, {target_width, target_height})
2019-11-26 03:57:47 +01:00
[
{:copy, webm, "#{thumb_name}.webm"},
{:copy, mp4, "#{thumb_name}.mp4"},
{:copy, gif, "#{thumb_name}.gif"}
]
true ->
[
{:copy, webm, "#{thumb_name}.webm"},
{:copy, mp4, "#{thumb_name}.mp4"}
]
end
2019-11-26 01:06:40 +01:00
end
2019-11-27 02:45:57 +01:00
defp scale_videos(file, _palette, dimensions, target_dimensions) do
{width, height} = box_dimensions(dimensions, target_dimensions)
2019-11-26 03:57:47 +01:00
webm = Briefly.create!(extname: ".webm")
2020-01-11 05:20:19 +01:00
mp4 = Briefly.create!(extname: ".mp4")
2019-11-27 02:45:57 +01:00
scale_filter = "scale=w=#{width}:h=#{height}"
2019-11-26 01:06:40 +01:00
{_output, 0} =
2020-01-11 05:20:19 +01:00
System.cmd("ffmpeg", [
"-loglevel",
"0",
"-y",
"-i",
file,
"-c:v",
"libvpx",
2020-04-03 03:45:21 +02:00
"-deadline",
2020-01-11 05:20:19 +01:00
"good",
"-cpu-used",
2020-04-03 03:45:21 +02:00
"5",
2020-01-11 05:20:19 +01:00
"-auto-alt-ref",
"0",
2020-04-03 03:45:21 +02:00
"-qmin",
"15",
"-qmax",
"35",
2020-01-11 05:20:19 +01:00
"-crf",
2020-04-03 03:45:21 +02:00
"31",
2020-01-11 05:20:19 +01:00
"-vf",
scale_filter,
2020-04-03 03:45:21 +02:00
"-threads",
"1",
"-max_muxing_queue_size",
2020-05-26 23:30:22 +02:00
"4096",
2020-01-11 05:20:19 +01:00
webm
])
2019-11-26 03:57:47 +01:00
{_output, 0} =
2020-01-11 05:20:19 +01:00
System.cmd("ffmpeg", [
"-loglevel",
"0",
"-y",
"-i",
file,
"-c:v",
"libx264",
"-pix_fmt",
"yuv420p",
"-profile:v",
"main",
"-preset",
"medium",
"-crf",
"18",
"-b:v",
"5M",
"-vf",
scale_filter,
2020-04-03 03:45:21 +02:00
"-threads",
"1",
"-max_muxing_queue_size",
2020-05-26 23:30:22 +02:00
"4096",
2020-01-11 05:20:19 +01:00
mp4
])
2019-11-26 03:57:47 +01:00
{webm, mp4}
end
2020-05-27 18:31:31 +02:00
defp scale_gif(file, palette, duration, {width, height}) do
2019-11-26 03:57:47 +01:00
gif = Briefly.create!(extname: ".gif")
2020-01-11 05:20:19 +01:00
scale_filter = "scale=w=#{width}:h=#{height}:force_original_aspect_ratio=decrease"
2019-11-26 03:57:47 +01:00
palette_filter = "paletteuse=dither=bayer:bayer_scale=5:diff_mode=rectangle"
2020-05-27 18:31:31 +02:00
rate_filter = rate_filter(duration)
2020-05-20 01:19:53 +02:00
filter_graph = "[0:v]#{scale_filter},#{rate_filter}[x];[x][1:v]#{palette_filter}"
2019-11-26 03:57:47 +01:00
{_output, 0} =
2020-01-11 05:20:19 +01:00
System.cmd("ffmpeg", [
"-loglevel",
"0",
"-y",
"-i",
file,
"-i",
palette,
"-lavfi",
filter_graph,
"-r",
"2",
gif
])
2019-11-26 03:57:47 +01:00
gif
end
defp gif_palette(file) do
palette = Briefly.create!(extname: ".png")
{_output, 0} =
2020-01-11 05:20:19 +01:00
System.cmd("ffmpeg", [
"-loglevel",
"0",
"-y",
"-i",
file,
"-vf",
"palettegen=stats_mode=diff",
palette
])
2019-11-26 01:06:40 +01:00
2019-11-26 03:57:47 +01:00
palette
2019-11-26 01:06:40 +01:00
end
2019-11-27 02:45:57 +01:00
# x264 requires image dimensions to be a multiple of 2
# -2 = ~1
def box_dimensions({width, height}, {target_width, target_height}) do
2020-01-11 05:20:19 +01:00
ratio = min(target_width / width, target_height / height)
new_width = min(max(trunc(width * ratio) &&& -2, 2), target_width)
2019-11-27 02:45:57 +01:00
new_height = min(max(trunc(height * ratio) &&& -2, 2), target_height)
{new_width, new_height}
2019-11-26 01:06:40 +01:00
end
2020-05-27 18:31:31 +02:00
# Avoid division by zero
def rate_filter(duration) when duration > 0.5, do: "fps=1/#{duration / 10},settb=1/2,setpts=N"
def rate_filter(duration), do: "setpts=N/TB/2"
2019-12-22 05:57:59 +01:00
end