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

[MS-51] Use file seeking in Membrane.WAV.Serializer #10

Merged
merged 8 commits into from
Mar 18, 2022

Conversation

balins
Copy link
Member

@balins balins commented Mar 15, 2022

If Membrane.File.SeekEvent is available, Membrane.WAV.Serializer will emit event that updates invalid WAV header. Otherwise, Membrane.WAV.Serializer works like before.

@balins balins added the enhancement New feature or request label Mar 15, 2022
@balins balins requested a review from bblaszkow06 March 15, 2022 10:37
@balins balins self-assigned this Mar 15, 2022
@@ -101,5 +102,38 @@ defmodule Membrane.WAV.SerializerTest do
assert_sink_buffer(pid, :sink, %Buffer{payload: ^payload})
Pipeline.stop_and_terminate(pid, blocking?: true)
end

@tag :tmp_dir
test "create valid file when `update_header?` = `true`", %{tmp_dir: tmp_dir} do
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The markdown syntax doesn't make sense here as it will never go through any markdown processor:

Suggested change
test "create valid file when `update_header?` = `true`", %{tmp_dir: tmp_dir} do
test "create valid file when 'update_header?' is enabled", %{tmp_dir: tmp_dir} do

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It was rather an emphasis for the reader that this is some `variable`. But surely can be removed if we don't follow such convention.

Comment on lines +42 to +44
The Serializer creates header with invalid blocks which depend on the file size. You can bypass this
using `Membrane.File.Sink` to save the file or fixing the header afterwards with `Membrane.WAV.Postprocessing`
if you need to use any other sink.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This sounds like the element is broken and you can somehow work around the issue. I'd rephrase it to something mentioning, that if the element after serializer doesn't support seek events, you may work this around by disabling option in the element and using Postprocessing

@@ -32,6 +36,18 @@ defmodule Membrane.WAV.Serializer do
Used when converting demand from buffers into bytes.
""",
default: 2048
],
update_header?: [
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To better communicate that this is not something usually done, I'd rename it to disable_seeking or disable_header_update.


{:ok, state}
end

@impl true
def handle_caps(:input, caps, _context, state) do
buffer = %Buffer{payload: create_header(caps)}
state = %{state | header_created: true}

state = %{state | header_length: byte_size(buffer.payload)}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This might be the place to subtract the 8 bytes not included in header length

lib/membrane_wav/serializer.ex Outdated Show resolved Hide resolved
lib/membrane_wav/serializer.ex Outdated Show resolved Hide resolved
lib/membrane_wav/serializer.ex Outdated Show resolved Hide resolved
Comment on lines 166 to 172
require Membrane.Logger

Membrane.Logger.warn("""
Couldn't update WAV header as `Membrane.File.SeekEvent` module is not available (reason: #{inspect(reason)}).
You can use `Membrane.File.Sink` in your pipeline to correctly save the file or fix it later using
`Membrane.WAV.Postprocessing`.
""")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should not go just with a warning that can be missed. In particular, the file plugin is now :test only and will "disappear" if running in MIX_ENV=dev. This scenario (enabled update and no 'seek' event is available) should raise with an error suggesting to disable the option and informing about consequences (i.e. the file won't have a valid header).

mix.exs Outdated
@@ -36,7 +36,7 @@ defmodule Membrane.WAV.Plugin.Mixfile do
{:ex_doc, "~> 0.26", only: :dev, runtime: false},
{:dialyxir, "~> 1.1", only: :dev, runtime: false},
{:credo, "~> 1.6", only: :dev, runtime: false},
{:membrane_file_plugin, "~> 0.7.0", only: :test},
{:membrane_file_plugin, "~> 0.8.0", only: :test},
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If it's not available in 'dev', you won't be able to test it from iex. We can solve it by making the plugin an optional: true dependency

Comment on lines 152 to 176
defp maybe_update_header_actions(%{header_length: header_length, data_length: data_length}) do
case Code.ensure_compiled(Membrane.File.SeekEvent) do
{:module, seek_event} ->
# subtracting 8 bytes as `file_length` field doesn't include "RIFF" header and the field itself
file_length = header_length + data_length - 8

[
event: {:output, struct!(seek_event, position: @file_length_offset)},
buffer: {:output, %Buffer{payload: <<file_length::32-little>>}},
event: {:output, struct!(seek_event, position: @data_length_offset)},
buffer: {:output, %Buffer{payload: <<data_length::32-little>>}}
]

{:error, reason} ->
require Membrane.Logger

Membrane.Logger.warn("""
Couldn't update WAV header as `Membrane.File.SeekEvent` module is not available (reason: #{inspect(reason)}).
You can use `Membrane.File.Sink` in your pipeline to correctly save the file or fix it later using
`Membrane.WAV.Postprocessing`.
""")

[]
end
end
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is invoked from an app, so ensure_loaded? should be enough. Also, if the check was moved outside, you could use the proper struct creation syntax and have compile-time check for the keys:

Suggested change
defp maybe_update_header_actions(%{header_length: header_length, data_length: data_length}) do
case Code.ensure_compiled(Membrane.File.SeekEvent) do
{:module, seek_event} ->
# subtracting 8 bytes as `file_length` field doesn't include "RIFF" header and the field itself
file_length = header_length + data_length - 8
[
event: {:output, struct!(seek_event, position: @file_length_offset)},
buffer: {:output, %Buffer{payload: <<file_length::32-little>>}},
event: {:output, struct!(seek_event, position: @data_length_offset)},
buffer: {:output, %Buffer{payload: <<data_length::32-little>>}}
]
{:error, reason} ->
require Membrane.Logger
Membrane.Logger.warn("""
Couldn't update WAV header as `Membrane.File.SeekEvent` module is not available (reason: #{inspect(reason)}).
You can use `Membrane.File.Sink` in your pipeline to correctly save the file or fix it later using
`Membrane.WAV.Postprocessing`.
""")
[]
end
end
if Code.ensure_loaded?(Membrane.File.SeekEvent) do
defp maybe_update_header_actions(%{
update_header?: true,
header_length: header_length,
data_length: data_length
}) do
# subtracting 8 bytes as `file_length` field doesn't include "RIFF" header and the field itself
file_length = header_length + data_length - 8
[
event: {:output, %Membrane.File.SeekEvent{position: @file_length_offset}},
buffer: {:output, %Buffer{payload: <<file_length::32-little>>}},
event: {:output, %Membrane.File.SeekEvent{position: @data_length_offset}},
buffer: {:output, %Buffer{payload: <<data_length::32-little>>}}
]
end
else
defp maybe_update_header_actions(%{update_header?: true}) do
# Suppress warnings
_offset = @file_length_offset
_offset = @data_length_offset
raise "Unable to update WAV file header using seek events. [...]"
end
end

This can be verified by replacing position with positions (assuming file plugin is still a test-only dependency) will result in compilation error for test env and clean compilation on dev

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Surely that's way better!

balins and others added 2 commits March 17, 2022 08:39
Co-authored-by: Bartosz Błaszków <bbartosz06@gmail.com>
@balins balins requested a review from bblaszkow06 March 17, 2022 09:31
Copy link
Member

@bblaszkow06 bblaszkow06 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cool

test/serializer/serializer_test.exs Show resolved Hide resolved
@balins balins merged commit 7dde7cd into master Mar 18, 2022
@balins balins deleted the MS-51-wav-plugin-use-seeking-in-file-sink branch March 18, 2022 08:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants