Skip to content

Commit 9d49f1d

Browse files
authored
fix stsz box, improve conditional fields handling (#119)
1 parent 2c439e6 commit 9d49f1d

File tree

4 files changed

+68
-60
lines changed

4 files changed

+68
-60
lines changed

lib/membrane_mp4/container.ex

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,10 @@ defmodule Membrane.MP4.Container do
1818
{:box, box_name_t}
1919
| {:field, field_name_t}
2020
| {:data, bitstring}
21-
| {:reason, :box_header | {:box_size, header: pos_integer, actual: pos_integer}}
21+
| {:reason,
22+
:box_header
23+
| {:box_size, header: pos_integer, actual: pos_integer}
24+
| {:non_empty_leftover, binary}}
2225
]
2326

2427
@type serialize_error_context_t :: [{:box, box_name_t} | {:field, field_name_t}]

lib/membrane_mp4/container/parse_helper.ex

Lines changed: 40 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,8 @@ defmodule Membrane.MP4.Container.ParseHelper do
2626
try:
2727
{:ok, {fields, rest}, context} <- parse_fields(content, box_schema.fields, context),
2828
try:
29-
{:ok, children, <<>>, context} <- parse_boxes(rest, box_schema.children, context, []) do
29+
{:ok, children, rest, context} <- parse_boxes(rest, box_schema.children, context, []),
30+
leftover: <<>> <- rest do
3031
box = %{fields: fields, children: children, size: content_size, header_size: header_size}
3132
parse_boxes(data, schema, context, [{name, box} | acc])
3233
else
@@ -38,19 +39,33 @@ defmodule Membrane.MP4.Container.ParseHelper do
3839
box = %{content: content, size: content_size, header_size: header_size}
3940
parse_boxes(data, schema, context, [{name, box} | acc])
4041

42+
leftover: leftover ->
43+
{:error, [box: name, reason: {:non_empty_leftover, leftover}]}
44+
4145
try: {:error, context} ->
4246
{:error, [box: name] ++ context}
4347
end
4448
end
4549

46-
defp parse_fields(data, [], context) do
47-
{:ok, {%{}, data}, context}
50+
defp parse_fields(data, fields, context) do
51+
do_parse_fields(data, fields, context, %{})
4852
end
4953

50-
defp parse_fields(data, [{name, type} | fields], context) do
51-
with {:ok, {term, rest}, context} <- parse_field(data, {name, type}, context),
52-
{:ok, {terms, rest}, context} <- parse_fields(rest, fields, context) do
53-
{:ok, {Map.put(terms, name, term), rest}, context}
54+
defp do_parse_fields(data, [], context, acc) do
55+
{:ok, {acc, data}, context}
56+
end
57+
58+
defp do_parse_fields(data, [{name, type} | fields], context, acc) do
59+
case parse_field(data, {name, type}, context) do
60+
{:ok, {term, rest}, context} ->
61+
acc = Map.put(acc, name, term)
62+
do_parse_fields(rest, fields, context, acc)
63+
64+
{:ok, :ignore, context} ->
65+
do_parse_fields(data, fields, context, acc)
66+
67+
{:error, context} ->
68+
{:error, context}
5469
end
5570
end
5671

@@ -63,27 +78,11 @@ defmodule Membrane.MP4.Container.ParseHelper do
6378
end
6479
end
6580

66-
defp parse_field(data, {name, {type, store: context_name, when: {key, [mask: mask]}}}, context) do
67-
context_object = Map.get(context, key, 0)
68-
69-
if (mask &&& context_object) == mask do
70-
parse_field(data, {name, {type, store: context_name}}, context)
71-
else
72-
{:ok, {[], data}, context}
73-
end
74-
end
75-
76-
defp parse_field(
77-
data,
78-
{name, {type, store: context_name, when: {key, [value: value]}}},
79-
context
80-
) do
81-
context_object = Map.get(context, key, 0)
82-
83-
if context_object == value do
81+
defp parse_field(data, {name, {type, store: context_name, when: when_clause}}, context) do
82+
if handle_when(when_clause, context) do
8483
parse_field(data, {name, {type, store: context_name}}, context)
8584
else
86-
{:ok, {[], data}, context}
85+
{:ok, :ignore, context}
8786
end
8887
end
8988

@@ -94,23 +93,11 @@ defmodule Membrane.MP4.Container.ParseHelper do
9493
{:ok, result, context}
9594
end
9695

97-
defp parse_field(data, {name, {type, when: {key, [mask: mask]}}}, context) do
98-
context_object = Map.get(context, key, 0)
99-
100-
if (mask &&& context_object) == mask do
101-
parse_field(data, {name, type}, context)
102-
else
103-
{:ok, {[], data}, context}
104-
end
105-
end
106-
107-
defp parse_field(data, {name, {type, when: {key, [value: value]}}}, context) do
108-
context_object = Map.get(context, key, 0)
109-
110-
if context_object == value do
96+
defp parse_field(data, {name, {type, when: when_clause}}, context) do
97+
if handle_when(when_clause, context) do
11198
parse_field(data, {name, type}, context)
11299
else
113-
{:ok, {[], data}, context}
100+
{:ok, :ignore, context}
114101
end
115102
end
116103

