# `Boombox`
[🔗](https://github.com/membraneframework/boombox/blob/v0.2.10/lib/boombox.ex#L1)

Boombox is a tool for audio and video streaming.

See `run/1` for details and [examples.livemd](examples.livemd) for examples.

# `boombox_server`

```elixir
@opaque boombox_server()
```

# `common_rtp_opt`

```elixir
@type common_rtp_opt() ::
  {:video_encoding, Membrane.RTP.encoding_name()}
  | {:video_payload_type, Membrane.RTP.payload_type()}
  | {:video_clock_rate, Membrane.RTP.clock_rate()}
  | {:audio_encoding, Membrane.RTP.encoding_name()}
  | {:audio_payload_type, Membrane.RTP.payload_type()}
  | {:audio_clock_rate, Membrane.RTP.clock_rate()}
  | {:aac_bitrate_mode, Membrane.RTP.AAC.Utils.mode()}
```

When configuring a track for a media type (video or audio), the following options are used:
  * <media_type>_encoding - MUST be provided to configure given media type. Some options are encoding-specific. Currently supported encodings are: AAC, Opus, H264, H265.
  * <media_type>_payload_type, <media_type>_clock rate - MAY be provided. If not, an unofficial default will be used.
The following encoding-specific parameters are available for both RTP input and output:
  * aac_bitrate_mode - MUST be provided for AAC encoding. Defines which mode should be assumed/set when depayloading/payloading.

# `elixir_input`

```elixir
@type elixir_input() :: {:stream | :writer | :message, in_raw_data_opts()}
```

# `elixir_output`

```elixir
@type elixir_output() :: {:stream | :reader | :message, out_raw_data_opts()}
```

# `hls_mode_opt`

```elixir
@type hls_mode_opt() :: {:mode, :live | :vod}
```

# `hls_variant_selection_policy_opt`

```elixir
@type hls_variant_selection_policy_opt() ::
  {:variant_selection_policy,
   Membrane.HTTPAdaptiveStream.Source.variant_selection_policy()}
```

# `in_raw_data_opts`

```elixir
@type in_raw_data_opts() :: [
  audio: :binary | boolean(),
  video: :image | boolean(),
  is_live: boolean()
]
```

# `in_rtp_opts`

```elixir
@type in_rtp_opts() :: [
  common_rtp_opt()
  | {:port, :inet.port_number()}
  | {:audio_specific_config, binary()}
  | {:vps, binary()}
  | {:pps, binary()}
  | {:sps, binary()}
]
```

In order to configure a RTP input a receiving port MUST be provided and the media that will be received
MUST be configured. Media configuration is explained further in `t:common_rtp_opt/0`.

The following encoding-specific parameters are available for RTP input:
  * audio_specific_config - MUST be provided for AAC encoding. Contains crucial information about the stream and has to be obtained from a side channel.
  * vps (H265 only), pps, sps - MAY be provided for H264 or H265 encodings. Parameter sets, could be obtained from a side channel. They contain information about the encoded stream.

# `input`

```elixir
@type input() ::
  (path_or_uri :: String.t())
  | {path_or_uri :: String.t(),
     [hls_variant_selection_policy_opt()]
     | [{:framerate, Membrane.H264.framerate() | Membrane.H265.framerate_t()}]}
  | {:mp4 | :aac | :wav | :mp3 | :ivf | :ogg | :h264 | :h265,
     location :: String.t()}
  | {:mp4 | :aac | :wav | :mp3 | :ivf | :ogg, location :: String.t(),
     [{:transport, :file | :http}]}
  | {:h264, location :: String.t(),
     transport: :file | :http, framerate: Membrane.H264.framerate()}
  | {:h265, location :: String.t(),
     transport: :file | :http, framerate: Membrane.H265.framerate_t()}
  | {:webrtc, webrtc_signaling()}
  | {:whip, uri :: String.t(), [{:token, String.t()}]}
  | {:rtmp, (uri :: String.t()) | (client_handler :: pid())}
  | {:rtsp, url :: String.t()}
  | {:rtp, in_rtp_opts()}
  | {:hls, url :: String.t()}
  | {:hls, url :: String.t(), [hls_variant_selection_policy_opt()]}
  | {:srt, url :: String.t()}
  | {:srt, url :: String.t(), srt_auth_opts()}
  | {:srt, server_awaiting_accept :: ExLibSRT.Server.t()}
```

# `out_raw_data_opts`

```elixir
@type out_raw_data_opts() :: [
  {:audio, :binary | boolean()}
  | {:video, :image | boolean()}
  | {:audio_format, Membrane.RawAudio.SampleFormat.t()}
  | {:audio_rate, Membrane.RawAudio.sample_rate_t()}
  | {:audio_channels, Membrane.RawAudio.channels_t()}
  | {:video_width, non_neg_integer()}
  | {:video_height, non_neg_integer()}
  | pace_control_opt()
]
```

# `out_rtp_opts`

```elixir
@type out_rtp_opts() :: [
  common_rtp_opt()
  | {:address, :inet.ip_address() | String.t()}
  | {:port, :inet.port_number()}
  | {:target, String.t()}
  | transcoding_policy_opt()
]
```

In order to configure a RTP output the target port and address MUST be provided (can be provided in `:target` option as a `<address>:<port>` string)
and the media that will be sent MUST be configured. Media configuration is explained further in `t:common_rtp_opt/0`.

# `output`

```elixir
@type output() ::
  (path_or_uri :: String.t())
  | {path_or_uri :: String.t(), [transcoding_policy_opt() | hls_mode_opt()]}
  | {:mp4 | :aac | :wav | :mp3 | :ivf | :ogg | :h264 | :h265,
     location :: String.t()}
  | {:mp4 | :aac | :wav | :mp3 | :ivf | :ogg | :h264 | :h265,
     location :: String.t(), [transcoding_policy_opt()]}
  | {:webrtc, webrtc_signaling()}
  | {:webrtc, webrtc_signaling(), [transcoding_policy_opt()]}
  | {:whip, uri :: String.t(),
     [
       {:token, String.t()}
       | {bandit_option :: atom(), term()}
       | transcoding_policy_opt()
     ]}
  | {:hls, location :: String.t()}
  | {:hls, location :: String.t(), [hls_mode_opt() | transcoding_policy_opt()]}
  | {:rtp, out_rtp_opts()}
  | {:srt, url :: String.t()}
  | {:srt, url :: String.t(), srt_auth_opts()}
  | :player
```

# `pace_control_opt`

```elixir
@type pace_control_opt() :: {:pace_control, boolean()}
```

If true the incoming streams will be passed to the output according to their
timestamps, if not they will be passed as fast as possible. True by default.

# `srt_auth_opts`

```elixir
@type srt_auth_opts() :: [stream_id: String.t(), password: String.t()]
```

# `transcoding_policy_opt`

```elixir
@type transcoding_policy_opt() :: {:transcoding_policy, :always | :if_needed | :never}
```

# `webrtc_signaling`

```elixir
@type webrtc_signaling() :: Membrane.WebRTC.Signaling.t() | String.t()
```

# `async`

```elixir
@spec async(Enumerable.t() | nil, input: input(), output: output()) ::
  Task.t() | Enumerable.t()
```

Asynchronous version of `run/2`.

Doesn't block the calling process until the termination of the processing.

It returns a `Task.t()` that can be awaited later.

If the output is a `:stream`, `:reader` or `:message` endpoint, or the input
is a `:writer` or `:message` endpoint, the behaviour is identical to `run/2`.

# `child_spec`

```elixir
@spec child_spec(
  [input: input(), output: output()]
  | [Enumerable.t() | [input: {:stream, in_raw_data_opts()}, output: output()]]
) :: Supervisor.child_spec()
```

Returns a child specification for running Boombox under a supervisor.

Boombox processes are one-shot: they process media and exit normally when
done. The `restart: :temporary` configuration means they will not be
restarted after finishing.

## Example

    children = [
      {Boombox, input: "rtmp://localhost:5432", output: "output.mp4"}
    ]

    Supervisor.start_link(children, strategy: :one_for_one)

## Example with :stream input
    audio_packets_stream = ...

    children = [
      {Boombox, [audio_packets_stream, input: {:stream, audio: :binary, video: false} output: "output.mp4"]}
    ]

    Supervisor.start_link(children, strategy: :one_for_one)

# `close`

```elixir
@spec close(Boombox.Writer.t() | Boombox.Reader.t()) ::
  :ok | {:error, :incompatible_mode | :already_finished}
```

Gracefully terminates Boombox when using `:reader` or `:writer` endpoints before a response
of type `:finished` has been received.

When using `:reader` endpoint on output informs Boombox that no more packets will be read
from it with `read/1` and that it should terminate accordingly.

When using `:writer` endpoint on input informs Boombox that it will not be provided
any more packets with `write/2` and should terminate accordingly.

# `play`

```elixir
@spec play(Enumerable.t() | nil, input() | elixir_input()) :: :ok
```

Runs boombox with given input and plays audio and video streams on your computer.

`Boombox.play(input)` is idiomatic to `Boombox.run(input: input, output: :player)`.

## Example

```
Boombox.play("rtmp://localhost:5432")
```

# `read`

```elixir
@spec read(Boombox.Reader.t()) ::
  {:ok, Boombox.Packet.t()} | :finished | {:error, :incompatible_mode}
```

Reads a packet from Boombox.

If returned with `:ok`, then this function can be called again to request the
next packet, and if returned with `:finished`, then Boombox finished it's
operation and will not produce any more packets.

Can be called only when using `:reader` endpoint on output.

# `run`

```elixir
@spec run(Enumerable.t() | nil,
  input: input() | elixir_input(),
  output: output() | elixir_output()
) ::
  :ok | Enumerable.t() | Boombox.Writer.t() | Boombox.Reader.t() | pid()
```

Runs boombox with given input and output.

## Example

```
Boombox.run(input: "rtmp://localhost:5432", output: "index.m3u8")

Boombox.run(
  input: "path/to/file.mp4",
  output: {:webrtc, "ws://0.0.0.0:1234"}
)
```

See `t:input/0` and `t:output/0` for available inputs and outputs and
[examples.livemd](examples.livemd) for examples.

Calling this function results in it blocking until the media flow is finished and
then returning `:ok`, except for when an endpoint with special behavior is used.

Input endpoints with special behaviours:
  * `:stream` - a `Stream` or other `Enumerable` containing `Boombox.Packet`s is expected as the first argument.
  * `:writer` - this function will return a `Boombox.Writer` struct, which is used to
  write media packets to boombox with `write/2` and to finish writing with `close/1`.
  * `:message` - this function returns a PID of a process to communicate with. The process accepts
  the following types of messages:
    - `{:boombox_packet, packet :: Boombox.Packet.t()}` - provides boombox
    with a media packet. The process will a `{:boombox_finished, boombox_pid :: pid()}` message to
    `sender_pid` if it has finished processing packets and should not be provided any more.
    - `:boombox_close` - tells boombox that no more packets will be provided and that it should terminate.

Output endpoints with special behaviours:
  * `:stream` - this function will return a `Stream` that contains `Boombox.Packet`s
  * `:reader` - this function will return a `Boombox.Reader` struct, which is used to read media packets from
  boombox with `read/1` and to stop reading with `close/1`.
  * `:message` - this function returns a PID of a process to communicate with. The process will
  send the following types of messages to the process that called this function:
    - `{:boombox_packet, boombox_pid :: pid(), packet :: Boombox.Packet.t()}` - contains a packet
    produced by boombox.
    - `{:boombox_finished, boombox_pid :: pid()}` - informs that boombox has finished producing
    packets and will begin terminating. No more messages will be sent.

# `run_cli`

```elixir
@spec run_cli([String.t()]) :: :ok
```

Runs boombox with CLI arguments.

## Example
```
# boombox.exs
Mix.install([:boombox])
Boombox.run_cli()
```

```sh
elixir boombox.exs -i "rtmp://localhost:5432" -o "index.m3u8"
```

# `start_link`

```elixir
@spec start_link(Enumerable.t() | nil,
  input: input() | {:stream, in_raw_data_opts()},
  output: output()
) ::
  {:ok, pid()} | {:error, term()}
```

Starts Boombox and links it to the calling process.

This function is suitable for supervision tree integration — see `child_spec/1`. Otherwise,
use `run/1` or `async/1`.

Returns `{:ok, pid}` once Boombox is ready to process media.
For protocols that require a server socket (RTMP, RTSP, RTP, SRT), this
function blocks until the server is bound and ready to accept connections.
For all other protocols it returns immediately after the pipeline starts.

The returned `pid` is the pipeline supervisor. You can monitor it to detect
when processing finishes or if Boombox crashes.

`:message` and `:reader` endpoints are not supported as input, and `:stream`, `:message`
and `:writer` endpoints are not supported as output — always use `run/1` or `async/1` for those.

# `write`

```elixir
@spec write(Boombox.Writer.t(), Boombox.Packet.t()) ::
  :ok | :finished | {:error, :incompatible_mode}
```

Writes provided packet to Boombox.

Returns `:ok` if more packets can be provided, and
`:finished` when Boombox finished consuming and will not accept any more packets. Returns
synchronously once the packet has been ingested and Boombox is ready for more packets.

Can be called only when using `:writer` endpoint on input.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
