mirror of
https://github.com/philomena-dev/philomena.git
synced 2025-01-19 22:27:59 +01:00
Merge pull request #388 from philomena-dev/transparency-fixes
Fix transparency issues in webm videos
This commit is contained in:
commit
0a48359ddc
2 changed files with 68 additions and 17 deletions
|
@ -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 =
|
||||||
[
|
[
|
||||||
|
|
|
@ -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"
|
||||||
|
|
Loading…
Reference in a new issue