Skip to content

Commit 07ccb03

Browse files
committed
wip guide
1 parent 181eaa5 commit 07ccb03

1 file changed

Lines changed: 76 additions & 45 deletions

File tree

guides/useful_concepts/manual_demands.md

Lines changed: 76 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Manual demands
22

3-
Elements with `:manual` flow control pads have two responsibilities:
3+
Elements with pads using manual flow control have two responsibilities:
44

55
- **Output pads**: produce exactly the amount of data requested in the
66
[`handle_demand/5`](`c:Membrane.Element.WithOutputPads.handle_demand/5`)
@@ -12,9 +12,9 @@ Elements with `:manual` flow control pads have two responsibilities:
1212
## Output pads and `handle_demand`
1313

1414
When downstream requests data, Membrane invokes `handle_demand/5` on the
15-
element whose output pad is connected to that downstream. The callback receives
16-
the pad name, the demanded amount, and the demand unit. The element is expected
17-
to produce and send that amount of data.
15+
element whose output pad with manual flow control is connected to that
16+
downstream. The callback receives the pad name, the demanded amount, and the
17+
demand unit. The element is expected to produce and send that amount of data.
1818

1919
The unit in which `demand_size` is expressed is resolved as follows:
2020

@@ -79,9 +79,9 @@ downstream.
7979

8080
## Input pads and the demand action
8181

82-
To receive data on a `:manual` input pad, the element must issue a
83-
[`:demand`](`t:Membrane.Element.Action.demand/0`) action. The demand unit
84-
is declared in the input pad spec.
82+
To receive data on an input pad with manual flow control, the element must
83+
issue a [`:demand`](`t:Membrane.Element.Action.demand/0`) action. The demand
84+
unit is declared in the input pad spec.
8585

8686
### `:buffers` and `:bytes`
8787

@@ -141,6 +141,10 @@ timestamps as the original buffer.
141141

142142
#### Declarative nature of demands
143143

