Merge pull request #388 from philomena-dev/transparency-fixes

Fix transparency issues in webm videos
This commit is contained in:
liamwhite 2024-12-26 09:53:10 -05:00 committed by GitHub
commit 0a48359ddc
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 68 additions and 17 deletions

View file

@ -13,6 +13,9 @@ defmodule PhilomenaMedia.GifPreview do
target_framerate: target_framerate() target_framerate: target_framerate()
] ]
@typedoc "One of av1, h264, libvpx, libvpx-vp9"
@type decoder :: String.t()
@doc """ @doc """
Generate a GIF preview of the given video input with evenly-spaced sample points. Generate a GIF preview of the given video input with evenly-spaced sample points.
@ -31,8 +34,8 @@ defmodule PhilomenaMedia.GifPreview do
* 1 or above: 5 images * 1 or above: 5 images
* otherwise: 2 images * otherwise: 2 images
""" """
@spec preview(Path.t(), Path.t(), duration(), dimensions(), opts()) :: :ok @spec preview(decoder(), Path.t(), Path.t(), duration(), dimensions(), opts()) :: :ok
def preview(video, gif, duration, dimensions, opts \\ []) do def preview(decoder, video, gif, duration, dimensions, opts \\ []) do
target_framerate = Keyword.get(opts, :target_framerate, 2) target_framerate = Keyword.get(opts, :target_framerate, 2)
num_images = num_images =
@ -48,22 +51,41 @@ defmodule PhilomenaMedia.GifPreview do
{_output, 0} = {_output, 0} =
System.cmd( System.cmd(
"ffmpeg", "ffmpeg",
commands(video, gif, clamp(duration), dimensions, num_images, target_framerate) commands(decoder, video, gif, clamp(duration), dimensions, num_images, target_framerate)
) )
:ok :ok
end end
@spec commands(Path.t(), Path.t(), duration(), dimensions(), num_images(), target_framerate()) :: @spec commands(
decoder(),
Path.t(),
Path.t(),
duration(),
dimensions(),
num_images(),
target_framerate()
) ::
[String.t()] [String.t()]
defp commands(video, gif, duration, {target_width, target_height}, num_images, target_framerate) do defp commands(
decoder,
video,
gif,
duration,
{target_width, target_height},
num_images,
target_framerate
) do
# Compute range [0, num_images) # Compute range [0, num_images)
image_range = 0..(num_images - 1) image_range = 0..(num_images - 1)
# Generate input list in the following form: # Generate input list in the following form:
# -ss 0.0 -i input.webm # -ss 0.0 -c:v libvpx -i input.webm
input_arguments = input_arguments =
Enum.flat_map(image_range, &["-ss", "#{&1 * duration / num_images}", "-i", video]) Enum.flat_map(
image_range,
&["-ss", "#{&1 * duration / num_images}", "-c:v", decoder, "-i", video]
)
# Generate graph in the following form: # Generate graph in the following form:
# [0:v] trim=end_frame=1 [t0]; [1:v] trim=end_frame=1 [t1] ... # [0:v] trim=end_frame=1 [t0]; [1:v] trim=end_frame=1 [t1] ...
@ -87,7 +109,7 @@ defmodule PhilomenaMedia.GifPreview do
"[s0] palettegen=stats_mode=single:max_colors=255:reserve_transparent=1 [palettegen]" "[s0] palettegen=stats_mode=single:max_colors=255:reserve_transparent=1 [palettegen]"
paletteuse_filter = paletteuse_filter =
"[s1][palettegen] paletteuse=dither=bayer:bayer_scale=5:new=1:alpha_threshold=255" "[s1][palettegen] paletteuse=dither=bayer:bayer_scale=5:new=1:alpha_threshold=251"
filter_graph = filter_graph =
[ [

View file

@ -29,11 +29,12 @@ defmodule PhilomenaMedia.Processors.Webm do
duration = analysis.duration duration = analysis.duration
stripped = strip(file) stripped = strip(file)
preview = preview(duration, stripped) preview = preview(duration, stripped)
mp4 = scale_mp4_only(stripped, dimensions, dimensions) decoder = select_decoder(file)
mp4 = scale_mp4_only(decoder, stripped, dimensions, dimensions)
{:ok, intensities} = Intensities.file(preview) {:ok, intensities} = Intensities.file(preview)
scaled = Enum.flat_map(versions, &scale(stripped, duration, dimensions, &1)) scaled = Enum.flat_map(versions, &scale(decoder, stripped, duration, dimensions, &1))
mp4 = [{:copy, mp4, "full.mp4"}] mp4 = [{:copy, mp4, "full.mp4"}]
[ [
@ -82,12 +83,12 @@ defmodule PhilomenaMedia.Processors.Webm do
stripped stripped
end end
defp scale(file, duration, dimensions, {thumb_name, target_dimensions}) do defp scale(decoder, file, duration, dimensions, {thumb_name, target_dimensions}) do
{webm, mp4} = scale_videos(file, dimensions, target_dimensions) {webm, mp4} = scale_videos(decoder, file, dimensions, target_dimensions)
cond do cond do
thumb_name in [:thumb, :thumb_small, :thumb_tiny] -> thumb_name in [:thumb, :thumb_small, :thumb_tiny] ->
gif = scale_gif(file, duration, dimensions, target_dimensions) gif = scale_gif(decoder, file, duration, dimensions, target_dimensions)
[ [
{:copy, webm, "#{thumb_name}.webm"}, {:copy, webm, "#{thumb_name}.webm"},
@ -103,7 +104,7 @@ defmodule PhilomenaMedia.Processors.Webm do
end end
end end
defp scale_videos(file, dimensions, target_dimensions) do defp scale_videos(decoder, file, dimensions, target_dimensions) do
filter = scale_filter(dimensions, target_dimensions) filter = scale_filter(dimensions, target_dimensions)
webm = Briefly.create!(extname: ".webm") webm = Briefly.create!(extname: ".webm")
mp4 = Briefly.create!(extname: ".mp4") mp4 = Briefly.create!(extname: ".mp4")
@ -113,6 +114,8 @@ defmodule PhilomenaMedia.Processors.Webm do
"-loglevel", "-loglevel",
"0", "0",
"-y", "-y",
"-c:v",
decoder,
"-i", "-i",
file, file,
"-c:v", "-c:v",
@ -162,7 +165,7 @@ defmodule PhilomenaMedia.Processors.Webm do
{webm, mp4} {webm, mp4}
end end
defp scale_mp4_only(file, dimensions, target_dimensions) do defp scale_mp4_only(decoder, file, dimensions, target_dimensions) do
filter = scale_filter(dimensions, target_dimensions) filter = scale_filter(dimensions, target_dimensions)
mp4 = Briefly.create!(extname: ".mp4") mp4 = Briefly.create!(extname: ".mp4")
@ -171,6 +174,8 @@ defmodule PhilomenaMedia.Processors.Webm do
"-loglevel", "-loglevel",
"0", "0",
"-y", "-y",
"-c:v",
decoder,
"-i", "-i",
file, file,
"-c:v", "-c:v",
@ -197,15 +202,39 @@ defmodule PhilomenaMedia.Processors.Webm do
mp4 mp4
end end
defp scale_gif(file, duration, dimensions, target_dimensions) do defp scale_gif(decoder, file, duration, dimensions, target_dimensions) do
{width, height} = box_dimensions(dimensions, target_dimensions) {width, height} = box_dimensions(dimensions, target_dimensions)
gif = Briefly.create!(extname: ".gif") gif = Briefly.create!(extname: ".gif")
GifPreview.preview(file, gif, duration, {width, height}) GifPreview.preview(decoder, file, gif, duration, {width, height})
gif gif
end end
defp select_decoder(file) do
{output, 0} =
System.cmd("ffprobe", [
"-loglevel",
"0",
"-select_streams",
"v:0",
"-show_entries",
"stream=codec_name",
"-of",
"default=noprint_wrappers=1:nokey=1",
"-i",
file
])
# Mediatools verifies that we only have one video stream and that it is
# one of the supported formats, so the following is safe to do:
case output do
"vp8\n" -> "libvpx"
"vp9\n" -> "libvpx-vp9"
"av1\n" -> "av1"
end
end
defp scale_filter(dimensions, target_dimensions) do defp scale_filter(dimensions, target_dimensions) do
{width, height} = box_dimensions(dimensions, target_dimensions) {width, height} = box_dimensions(dimensions, target_dimensions)
"scale=w=#{width}:h=#{height},setsar=1" "scale=w=#{width}:h=#{height},setsar=1"