Packages
membrane_h264_ffmpeg_plugin
0.7.0
0.32.7
0.32.6
0.32.5
0.32.4
0.32.3
0.32.2
0.32.1
0.32.0
0.31.8
0.31.7
0.31.6
0.31.5
0.31.4
0.31.3
0.31.2
0.31.1
0.31.0
0.30.1
0.30.0
0.29.0
0.28.2
0.28.1
0.28.0
0.27.0
0.26.2
0.26.1
0.26.0
0.25.4
0.25.3
0.25.2
0.25.1
0.25.0
0.24.0
0.23.0
0.22.1
0.22.0
0.21.6
0.21.5
0.21.4
0.21.3
0.21.2
0.21.1
0.21.0
0.20.1
0.20.0
0.19.0
0.18.0
0.17.0
0.16.3
0.16.2
0.16.1
0.16.0
0.15.0
0.14.0
0.13.3
0.13.2
0.13.1
0.13.0
0.12.2
0.12.1
0.12.0
0.11.0
0.10.0
0.9.0
0.8.0
0.7.0
0.6.0
0.5.0
Membrane H264 decoder and encoder based on FFmpeg and x264
Current section
Files
Jump to
Current section
Files
lib/membrane_h264_ffmpeg/encoder.ex
defmodule Membrane.H264.FFmpeg.Encoder do
@moduledoc """
Membrane element that encodes raw video frames to H264 format.
The element expects each frame to be received in a separate buffer, so the parser
(`Membrane.Element.RawVideo.Parser`) may be required in a pipeline before
the encoder (e.g. when input is read from `Membrane.File.Source`).
Additionaly, the encoder has to receive proper caps with picture format and dimensions
before any encoding takes place.
Please check `t:t/0` for available options.
"""
use Membrane.Filter
alias __MODULE__.Native
alias Membrane.Buffer
alias Membrane.Caps.Video.{H264, Raw}
use Bunch
use Bunch.Typespec
def_input_pad :input,
demand_unit: :buffers,
caps: {Raw, format: one_of([:I420, :I422]), aligned: true}
def_output_pad :output,
caps: {H264, stream_format: :byte_stream, alignment: :au}
@default_crf 23
@list_type presets :: [
:ultrafast,
:superfast,
:veryfast,
:faster,
:fast,
:medium,
:slow,
:slower,
:veryslow,
:placebo
]
def_options crf: [
description: """
Constant rate factor that affects the quality of output stream.
Value of 0 is lossless compression while 51 (for 8-bit samples)
or 63 (10-bit) offers the worst quality.
The range is exponential, so increasing the CRF value +6 results
in roughly half the bitrate / file size, while -6 leads
to roughly twice the bitrate.
""",
type: :int,
default: @default_crf
],
preset: [
description: """
Collection of predefined options providing certain encoding.
The slower the preset choosen, the higher compression for the
same quality can be achieved.
""",
type: :atom,
spec: presets(),
default: :medium
],
profile: [
description: """
Defines the features that will have to be supported by decoder
to decode video encoded with this element.
""",
type: :atom,
spec: H264.profile_t(),
default: :high
]
@impl true
def handle_init(opts) do
{:ok, opts |> Map.merge(%{encoder_ref: nil})}
end
@impl true
def handle_demand(:output, _size, :buffers, _ctx, %{encoder_ref: nil} = state) do
# Wait until we have an encoder
{:ok, state}
end
def handle_demand(:output, size, :buffers, _ctx, state) do
{{:ok, demand: {:input, size}}, state}
end
@impl true
def handle_process(:input, %Buffer{payload: payload}, ctx, state) do
%{encoder_ref: encoder_ref} = state
with {:ok, frames} <- Native.encode(payload, encoder_ref) do
bufs = wrap_frames(frames)
in_caps = ctx.pads.input.caps
caps =
{:output,
%H264{
alignment: :au,
framerate: in_caps.framerate,
height: in_caps.height,
width: in_caps.width,
profile: state.profile,
stream_format: :byte_stream
}}
# redemand is needed until the internal buffer of encoder is filled (no buffers will be
# generated before that) but it is a noop if the demand has been fulfilled
actions = [{:caps, caps} | bufs] ++ [redemand: :output]
{{:ok, actions}, state}
else
{:error, reason} -> {{:error, reason}, state}
end
end
@impl true
def handle_caps(:input, %Raw{} = caps, _ctx, state) do
{framerate_num, framerate_denom} = caps.framerate
with {:ok, encoder_ref} <-
Native.create(
caps.width,
caps.height,
caps.format,
state.preset,
state.profile,
framerate_num,
framerate_denom,
state.crf
) do
{{:ok, redemand: :output}, %{state | encoder_ref: encoder_ref}}
else
{:error, reason} -> {{:error, reason}, state}
end
end
@impl true
def handle_end_of_stream(:input, _ctx, state) do
with {:ok, frames} <- Native.flush(state.encoder_ref),
bufs <- wrap_frames(frames) do
actions = bufs ++ [end_of_stream: :output, notify: {:end_of_stream, :input}]
{{:ok, actions}, state}
else
{:error, reason} -> {{:error, reason}, state}
end
end
@impl true
def handle_prepared_to_stopped(_ctx, state) do
{:ok, %{state | encoder_ref: nil}}
end
defp wrap_frames([]), do: []
defp wrap_frames(frames) do
frames |> Enum.map(fn frame -> %Buffer{payload: frame} end) ~> [buffer: {:output, &1}]
end
end