144+
This section applies to `:buffers` and `:bytes` demand units. Timestamp demand
145+
units behave differently — see the [timestamp section](#timestamp-demand-units)
146+
below.
147+
144148
The `:demand` action **overwrites** the current demand — it does not add to it.
145149
Issuing `demand: {:input, 5}` sets the demand to 5 regardless of how much
146150
demand was already pending.
@@ -179,37 +183,57 @@ Timestamp demand units let an element request buffers by specifying a
179183
all buffers up to and including the first one whose timestamp meets or exceeds
180184
the demanded value.
181185

182-
Timestamp demand units are only applicable to **input pads**. Output pads do
183-
not support them. If an input pad uses a timestamp demand unit and the linked
184-
upstream element's output pad does not specify a `demand_unit`, that element
185-
will receive `handle_demand/5` with demand expressed in `:buffers`.
186+
Timestamp demand units are only applicable to **input pads with manual flow
187+
control**. Output pads do not support them. If an input pad uses a timestamp
188+
demand unit and the linked upstream output pad does not specify a `demand_unit`,
189+
that element will receive `handle_demand/5` with demand expressed in `:buffers`.
186190

187191
#### Available variants
188192

189-
- `{:timestamp, :pts}` — demand is expressed in terms of each buffer's PTS.
190-
- `{:timestamp, :dts}` — demand is expressed in terms of each buffer's DTS.
191-
- `:timestamp` or `{:timestamp, :dts_or_pts}` — uses DTS if present on a
192-
buffer, falls back to PTS.
193+
- `{:timestamp, :pts}` — uses each buffer's `:pts` field.
194+
- `{:timestamp, :dts}` — uses each buffer's `:dts` field.
195+
- `:timestamp` or `{:timestamp, :dts_or_pts}` — uses `buffer.dts || buffer.pts`.
196+
197+
#### Timestamp requirements
198+
199+
All buffers passing through an input pad with a timestamp demand unit must have
200+
their relevant timestamp field set to a non-`nil` value — Membrane will raise
201+
an error if a buffer is missing its timestamp. Additionally, timestamps must be
202+
**monotonically non-decreasing**; non-monotonic timestamps will cause a warning.
203+
204+
For `{:timestamp, :pts}`, note that PTS can be non-monotonic in streams with
205+
B-frames (see the [timestamps guide](timestamps.md)). Prefer
206+
`{:timestamp, :dts}` when DTS is available.
193207

194208
#### How timestamp demands work
195209

196-
The demand value is a `t:Membrane.Time.t/0` duration (in nanoseconds),
197-
interpreted relative to an **offset** — the timestamp of the very first buffer
198-
ever received on the pad. You never need to know the actual starting timestamp;
199-
all demand values are expressed as durations from that origin.
210+
The demand value is a `t:Membrane.Time.t/0` value. Use `Membrane.Time`
211+
functions to construct it, for example `Membrane.Time.seconds(1)`.
212+
213+
Timestamps are interpreted relative to an **offset** — the timestamp of the
214+
very first buffer ever received on the pad. This means you never need to know
215+
the absolute starting timestamp of the stream; you only work with durations
216+
from that origin.
217+
218+
For example, if the first buffer arrives with `pts: Membrane.Time.seconds(100)`
219+
and you issue `demand: {:input, Membrane.Time.seconds(1)}`, Membrane will
220+
deliver buffers until it reaches the first one with
221+
`pts >= Membrane.Time.seconds(100) + Membrane.Time.seconds(1)`, i.e.
222+
`pts >= Membrane.Time.seconds(101)`.
200223

201224
When you issue `demand: {:input, t}`, Membrane delivers all buffered buffers
202225
whose timestamp (minus the offset) is strictly less than `t`, plus the first
203226
buffer whose timestamp (minus the offset) equals or exceeds `t`. This means
204-
that **if you demand a value greater than the last received buffer's timestamp,
205-
you are guaranteed to receive at least one new buffer**.
227+
that **if you demand a value strictly greater than the last received buffer's
228+
timestamp (minus the offset), you are guaranteed to receive at least one new
229+
buffer**.
206230

207231
#### Incrementing demands
208232

209233
To receive a continuous stream in time-based chunks, always demand a value
210-
larger than the timestamp of the last received buffer. Issuing a demand at or
211-
below the last received timestamp will result in a warning and no buffers being
212-
delivered.
234+
larger than the timestamp (minus the offset) of the last received buffer.
235+
Issuing a demand at or below that value will result in a warning and no buffers
236+
being delivered.
213237

214238
#### Example: a sink consuming one second of data per second
215239

@@ -250,26 +274,19 @@ Each timer tick advances the demanded threshold by one second: `1s`, `2s`,
250274
on `:input`, so each demand naturally covers the next one-second slice of the
251275
stream.
252276

253-
> #### Buffers must carry timestamps {: .warning}
254-
>
255-
> Every buffer must have its relevant timestamp field (`:pts` or `:dts`) set to
256-
> a non-`nil` value. Missing timestamps will cause incorrect behavior.
277+
## Filters with manual flow control
257278

258-
> #### Prefer DTS for encoded video {: .warning}
259-
>
260-
> PTS can be non-monotonic in streams with B-frames. Prefer
261-
> `{:timestamp, :dts}` when DTS is available and monotonic, to avoid warnings.
262-
263-
## Filters: manual input and output
264-
265-
A filter with both a manual input and a manual output is the most common case
266-
for manual flow control. The canonical pattern is:
279+
The canonical pattern for a filter with both pads using manual flow control is:
267280

268281
- **`handle_demand`** — propagate the demand upstream by returning a `:demand`
269282
action on the input pad.
270283
- **`handle_buffer`** — process the incoming buffer and forward it (possibly
271284
modified) downstream via a `:buffer` action.
272285

286+
The following example doubles each buffer's payload, so one input buffer
287+
produces one output buffer. To satisfy a demand of `n` output buffers, the
288+
filter demands `n` input buffers.
289+
273290
```elixir
274291
defmodule MyFilter do
275292
use Membrane.Filter
@@ -285,21 +302,35 @@ defmodule MyFilter do
285302

286303
@impl true
287304
def handle_demand(:output, demand_size, :buffers, _ctx, state) do
288-
{[demand: {:input, demand_size}], state}
305+
# Each output buffer requires 2 input buffers to produce
306+
{[demand: {:input, demand_size * 2}], state}
289307
end
290308

291309
@impl true
292310
def handle_buffer(:input, buffer, _ctx, state) do
293-
processed = %{buffer | payload: process(buffer.payload)}
294-
{[buffer: {:output, processed}], state}
311+
# Combine every two input buffers into one output buffer
312+
buffers = [buffer | state.pending]
313+
314+
case buffers do
315+
[second, first] ->
316+
output = %Membrane.Buffer{payload: first.payload <> second.payload}
317+
{[buffer: {:output, output}], %{state | pending: []}}
318+
319+
[_one] ->
320+
{[], %{state | pending: buffers}}
321+
end
295322
end
296323

297-
defp process(payload), do: payload
324+
@impl true
325+
def handle_init(_ctx, _opts) do
326+
{[], %{pending: []}}
327+
end
298328
end
299329
```
300330

301-
> #### Do not use redemand in a filter's `handle_demand` {: .warning}
331+
> #### Do not use redemand in `handle_demand` of a pad with manual flow control that has a corresponding input pad {: .warning}
302332
>
303-
> Returning `:redemand` from `handle_demand` in a filter is illegal.
304-
> `handle_demand` should propagate the demand to the input pad and return.
305-
> Processing happens in `handle_buffer` when the data actually arrives.
333+
> Returning `:redemand` from `handle_demand` is illegal when the element also
334+
> has an input pad it relies on for data. `handle_demand` should propagate the
335+
> demand upstream and return. Processing happens in `handle_buffer` when the
336+
> data actually arrives.

0 commit comments

Comments
 (0)