mirror of
https://github.com/philomena-dev/philomena.git
synced 2024-11-27 05:37:59 +01:00
wip
This commit is contained in:
parent
19eb5be999
commit
6151138525
12 changed files with 744 additions and 0 deletions
36
lib/philomena_media/libavcodec/aac.ex
Normal file
36
lib/philomena_media/libavcodec/aac.ex
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
defmodule PhilomenaMedia.Libavcodec.Aac do
|
||||||
|
@moduledoc """
|
||||||
|
Represents the `aac` encoder, which takes an audio input and generates encoded output.
|
||||||
|
|
||||||
|
## Example
|
||||||
|
|
||||||
|
Aac.new()
|
||||||
|
|
||||||
|
No options are exposed. However, see https://ffmpeg.org/ffmpeg-codecs.html#aac for
|
||||||
|
additional information.
|
||||||
|
"""
|
||||||
|
|
||||||
|
@type opts :: []
|
||||||
|
|
||||||
|
@type t :: %__MODULE__{
|
||||||
|
name: String.t(),
|
||||||
|
opts: opts(),
|
||||||
|
force_format: nil,
|
||||||
|
type: :audio
|
||||||
|
}
|
||||||
|
|
||||||
|
defstruct name: "aac",
|
||||||
|
opts: [],
|
||||||
|
force_format: nil,
|
||||||
|
type: :audio
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Construct a new AAC encoder.
|
||||||
|
|
||||||
|
See module documentation for usage.
|
||||||
|
"""
|
||||||
|
@spec new() :: t()
|
||||||
|
def new do
|
||||||
|
%__MODULE__{}
|
||||||
|
end
|
||||||
|
end
|
36
lib/philomena_media/libavcodec/encode_stream.ex
Normal file
36
lib/philomena_media/libavcodec/encode_stream.ex
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
defmodule PhilomenaMedia.Libavcodec.EncodeStream do
|
||||||
|
@moduledoc """
|
||||||
|
Represents a stream which encodes data.
|
||||||
|
"""
|
||||||
|
|
||||||
|
@type threads :: non_neg_integer()
|
||||||
|
@type slices :: non_neg_integer()
|
||||||
|
@type max_muxing_queue_size :: non_neg_integer()
|
||||||
|
|
||||||
|
@type opts :: [
|
||||||
|
threads: threads(),
|
||||||
|
slices: slices(),
|
||||||
|
max_muxing_queue_size: max_muxing_queue_size()
|
||||||
|
]
|
||||||
|
|
||||||
|
@type t :: %__MODULE__{
|
||||||
|
encoder: nil | struct(),
|
||||||
|
opts: opts()
|
||||||
|
}
|
||||||
|
|
||||||
|
defstruct encoder: nil,
|
||||||
|
opts: []
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Constructs a new encode stream.
|
||||||
|
|
||||||
|
See the individual encoders for additional options.
|
||||||
|
"""
|
||||||
|
@spec new(opts(), nil | struct()) :: t()
|
||||||
|
def new(opts, encoder \\ nil) do
|
||||||
|
%__MODULE__{
|
||||||
|
encoder: encoder,
|
||||||
|
opts: Keyword.take(opts, [:threads, :slices, :max_muxing_queue_size])
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
36
lib/philomena_media/libavcodec/gif.ex
Normal file
36
lib/philomena_media/libavcodec/gif.ex
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
defmodule PhilomenaMedia.Libavcodec.Gif do
|
||||||
|
@moduledoc """
|
||||||
|
Represents the `gif` encoder, which takes a video input and generates encoded output.
|
||||||
|
|
||||||
|
## Example
|
||||||
|
|
||||||
|
Gif.new()
|
||||||
|
|
||||||
|
No options are exposed. However, see https://ffmpeg.org/ffmpeg-codecs.html#GIF for
|
||||||
|
additional information.
|
||||||
|
"""
|
||||||
|
|
||||||
|
@type opts :: []
|
||||||
|
|
||||||
|
@type t :: %__MODULE__{
|
||||||
|
name: String.t(),
|
||||||
|
opts: opts(),
|
||||||
|
force_format: nil,
|
||||||
|
type: :video
|
||||||
|
}
|
||||||
|
|
||||||
|
defstruct name: "gif",
|
||||||
|
opts: [],
|
||||||
|
force_format: nil,
|
||||||
|
type: :video
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Construct a new GIF encoder.
|
||||||
|
|
||||||
|
See module documentation for usage.
|
||||||
|
"""
|
||||||
|
@spec new() :: t()
|
||||||
|
def new do
|
||||||
|
%__MODULE__{}
|
||||||
|
end
|
||||||
|
end
|
36
lib/philomena_media/libavcodec/libopus.ex
Normal file
36
lib/philomena_media/libavcodec/libopus.ex
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
defmodule PhilomenaMedia.Libavcodec.Libopus do
|
||||||
|
@moduledoc """
|
||||||
|
Represents the `libopus` encoder, which takes an audio input and generates encoded output.
|
||||||
|
|
||||||
|
## Example
|
||||||
|
|
||||||
|
Libopus.new()
|
||||||
|
|
||||||
|
No options are exposed. However, see https://ffmpeg.org/ffmpeg-codecs.html#libopus-1 for
|
||||||
|
additional information.
|
||||||
|
"""
|
||||||
|
|
||||||
|
@type opts :: []
|
||||||
|
|
||||||
|
@type t :: %__MODULE__{
|
||||||
|
name: String.t(),
|
||||||
|
opts: opts(),
|
||||||
|
force_format: nil,
|
||||||
|
type: :audio
|
||||||
|
}
|
||||||
|
|
||||||
|
defstruct name: "libopus",
|
||||||
|
opts: [],
|
||||||
|
force_format: nil,
|
||||||
|
type: :audio
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Construct a new libopus encoder.
|
||||||
|
|
||||||
|
See module documentation for usage.
|
||||||
|
"""
|
||||||
|
@spec new() :: t()
|
||||||
|
def new do
|
||||||
|
%__MODULE__{}
|
||||||
|
end
|
||||||
|
end
|
50
lib/philomena_media/libavcodec/libvpx.ex
Normal file
50
lib/philomena_media/libavcodec/libvpx.ex
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
defmodule PhilomenaMedia.Libavcodec.Libvpx do
|
||||||
|
@moduledoc """
|
||||||
|
Represents the `libvpx` (VP8) encoder, which takes a video input and generates encoded output.
|
||||||
|
|
||||||
|
## Example with all options
|
||||||
|
|
||||||
|
Libvpx.new(
|
||||||
|
deadline: :good,
|
||||||
|
"cpu-used": 5,
|
||||||
|
crf: 31
|
||||||
|
)
|
||||||
|
|
||||||
|
See https://ffmpeg.org/ffmpeg-codecs.html#libvpx for more information about the options.
|
||||||
|
"""
|
||||||
|
|
||||||
|
@type deadline :: :best | :good | :realtime
|
||||||
|
@type cpu_used :: -16..16
|
||||||
|
@type qrange :: 0..63
|
||||||
|
@type crf :: qrange()
|
||||||
|
|
||||||
|
@type opts :: [
|
||||||
|
deadline: deadline(),
|
||||||
|
"cpu-used": cpu_used(),
|
||||||
|
crf: qrange()
|
||||||
|
]
|
||||||
|
|
||||||
|
@type t :: %__MODULE__{
|
||||||
|
name: String.t(),
|
||||||
|
opts: opts(),
|
||||||
|
force_format: :yuv420p,
|
||||||
|
type: :video
|
||||||
|
}
|
||||||
|
|
||||||
|
defstruct name: "libvpx",
|
||||||
|
opts: [],
|
||||||
|
force_format: :yuv420p,
|
||||||
|
type: :video
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Construct a new libvpx (VP8) encoder.
|
||||||
|
|
||||||
|
See module documentation for usage.
|
||||||
|
"""
|
||||||
|
@spec new(opts()) :: t()
|
||||||
|
def new(opts) do
|
||||||
|
%__MODULE__{
|
||||||
|
opts: Keyword.take(opts, [:deadline, :"cpu-used", :crf])
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
50
lib/philomena_media/libavcodec/libx264.ex
Normal file
50
lib/philomena_media/libavcodec/libx264.ex
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
defmodule PhilomenaMedia.Libavcodec.Libx264 do
|
||||||
|
@moduledoc """
|
||||||
|
Represents the `libx264` (H.264) encoder, which takes a video input and generates encoded output.
|
||||||
|
|
||||||
|
## Example with all options
|
||||||
|
|
||||||
|
Libx264.new(
|
||||||
|
profile: :main,
|
||||||
|
preset: :medium,
|
||||||
|
crf: 18,
|
||||||
|
)
|
||||||
|
|
||||||
|
See https://ffmpeg.org/ffmpeg-codecs.html#libx264_002c-libx264rgb for more information about the options.
|
||||||
|
"""
|
||||||
|
|
||||||
|
@type profile :: :baseline | :main | :high
|
||||||
|
@type preset :: :slow | :medium
|
||||||
|
@type qrange :: 0..51
|
||||||
|
@type crf :: qrange()
|
||||||
|
|
||||||
|
@type opts :: [
|
||||||
|
profile: profile(),
|
||||||
|
preset: preset(),
|
||||||
|
crf: qrange()
|
||||||
|
]
|
||||||
|
|
||||||
|
@type t :: %__MODULE__{
|
||||||
|
name: String.t(),
|
||||||
|
opts: opts(),
|
||||||
|
force_format: :yuv420p,
|
||||||
|
type: :video
|
||||||
|
}
|
||||||
|
|
||||||
|
defstruct name: "libx264",
|
||||||
|
opts: [],
|
||||||
|
force_format: :yuv420p,
|
||||||
|
type: :video
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Construct a new libx264 (H.264) encoder.
|
||||||
|
|
||||||
|
See module documentation for usage.
|
||||||
|
"""
|
||||||
|
@spec new(opts()) :: t()
|
||||||
|
def new(opts) do
|
||||||
|
%__MODULE__{
|
||||||
|
opts: Keyword.take(opts, [:profile, :preset, :crf])
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
58
lib/philomena_media/libavfilter/endpoint.ex
Normal file
58
lib/philomena_media/libavfilter/endpoint.ex
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
defmodule PhilomenaMedia.Libavfilter.Endpoint do
|
||||||
|
@moduledoc """
|
||||||
|
Represents an endpoint vertex of the filter graph. Processing starts or stops here.
|
||||||
|
|
||||||
|
An endpoint which has no input but produces one output is a source. An endpoint which has one
|
||||||
|
input but produces no output is a sink.
|
||||||
|
|
||||||
|
See https://ffmpeg.org/ffmpeg-filters.html#Filtergraph-description for more information.
|
||||||
|
"""
|
||||||
|
|
||||||
|
@type index :: non_neg_integer()
|
||||||
|
@type pad_type :: PhilomenaMedia.Libavfilter.FilterNode.pad_type()
|
||||||
|
|
||||||
|
@type t :: %__MODULE__{
|
||||||
|
name: nil,
|
||||||
|
opts: [],
|
||||||
|
inputs: PhilomenaMedia.Libavfilter.FilterNode.pad_list(),
|
||||||
|
outputs: PhilomenaMedia.Libavfilter.FilterNode.pad_list(),
|
||||||
|
index: index()
|
||||||
|
}
|
||||||
|
|
||||||
|
@derive [PhilomenaMedia.Libavfilter.FilterNode]
|
||||||
|
defstruct name: nil,
|
||||||
|
opts: [],
|
||||||
|
inputs: [],
|
||||||
|
outputs: [],
|
||||||
|
index: 0
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Create a new source endpoint with the given pad type.
|
||||||
|
|
||||||
|
By default, this corresponds to stream index 0. Has one output pad with name `source`.
|
||||||
|
|
||||||
|
See the moduledoc for `m:PhilomenaMedia.Libavfilter.FilterGraph` for a usage example.
|
||||||
|
"""
|
||||||
|
@spec new_source(index(), pad_type()) :: t()
|
||||||
|
def new_source(index \\ 0, pad_type) do
|
||||||
|
%__MODULE__{
|
||||||
|
outputs: [source: pad_type],
|
||||||
|
index: index
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Create a new sink endpoint with the given pad type.
|
||||||
|
|
||||||
|
By default, this corresponds to stream index 0. Has one input pad with name `sink`.
|
||||||
|
|
||||||
|
See the moduledoc for `m:PhilomenaMedia.Libavfilter.FilterGraph` for a usage example.
|
||||||
|
"""
|
||||||
|
@spec new_sink(index(), pad_type()) :: t()
|
||||||
|
def new_sink(index \\ 0, pad_type) do
|
||||||
|
%__MODULE__{
|
||||||
|
inputs: [sink: pad_type],
|
||||||
|
index: index
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
194
lib/philomena_media/libavfilter/filter_graph.ex
Normal file
194
lib/philomena_media/libavfilter/filter_graph.ex
Normal file
|
@ -0,0 +1,194 @@
|
||||||
|
defmodule PhilomenaMedia.Libavfilter.FilterGraph do
|
||||||
|
@moduledoc """
|
||||||
|
Represents a complex filter graph.
|
||||||
|
|
||||||
|
## Example
|
||||||
|
|
||||||
|
{graph, [source, palettegen, paletteuse, sink]} =
|
||||||
|
FilterGraph.new([
|
||||||
|
Endpoint.new_source(:video),
|
||||||
|
Palettegen.new(stats_mode: :single),
|
||||||
|
Paletteuse.new(new: true),
|
||||||
|
Endpoint.new_sink(:video)
|
||||||
|
])
|
||||||
|
|
||||||
|
graph
|
||||||
|
|> FilterGraph.connect({source, :source}, {palettegen, :source})
|
||||||
|
|> FilterGraph.connect({source, :source}, {paletteuse, :source})
|
||||||
|
|> FilterGraph.connect({palettegen, :result}, {paletteuse, :palette})
|
||||||
|
|> FilterGraph.connect({paletteuse, :result}, {sink, :sink})
|
||||||
|
|
||||||
|
This creates the following conceptual graph:
|
||||||
|
|
||||||
|
source --> palettegen
|
||||||
|
| |
|
||||||
|
| |
|
||||||
|
| v
|
||||||
|
--------paletteuse --> sink
|
||||||
|
|
||||||
|
See https://ffmpeg.org/ffmpeg-filters.html#Filtergraph-description for more information.
|
||||||
|
"""
|
||||||
|
|
||||||
|
alias PhilomenaMedia.Libavfilter.FilterNode
|
||||||
|
alias PhilomenaMedia.Libavfilter.Endpoint
|
||||||
|
|
||||||
|
@type t :: %__MODULE__{
|
||||||
|
forward_adjacency: %{tagged_vertex() => MapSet.t()},
|
||||||
|
reverse_adjacency: %{tagged_vertex() => MapSet.t()},
|
||||||
|
vertices: %{integer() => struct()},
|
||||||
|
index: integer()
|
||||||
|
}
|
||||||
|
|
||||||
|
@type vertex :: integer()
|
||||||
|
@type pad_name :: FilterNode.pad_name()
|
||||||
|
@type pad :: {vertex(), pad_name()}
|
||||||
|
|
||||||
|
@type pad_index :: integer()
|
||||||
|
@type tagged_vertex :: {vertex(), pad_index()}
|
||||||
|
|
||||||
|
defstruct forward_adjacency: %{},
|
||||||
|
reverse_adjacency: %{},
|
||||||
|
vertices: %{},
|
||||||
|
index: 0
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Creates a new filtergraph instance with an optional list of vertices to add to the graph.
|
||||||
|
|
||||||
|
See the moduledoc for a full example.
|
||||||
|
"""
|
||||||
|
@spec new([struct()]) :: {t(), [vertex()]}
|
||||||
|
def new(nodes \\ []) do
|
||||||
|
add(%__MODULE__{}, nodes)
|
||||||
|
end
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Adds the specified list of vertices to the graph.
|
||||||
|
|
||||||
|
Returns the updated filtergraph structure and a list of vertices.
|
||||||
|
See the moduledoc for a full example.
|
||||||
|
"""
|
||||||
|
@spec add(t(), [struct()]) :: {t(), [vertex()]}
|
||||||
|
def add(g, nodes) do
|
||||||
|
{vertices, g} =
|
||||||
|
Enum.map_reduce(nodes, g, fn node, acc ->
|
||||||
|
{
|
||||||
|
acc.index,
|
||||||
|
%{acc | vertices: Map.put(acc.vertices, acc.index, node), index: acc.index + 1}
|
||||||
|
}
|
||||||
|
end)
|
||||||
|
|
||||||
|
{g, vertices}
|
||||||
|
end
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Connects two vertex references together with the given pad name.
|
||||||
|
|
||||||
|
Returns the updated filtergraph structure.
|
||||||
|
See the moduledoc for a full example.
|
||||||
|
"""
|
||||||
|
@spec connect(t(), pad(), pad()) :: t()
|
||||||
|
def connect(g, {output_vert, output_name}, {input_vert, input_name}) do
|
||||||
|
output_node = Map.fetch!(g.vertices, output_vert)
|
||||||
|
output_pad = {output_vert, pad_name_to_index!(FilterNode.outputs(output_node), output_name)}
|
||||||
|
|
||||||
|
input_node = Map.fetch!(g.vertices, input_vert)
|
||||||
|
input_pad = {input_vert, pad_name_to_index!(FilterNode.inputs(input_node), input_name)}
|
||||||
|
|
||||||
|
forward_adjacency =
|
||||||
|
Map.update(g.forward_adjacency, output_pad, MapSet.new([input_pad]), fn v ->
|
||||||
|
MapSet.put(v, input_pad)
|
||||||
|
end)
|
||||||
|
|
||||||
|
reverse_adjacency =
|
||||||
|
Map.update(g.reverse_adjacency, input_pad, MapSet.new([output_pad]), fn v ->
|
||||||
|
MapSet.put(v, output_pad)
|
||||||
|
end)
|
||||||
|
|
||||||
|
%{g | forward_adjacency: forward_adjacency, reverse_adjacency: reverse_adjacency}
|
||||||
|
end
|
||||||
|
|
||||||
|
@spec pad_name_to_index!(FilterNode.pad_list(), FilterNode.pad_name()) :: pad_index()
|
||||||
|
defp pad_name_to_index!(pads, pad_name) do
|
||||||
|
pads
|
||||||
|
|> Enum.find_index(fn {name, _type} -> name == pad_name end)
|
||||||
|
|> case do
|
||||||
|
nil ->
|
||||||
|
raise "Pad #{inspect(pad_name)} not found in list #{inspect(pads)}"
|
||||||
|
|
||||||
|
value ->
|
||||||
|
value
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Convert the filtergraph to a textual representation.
|
||||||
|
"""
|
||||||
|
@spec to_graph(t()) :: String.t()
|
||||||
|
def to_graph(g) do
|
||||||
|
g.vertices
|
||||||
|
|> Enum.map(fn
|
||||||
|
{_vert, %Endpoint{}} ->
|
||||||
|
[]
|
||||||
|
|
||||||
|
{vert, node} ->
|
||||||
|
[
|
||||||
|
incoming_pads!(g, vert),
|
||||||
|
FilterNode.name(node),
|
||||||
|
encode_opts!(node),
|
||||||
|
outgoing_pads!(g, vert),
|
||||||
|
";"
|
||||||
|
]
|
||||||
|
end)
|
||||||
|
|> IO.iodata_to_binary()
|
||||||
|
end
|
||||||
|
|
||||||
|
@spec incoming_pad!(t(), tagged_vertex()) :: iodata()
|
||||||
|
defp incoming_pad!(g, {target_vert, target_index}) do
|
||||||
|
[{source_vert, source_index}] =
|
||||||
|
g.reverse_adjacency
|
||||||
|
|> Map.fetch!({target_vert, target_index})
|
||||||
|
|> MapSet.to_list()
|
||||||
|
|
||||||
|
case g.vertices[source_vert] do
|
||||||
|
%_{index: index} ->
|
||||||
|
"[#{index}:v]"
|
||||||
|
|
||||||
|
_ ->
|
||||||
|
"[p#{source_vert}_#{source_index}:v]"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
@spec incoming_pads!(t(), vertex()) :: iodata()
|
||||||
|
defp incoming_pads!(g, target_vert) do
|
||||||
|
g.vertices
|
||||||
|
|> Map.fetch!(target_vert)
|
||||||
|
|> FilterNode.inputs()
|
||||||
|
|> Enum.with_index()
|
||||||
|
|> Enum.map(fn {_name, index} -> incoming_pad!(g, {target_vert, index}) end)
|
||||||
|
end
|
||||||
|
|
||||||
|
@spec outgoing_pad!(t(), tagged_vertex()) :: iodata()
|
||||||
|
defp outgoing_pad!(_g, {source_vert, source_index}) do
|
||||||
|
"[p#{source_vert}_#{source_index}:v]"
|
||||||
|
end
|
||||||
|
|
||||||
|
@spec outgoing_pads!(t(), vertex()) :: iodata()
|
||||||
|
defp outgoing_pads!(g, source_vert) do
|
||||||
|
g.vertices
|
||||||
|
|> Map.fetch!(source_vert)
|
||||||
|
|> FilterNode.outputs()
|
||||||
|
|> Enum.with_index()
|
||||||
|
|> Enum.map(fn {_name, index} -> outgoing_pad!(g, {source_vert, index}) end)
|
||||||
|
end
|
||||||
|
|
||||||
|
@spec encode_opts!(struct()) :: iodata()
|
||||||
|
defp encode_opts!(filter_node)
|
||||||
|
|
||||||
|
defp encode_opts!(%_{opts: opts}) when opts == %{} do
|
||||||
|
""
|
||||||
|
end
|
||||||
|
|
||||||
|
defp encode_opts!(%_{opts: opts}) do
|
||||||
|
["=", Enum.map_join(opts, ":", fn {k, v} -> "#{k}=#{v}" end)]
|
||||||
|
end
|
||||||
|
end
|
62
lib/philomena_media/libavfilter/filter_node.ex
Normal file
62
lib/philomena_media/libavfilter/filter_node.ex
Normal file
|
@ -0,0 +1,62 @@
|
||||||
|
defprotocol PhilomenaMedia.Libavfilter.FilterNode do
|
||||||
|
@doc """
|
||||||
|
The name of this filter, as a string, or nil if the filter is an endpoint.
|
||||||
|
"""
|
||||||
|
@spec name(t) :: String.t() | nil
|
||||||
|
def name(t)
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
A list of options passed to this filter, if any.
|
||||||
|
|
||||||
|
## Example
|
||||||
|
|
||||||
|
iex> FilterNode.opts(%Paletteuse{...})
|
||||||
|
[dither: :bayer, bayer_scale: 5]
|
||||||
|
|
||||||
|
"""
|
||||||
|
@spec opts(t) :: keyword()
|
||||||
|
def opts(t)
|
||||||
|
|
||||||
|
@type pad_name :: atom()
|
||||||
|
@type pad_type :: :video
|
||||||
|
@type pad_list :: [{pad_name(), pad_type()}]
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
A list of inputs passed to this filter, if any.
|
||||||
|
|
||||||
|
> #### Info {: .info}
|
||||||
|
>
|
||||||
|
> Inputs are given names for clarity, but libavfilter addresses inputs
|
||||||
|
> by *numeric index*, not by name, so this must not be placed into a map.
|
||||||
|
|
||||||
|
## Example
|
||||||
|
|
||||||
|
iex> FilterNode.inputs(%Paletteuse{...})
|
||||||
|
[source: :video, palette: :video]
|
||||||
|
"""
|
||||||
|
@spec inputs(t) :: pad_list()
|
||||||
|
def inputs(t)
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
A list of outputs generated by this filter, if any.
|
||||||
|
|
||||||
|
> #### Info {: .info}
|
||||||
|
>
|
||||||
|
> Outputs are given names for clarity, but libavfilter addresses outputs
|
||||||
|
> by *numeric index*, not by name, so this must not be placed into a map.
|
||||||
|
|
||||||
|
## Example
|
||||||
|
|
||||||
|
iex> FilterNode.outputs(%Scale{...})
|
||||||
|
[result: :video]
|
||||||
|
"""
|
||||||
|
@spec outputs(t) :: pad_list()
|
||||||
|
def outputs(t)
|
||||||
|
end
|
||||||
|
|
||||||
|
defimpl PhilomenaMedia.Libavfilter.FilterNode, for: Any do
|
||||||
|
def name(t), do: t.name
|
||||||
|
def opts(t), do: t.opts
|
||||||
|
def inputs(t), do: t.inputs
|
||||||
|
def outputs(t), do: t.outputs
|
||||||
|
end
|
58
lib/philomena_media/libavfilter/palettegen.ex
Normal file
58
lib/philomena_media/libavfilter/palettegen.ex
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
defmodule PhilomenaMedia.Libavfilter.Palettegen do
|
||||||
|
@moduledoc """
|
||||||
|
Represents the `palettegen` filter, which takes a video input and generates a video output.
|
||||||
|
|
||||||
|
The palette for each frame processed is generated using the median cut algorithm.
|
||||||
|
|
||||||
|
Has one input pad with name `source`. Has one output pad with name `result`.
|
||||||
|
|
||||||
|
## Example with all options
|
||||||
|
|
||||||
|
Palettegen.new(
|
||||||
|
max_colors: 255,
|
||||||
|
reserve_transparent: true,
|
||||||
|
stats_mode: :diff
|
||||||
|
)
|
||||||
|
|
||||||
|
See https://ffmpeg.org/ffmpeg-filters.html#palettegen for more information about the options.
|
||||||
|
"""
|
||||||
|
|
||||||
|
@type max_colors :: 0..256
|
||||||
|
@type reserve_transparent :: boolean()
|
||||||
|
@type stats_mode :: :full | :diff | :single
|
||||||
|
|
||||||
|
@type opts :: [
|
||||||
|
max_colors: max_colors(),
|
||||||
|
reserve_transparent: reserve_transparent(),
|
||||||
|
stats_mode: stats_mode()
|
||||||
|
]
|
||||||
|
|
||||||
|
@type t :: %__MODULE__{
|
||||||
|
name: String.t(),
|
||||||
|
opts: opts(),
|
||||||
|
inputs: PhilomenaMedia.Libavfilter.FilterNode.pad_list(),
|
||||||
|
outputs: PhilomenaMedia.Libavfilter.FilterNode.pad_list()
|
||||||
|
}
|
||||||
|
|
||||||
|
@derive [PhilomenaMedia.Libavfilter.FilterNode]
|
||||||
|
defstruct name: "palettegen",
|
||||||
|
opts: [],
|
||||||
|
inputs: [source: :video],
|
||||||
|
outputs: [result: :video]
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Construct a new palettegen filter.
|
||||||
|
|
||||||
|
See module documentation for usage.
|
||||||
|
"""
|
||||||
|
@spec new(opts()) :: t()
|
||||||
|
def new(opts) do
|
||||||
|
%__MODULE__{
|
||||||
|
opts: [
|
||||||
|
max_colors: Keyword.get(opts, :max_colors, 255),
|
||||||
|
reserve_transparent: Keyword.get(opts, :reserve_transparent, true),
|
||||||
|
stats_mode: Keyword.get(opts, :stats_mode, :diff)
|
||||||
|
]
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
67
lib/philomena_media/libavfilter/paletteuse.ex
Normal file
67
lib/philomena_media/libavfilter/paletteuse.ex
Normal file
|
@ -0,0 +1,67 @@
|
||||||
|
defmodule PhilomenaMedia.Libavfilter.Paletteuse do
|
||||||
|
@moduledoc """
|
||||||
|
Represents the `paletteuse` filter, which takes two video inputs and generates a video output.
|
||||||
|
|
||||||
|
The first input is the video stream, and the second input is the a 256-color palette.
|
||||||
|
Video colors are mapped onto the palette using a kd-tree quantization algorithm.
|
||||||
|
|
||||||
|
Has two input pads with names `source` and `palette`. Has one output pad with name `result`.
|
||||||
|
|
||||||
|
## Example with all options
|
||||||
|
|
||||||
|
Paletteuse.new(
|
||||||
|
dither: :bayer,
|
||||||
|
bayer_scale: 5,
|
||||||
|
diff_mode: :rectangle,
|
||||||
|
new: false,
|
||||||
|
alpha_threshold: 255
|
||||||
|
)
|
||||||
|
|
||||||
|
See https://ffmpeg.org/ffmpeg-filters.html#paletteuse for more information about the options.
|
||||||
|
"""
|
||||||
|
|
||||||
|
@type dither :: :bayer | :none
|
||||||
|
@type bayer_scale :: 0..5
|
||||||
|
@type diff_mode :: :none | :rectangle
|
||||||
|
@type new :: boolean()
|
||||||
|
@type alpha_threshold :: 0..255
|
||||||
|
|
||||||
|
@type opts :: [
|
||||||
|
dither: dither(),
|
||||||
|
bayer_scale: bayer_scale(),
|
||||||
|
diff_mode: diff_mode(),
|
||||||
|
new: new(),
|
||||||
|
alpha_threshold: alpha_threshold()
|
||||||
|
]
|
||||||
|
|
||||||
|
@type t :: %__MODULE__{
|
||||||
|
name: String.t(),
|
||||||
|
opts: opts(),
|
||||||
|
inputs: PhilomenaMedia.Libavfilter.FilterNode.pad_list(),
|
||||||
|
outputs: PhilomenaMedia.Libavfilter.FilterNode.pad_list()
|
||||||
|
}
|
||||||
|
|
||||||
|
@derive [PhilomenaMedia.Libavfilter.FilterNode]
|
||||||
|
defstruct name: "paletteuse",
|
||||||
|
opts: [],
|
||||||
|
inputs: [source: :video, palette: :video],
|
||||||
|
outputs: [result: :video]
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Construct a new paletteuse filter.
|
||||||
|
|
||||||
|
See module documentation for usage.
|
||||||
|
"""
|
||||||
|
@spec new(opts()) :: t()
|
||||||
|
def new(opts) do
|
||||||
|
%__MODULE__{
|
||||||
|
opts: [
|
||||||
|
dither: Keyword.get(opts, :dither, :bayer),
|
||||||
|
bayer_scale: Keyword.get(opts, :bayer_scale, 5),
|
||||||
|
diff_mode: Keyword.get(opts, :diff_mode, :rectangle),
|
||||||
|
new: Keyword.get(opts, :new, false),
|
||||||
|
alpha_threshold: Keyword.get(opts, :alpha_threshold, 255)
|
||||||
|
]
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
61
lib/philomena_media/libavfilter/scale.ex
Normal file
61
lib/philomena_media/libavfilter/scale.ex
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
defmodule PhilomenaMedia.Libavfilter.Scale do
|
||||||
|
@moduledoc """
|
||||||
|
Represents the `scale` filter, which takes a video input and generates a video output.
|
||||||
|
|
||||||
|
Has one input pad with name `source`. Has one output pad with name `result`.
|
||||||
|
|
||||||
|
## Example with all options
|
||||||
|
|
||||||
|
Scale.new(
|
||||||
|
width: 250,
|
||||||
|
height: 250,
|
||||||
|
force_original_aspect_ratio: :decrease,
|
||||||
|
force_divisible_by: 2
|
||||||
|
)
|
||||||
|
|
||||||
|
See https://ffmpeg.org/ffmpeg-filters.html#scale-1 for more information about the options.
|
||||||
|
"""
|
||||||
|
|
||||||
|
@type dimension :: integer()
|
||||||
|
@type width :: dimension()
|
||||||
|
@type height :: dimension()
|
||||||
|
@type force_original_aspect_ratio :: :disable | :decrease | :increase
|
||||||
|
@type force_divisible_by :: pos_integer()
|
||||||
|
|
||||||
|
@type opts :: [
|
||||||
|
width: width(),
|
||||||
|
height: height(),
|
||||||
|
force_original_aspect_ratio: force_original_aspect_ratio(),
|
||||||
|
force_divisible_by: force_divisible_by()
|
||||||
|
]
|
||||||
|
|
||||||
|
@type t :: %__MODULE__{
|
||||||
|
name: String.t(),
|
||||||
|
opts: opts(),
|
||||||
|
inputs: PhilomenaMedia.Libavfilter.FilterNode.pad_list(),
|
||||||
|
outputs: PhilomenaMedia.Libavfilter.FilterNode.pad_list()
|
||||||
|
}
|
||||||
|
|
||||||
|
@derive [PhilomenaMedia.Libavfilter.FilterNode]
|
||||||
|
defstruct name: "scale",
|
||||||
|
opts: [],
|
||||||
|
inputs: [source: :video],
|
||||||
|
outputs: [result: :video]
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Construct a new scale filter.
|
||||||
|
|
||||||
|
See module documentation for usage.
|
||||||
|
"""
|
||||||
|
@spec new(opts()) :: t()
|
||||||
|
def new(opts) do
|
||||||
|
%__MODULE__{
|
||||||
|
opts: [
|
||||||
|
width: Keyword.fetch!(opts, :width),
|
||||||
|
height: Keyword.fetch!(opts, :height),
|
||||||
|
force_original_aspect_ratio: Keyword.get(opts, :force_original_aspect_ratio, :disable),
|
||||||
|
force_divisible_by: Keyword.get(opts, :force_divisible_by, 1)
|
||||||
|
]
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
Loading…
Reference in a new issue