Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for version1 boxes. #109

Merged
merged 4 commits into from
Apr 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ The package can be installed by adding `membrane_mp4_plugin` to your list of dep
```elixir
defp deps do
[
{:membrane_mp4_plugin, "~> 0.34.1"}
{:membrane_mp4_plugin, "~> 0.34.2"}
]
end
```
Expand Down
1 change: 1 addition & 0 deletions lib/membrane_mp4/container/header.ex
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ defmodule Membrane.MP4.Container.Header do

The `content_size` field is equal to the box size minus the size of the header (8 bytes).
"""
use Bunch.Access

@enforce_keys [:name, :content_size, :header_size]

Expand Down
41 changes: 32 additions & 9 deletions lib/membrane_mp4/container/parse_helper.ex
Original file line number Diff line number Diff line change
Expand Up @@ -18,23 +18,24 @@ defmodule Membrane.MP4.Container.ParseHelper do

def parse_boxes(data, schema, context, acc) do
withl header_content:
{:ok, %{name: name, content_size: content_size}, rest} <- Header.parse(data),
{:ok, %{name: name, content_size: content_size, header_size: header_size}, rest} <-
Header.parse(data),
header_content: <<content::binary-size(content_size), data::binary>> <- rest,
do: box_schema = schema[name],
known?: true <- box_schema && not box_schema.black_box?,
try:
{:ok, {fields, rest}, context} <- parse_fields(content, box_schema.fields, context),
try:
{:ok, children, <<>>, context} <- parse_boxes(rest, box_schema.children, context, []) do
box = %{fields: fields, children: children}
box = %{fields: fields, children: children, size: content_size, header_size: header_size}
parse_boxes(data, schema, context, [{name, box} | acc])
else
header_content: _error ->
# more data needed
{:ok, Enum.reverse(acc), data, context}

known?: _ ->
box = %{content: content}
box = %{content: content, size: content_size, header_size: header_size}
parse_boxes(data, schema, context, [{name, box} | acc])

try: {:error, context} ->
Expand Down Expand Up @@ -62,11 +63,24 @@ defmodule Membrane.MP4.Container.ParseHelper do
end
end

defp parse_field(data, {name, {type, store: context_name, when: condition}}, context) do
{flag, key} = condition
defp parse_field(data, {name, {type, store: context_name, when: {key, [mask: mask]}}}, context) do
context_object = Map.get(context, key, 0)

if (flag &&& context_object) == flag do
if (mask &&& context_object) == mask do
parse_field(data, {name, {type, store: context_name}}, context)
else
{:ok, {[], data}, context}
end
end

defp parse_field(
data,
{name, {type, store: context_name, when: {key, [value: value]}}},
context
) do
context_object = Map.get(context, key, 0)

if context_object == value do
parse_field(data, {name, {type, store: context_name}}, context)
else
{:ok, {[], data}, context}
Expand All @@ -80,11 +94,20 @@ defmodule Membrane.MP4.Container.ParseHelper do
{:ok, result, context}
end

defp parse_field(data, {name, {type, when: condition}}, context) do
{flag, key} = condition
defp parse_field(data, {name, {type, when: {key, [mask: mask]}}}, context) do
context_object = Map.get(context, key, 0)

if (mask &&& context_object) == mask do
parse_field(data, {name, type}, context)
else
{:ok, {[], data}, context}
end
end

defp parse_field(data, {name, {type, when: {key, [value: value]}}}, context) do
context_object = Map.get(context, key, 0)

if (flag &&& context_object) == flag do
if context_object == value do
parse_field(data, {name, type}, context)
else
{:ok, {[], data}, context}
Expand Down
47 changes: 35 additions & 12 deletions lib/membrane_mp4/container/schema.ex
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ defmodule Membrane.MP4.Container.Schema do
"""

@full_box [
version: :uint8,
version: {:uint8, store: :version},
flags: {:uint24, store: :fo_flags}
]

