Current section

Files

Jump to
xav lib video_converter.ex
Raw

lib/video_converter.ex

defmodule Xav.VideoConverter do
@moduledoc """
Video samples converter.
It supports pixel format conversion and/or scaling.
"""
alias Xav.Frame
alias Xav.VideoConverter.NIF
@type t :: %__MODULE__{
converter: reference(),
out_format: Frame.video_format(),
out_width: Frame.width(),
out_height: Frame.height()
}
@typedoc """
Type definition for converter options.
* `out_format` - video format to convert to (`e.g. :rgb24`).
* `out_width` - scale the video frame to this width.
* `out_height` - scale the video frame to this height.
If `out_width` and `out_height` are both not provided, scaling is not performed. If one of the
dimensions is `nil`, the other will be calculated based on the input dimensions as
to keep the aspect ratio.
"""
@type converter_opts() :: [
out_format: Frame.video_format(),
out_width: Frame.width(),
out_height: Frame.height()
]
defstruct [:converter, :out_format, :out_width, :out_height]
@doc """
Creates a new video converter.
"""
@spec new(converter_opts()) :: t()
def new(converter_opts) do
opts = Keyword.validate!(converter_opts, [:out_format, :out_width, :out_height])
if is_nil(opts[:out_format]) and is_nil(opts[:out_width]) and is_nil(opts[:out_height]) do
raise "At least one of `out_format`, `out_width` or `out_height` must be provided"
end
:ok = validate_converter_options(opts)
converter = NIF.new(opts[:out_format], opts[:out_width] || -1, opts[:out_height] || -1)
%__MODULE__{
converter: converter,
out_format: opts[:out_format],
out_width: opts[:out_width],
out_height: opts[:out_height]
}
end
@doc """
Converts a video frame.
"""
@spec convert(t(), Frame.t()) :: Frame.t()
def convert(
%__MODULE__{out_format: format, out_width: nil, out_height: nil},
%Frame{format: format} = frame
),
do: frame
def convert(%__MODULE__{converter: converter}, frame) do
{data, out_format, width, height, _pts} =
NIF.convert(converter, frame.data, frame.width, frame.height, frame.format)
%Frame{
type: frame.type,
data: data,
format: out_format,
width: width,
height: height,
pts: frame.pts
}
end
defp validate_converter_options([]), do: :ok
defp validate_converter_options([{_key, nil} | opts]) do
validate_converter_options(opts)
end
defp validate_converter_options([{key, value} | _opts])
when key in [:out_width, :out_height] and not is_integer(value) do
raise %ArgumentError{
message: "Expected an integer value for #{inspect(key)}, received: #{inspect(value)}"
}
end
defp validate_converter_options([{key, value} | _opts])
when key in [:out_width, :out_height] and value < 1 do
raise %ArgumentError{
message: "Invalid value for #{inspect(key)}, expected a value to be >= 1"
}
end
defp validate_converter_options([{_key, _value} | opts]) do
validate_converter_options(opts)
end
end