diff --git a/lib/philomena_media/gif_preview.ex b/lib/philomena_media/gif_preview.ex index fe3f7914..ac40044b 100644 --- a/lib/philomena_media/gif_preview.ex +++ b/lib/philomena_media/gif_preview.ex @@ -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. @@ -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 = @@ -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] ... @@ -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 = [ diff --git a/lib/philomena_media/processors/webm.ex b/lib/philomena_media/processors/webm.ex index 22c54a6d..0fcc1129 100644 --- a/lib/philomena_media/processors/webm.ex +++ b/lib/philomena_media/processors/webm.ex @@ -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"}] [ @@ -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"}, @@ -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") @@ -113,6 +114,8 @@ defmodule PhilomenaMedia.Processors.Webm do "-loglevel", "0", "-y", + "-c:v", + decoder, "-i", file, "-c:v", @@ -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") @@ -171,6 +174,8 @@ defmodule PhilomenaMedia.Processors.Webm do "-loglevel", "0", "-y", + "-c:v", + decoder, "-i", file, "-c:v", @@ -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"