Expand Down Expand Up @@ -70,10 +70,13 @@ defmodule Membrane.MP4.Container.Schema do
fields:
@full_box ++
[
creation_time: :uint32,
modification_time: :uint32,
creation_time: {:uint32, when: {:version, value: 0}},
creation_time: {:uint64, when: {:version, value: 1}},
modification_time: {:uint32, when: {:version, value: 0}},
modification_time: {:uint64, when: {:version, value: 1}},
timescale: :uint32,
duration: :uint32,
duration: {:uint32, when: {:version, value: 0}},
duration: {:uint64, when: {:version, value: 1}},
rate: :fp16d16,
volume: :fp8d8,
reserved: <<0::size(80)>>,
Expand Down Expand Up @@ -101,11 +104,14 @@ defmodule Membrane.MP4.Container.Schema do
fields:
@full_box ++
[
creation_time: :uint32,
modification_time: :uint32,
creation_time: {:uint32, when: {:version, value: 0}},
creation_time: {:uint64, when: {:version, value: 1}},
modification_time: {:uint32, when: {:version, value: 0}},
modification_time: {:uint64, when: {:version, value: 1}},
track_id: :uint32,
reserved: <<0::32>>,
duration: :uint32,
duration: {:uint32, when: {:version, value: 0}},
duration: {:uint64, when: {:version, value: 1}},
reserved: <<0::64>>,
layer: :int16,
alternate_group: :int16,
Expand All @@ -130,10 +136,13 @@ defmodule Membrane.MP4.Container.Schema do
fields:
@full_box ++
[
creation_time: :uint32,
modification_time: :uint32,
creation_time: {:uint32, when: {:version, value: 0}},
creation_time: {:uint64, when: {:version, value: 1}},
modification_time: {:uint32, when: {:version, value: 0}},
modification_time: {:uint64, when: {:version, value: 1}},
timescale: :uint32,
duration: :uint32,
duration: {:uint32, when: {:version, value: 0}},
duration: {:uint64, when: {:version, value: 1}},
reserved: <<0::1>>,
language: :uint15,
reserved: <<0::16>>
Expand Down Expand Up @@ -280,7 +289,7 @@ defmodule Membrane.MP4.Container.Schema do
{:list,
[
sample_count: :uint32,
sample_offset: :uint32
sample_composition_offset: :uint32
]}
]
],
Expand Down Expand Up @@ -325,6 +334,19 @@ defmodule Membrane.MP4.Container.Schema do
chunk_offset: :uint32
]}
]
],
co64: [
version: 0,
fields:
@full_box ++
[
entry_count: :uint32,
entry_list:
{:list,
[
chunk_offset: :uint64
]}
]
]
]
]
Expand Down Expand Up @@ -426,7 +448,8 @@ defmodule Membrane.MP4.Container.Schema do
sample_duration: :uint32,
sample_size: :uint32,
sample_flags: :bin32,
sample_offset: {:uint32, when: {0x800, :fo_flags}}
sample_composition_offset:
{:uint32, when: {:fo_flags, mask: 0x800}}
]}
]
]
Expand Down
8 changes: 4 additions & 4 deletions lib/membrane_mp4/container/schema_parser.ex
Original file line number Diff line number Diff line change
Expand Up @@ -62,10 +62,10 @@ defmodule Membrane.MP4.Container.Schema.Parser do
{name, type}
end

defp parse_field({name, {type, store: context_name, when: {flag, context_name}}})
defp parse_field({name, {type, store: context_name, when: {context_name, opts}}})
when is_atom(name) do
{name, type} = parse_field({name, type})
type = {type, store: context_name, when: {flag, context_name}}
type = {type, store: context_name, when: {context_name, opts}}
{name, type}
end

Expand All @@ -75,9 +75,9 @@ defmodule Membrane.MP4.Container.Schema.Parser do
{name, type}
end

defp parse_field({name, {type, when: {flag, context_name}}}) when is_atom(name) do
defp parse_field({name, {type, when: {context_name, opts}}}) when is_atom(name) do
{name, type} = parse_field({name, type})
type = {type, when: {flag, context_name}}
type = {type, when: {context_name, opts}}
{name, type}
end

Expand Down
30 changes: 24 additions & 6 deletions lib/membrane_mp4/container/serialize_helper.ex
Original file line number Diff line number Diff line change
Expand Up @@ -64,11 +64,20 @@ defmodule Membrane.MP4.Container.SerializeHelper do
end
end

defp serialize_field(term, {type, store: context_name, when: condition}, context) do
{flag, key} = condition
defp serialize_field(term, {type, store: context_name, when: {key, [mask: mask]}}, context) do
context_object = Map.get(context, key)

if context_object != nil and (flag &&& context_object) == flag do
if context_object != nil and (mask &&& context_object) == mask do
serialize_field(term, {type, store: context_name}, context)
else
{{:ok, <<>>}, context}
end
end

defp serialize_field(term, {type, store: context_name, when: {key, [value: value]}}, context) do
context_object = Map.get(context, key)

if context_object != nil and context_object == value do
serialize_field(term, {type, store: context_name}, context)
else
{{:ok, <<>>}, context}
Expand All @@ -80,11 +89,20 @@ defmodule Membrane.MP4.Container.SerializeHelper do
serialize_field(term, type, context)
end

defp serialize_field(term, {type, when: condition}, context) do
{flag, key} = condition
defp serialize_field(term, {type, when: {key, [mask: mask]}}, context) do
context_object = Map.get(context, key, 0)

if (mask &&& context_object) == mask do
serialize_field(term, type, context)
else
{{:ok, <<>>}, context}
end
end

defp serialize_field(term, {type, when: {key, [value: value]}}, context) do
context_object = Map.get(context, key, 0)

if (flag &&& context_object) == flag do
if context_object == value do
serialize_field(term, type, context)
else
{{:ok, <<>>}, context}
Expand Down
Loading