Skip to content

Commit

Permalink
Merge pull request #388 from philomena-dev/transparency-fixes
Browse files Browse the repository at this point in the history
Fix transparency issues in webm videos
  • Loading branch information
liamwhite authored Dec 26, 2024
2 parents d695bf1 + 9b022e7 commit 0a48359
Show file tree
Hide file tree
Showing 2 changed files with 68 additions and 17 deletions.
38 changes: 30 additions & 8 deletions lib/philomena_media/gif_preview.ex
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ defmodule PhilomenaMedia.GifPreview do
target_framerate: target_framerate()
]

@typedoc "One of av1, h264, libvpx, libvpx-vp9"
@type decoder :: String.t()

@doc """
Generate a GIF preview of the given video input with evenly-spaced sample points.
Expand All @@ -31,8 +34,8 @@ defmodule PhilomenaMedia.GifPreview do
* 1 or above: 5 images
* otherwise: 2 images
"""
@spec preview(Path.t(), Path.t(), duration(), dimensions(), opts()) :: :ok
def preview(video, gif, duration, dimensions, opts \\ []) do
@spec preview(decoder(), Path.t(), Path.t(), duration(), dimensions(), opts()) :: :ok
def preview(decoder, video, gif, duration, dimensions, opts \\ []) do
target_framerate = Keyword.get(opts, :target_framerate, 2)

num_images =
Expand All @@ -48,22 +51,41 @@ defmodule PhilomenaMedia.GifPreview do
{_output, 0} =
System.cmd(
"ffmpeg",
commands(video, gif, clamp(duration), dimensions, num_images, target_framerate)
commands(decoder, video, gif, clamp(duration), dimensions, num_images, target_framerate)
)

:ok
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()]
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)
image_range = 0..(num_images - 1)

# Generate input list in the following form:
# -ss 0.0 -i input.webm
# -ss 0.0 -c:v libvpx -i input.webm
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:
# [0:v] trim=end_frame=1 [t0]; [1:v] trim=end_frame=1 [t1] ...
Expand All @@ -87,7 +109,7 @@ defmodule PhilomenaMedia.GifPreview do
"[s0] palettegen=stats_mode=single:max_colors=255:reserve_transparent=1 [palettegen]"

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 =
[
Expand Down
47 changes: 38 additions & 9 deletions lib/philomena_media/processors/webm.ex
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,12 @@ defmodule PhilomenaMedia.Processors.Webm do
duration = analysis.duration
stripped = strip(file)
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)

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"}]

[
Expand Down Expand Up @@ -82,12 +83,12 @@ defmodule PhilomenaMedia.Processors.Webm do
stripped
end

defp scale(file, duration, dimensions, {thumb_name, target_dimensions}) do
{webm, mp4} = scale_videos(file, dimensions, target_dimensions)
defp scale(decoder, file, duration, dimensions, {thumb_name, target_dimensions}) do
{webm, mp4} = scale_videos(decoder, file, dimensions, target_dimensions)

cond do
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"},
Expand All @@ -103,7 +104,7 @@ defmodule PhilomenaMedia.Processors.Webm do
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)
webm = Briefly.create!(extname: ".webm")
mp4 = Briefly.create!(extname: ".mp4")
Expand All @@ -113,6 +114,8 @@ defmodule PhilomenaMedia.Processors.Webm do
"-loglevel",
"0",
"-y",
"-c:v",
decoder,
"-i",
file,
"-c:v",
Expand Down Expand Up @@ -162,7 +165,7 @@ defmodule PhilomenaMedia.Processors.Webm do
{webm, mp4}
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)
mp4 = Briefly.create!(extname: ".mp4")

Expand All @@ -171,6 +174,8 @@ defmodule PhilomenaMedia.Processors.Webm do
"-loglevel",
"0",
"-y",
"-c:v",
decoder,
"-i",
file,
"-c:v",
Expand All @@ -197,15 +202,39 @@ defmodule PhilomenaMedia.Processors.Webm do
mp4
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)
gif = Briefly.create!(extname: ".gif")

GifPreview.preview(file, gif, duration, {width, height})
GifPreview.preview(decoder, file, gif, duration, {width, height})

gif
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
{width, height} = box_dimensions(dimensions, target_dimensions)
"scale=w=#{width}:h=#{height},setsar=1"
Expand Down

0 comments on commit 0a48359

Please sign in to comment.