@@ -188,4 +175,16 @@ defmodule Membrane.MP4.Container.ParseHelper do
188175
defp parse_field_error(_data, name, context) do
189176
{:error, [field: name] ++ context}
190177
end
178+
179+
defp handle_when({key, condition}, context) do
180+
with {:ok, value} <- Map.fetch(context, key) do
181+
case condition do
182+
[value: cond_value] -> value == cond_value
183+
[mask: mask] -> (mask &&& value) == mask
184+
end
185+
else
186+
:error ->
187+
raise "MP4 schema field #{key} not found in context"
188+
end
189+
end
191190
end

lib/membrane_mp4/container/schema.ex

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -313,7 +313,7 @@ defmodule Membrane.MP4.Container.Schema do
313313
fields:
314314
@full_box ++
315315
[
316-
sample_size: :uint32,
316+
sample_size: {:uint32, store: :sample_size},
317317
sample_count: :uint32,
318318
entry_list: {
319319
{:list,

lib/membrane_mp4/movie_box/sample_table_box.ex

Lines changed: 23 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ defmodule Membrane.MP4.MovieBox.SampleTableBox do
1212
sample_deltas = assemble_sample_deltas(table)
1313
maybe_sample_sync = maybe_sample_sync(table)
1414
sample_to_chunk = assemble_sample_to_chunk(table)
15-
sample_sizes = assemble_sample_sizes(table)
1615
chunk_offsets = assemble_chunk_offsets(table)
1716

1817
[
@@ -47,15 +46,7 @@ defmodule Membrane.MP4.MovieBox.SampleTableBox do
4746
entry_list: sample_to_chunk
4847
}
4948
},
50-
stsz: %{
51-
fields: %{
52-
version: 0,
53-
flags: 0,
54-
sample_size: 0,
55-
sample_count: table.sample_count,
56-
entry_list: sample_sizes
57-
}
58-
},
49+
stsz: assemble_stsz(table),
5950
stco: %{
6051
fields: %{
6152
version: 0,
@@ -256,12 +247,27 @@ defmodule Membrane.MP4.MovieBox.SampleTableBox do
256247
}
257248
)
258249

259-
defp assemble_sample_sizes(%{sample_sizes: sample_sizes}),
260-
do: Enum.map(sample_sizes, &%{entry_size: &1})
261-
262250
defp assemble_chunk_offsets(%{chunk_offsets: chunk_offsets}),
263251
do: Enum.map(chunk_offsets, &%{chunk_offset: &1})
264252

253+
defp assemble_stsz(%{sample_sizes: sample_sizes, sample_count: sample_count}) do
254+
fields =
255+
if sample_sizes != [] and Enum.all?(sample_sizes) == hd(sample_sizes) do
256+
%{sample_size: hd(sample_sizes), entry_list: []}
257+
else
258+
%{sample_size: 0, entry_list: Enum.map(sample_sizes, &%{entry_size: &1})}
259+
end
260+
261+
%{
262+
fields:
263+
Map.merge(fields, %{
264+
version: 0,
265+
flags: 0,
266+
sample_count: sample_count
267+
})
268+
}
269+
end
270+
265271
@spec unpack(%{children: Container.t(), fields: map()}, timescale :: pos_integer()) ::
266272
SampleTable.t()
267273
def unpack(%{children: boxes}, timescale) do
@@ -293,12 +299,12 @@ defmodule Membrane.MP4.MovieBox.SampleTableBox do
293299
offsets |> Enum.map(fn %{chunk_offset: offset} -> offset end)
294300
end
295301

296-
defp unpack_sample_sizes(%{fields: %{entry_list: [], sample_count: 1, sample_size: sample_size}}) do
297-
[sample_size]
302+
defp unpack_sample_sizes(%{fields: %{sample_size: 0, entry_list: sizes}}) do
303+
Enum.map(sizes, fn %{entry_size: size} -> size end)
298304
end
299305

300-
defp unpack_sample_sizes(%{fields: %{entry_list: sizes}}) do
301-
sizes |> Enum.map(fn %{entry_size: size} -> size end)
306+
defp unpack_sample_sizes(%{fields: %{sample_size: sample_size, sample_count: sample_count}}) do
307+
Bunch.Enum.repeated(sample_size, sample_count)
302308
end
303309

304310
defp unpack_sample_description(%{children: [{avc, %{children: boxes, fields: fields}}]})

0 commit comments

Comments
 (0)