-
Notifications
You must be signed in to change notification settings - Fork 2
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
Conversation
test/serializer/serializer_test.exs
Outdated
@@ -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 |
There was a problem hiding this comment.
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:
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 |
There was a problem hiding this comment.
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.
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. |
There was a problem hiding this comment.
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
lib/membrane_wav/serializer.ex
Outdated
@@ -32,6 +36,18 @@ defmodule Membrane.WAV.Serializer do | |||
Used when converting demand from buffers into bytes. | |||
""", | |||
default: 2048 | |||
], | |||
update_header?: [ |
There was a problem hiding this comment.
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
.
lib/membrane_wav/serializer.ex
Outdated
|
||
{: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)} |
There was a problem hiding this comment.
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
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`. | ||
""") |
There was a problem hiding this comment.
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}, |
There was a problem hiding this comment.
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
lib/membrane_wav/serializer.ex
Outdated
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 |
There was a problem hiding this comment.
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:
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
There was a problem hiding this comment.
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!
Co-authored-by: Bartosz Błaszków <bbartosz06@gmail.com>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If
Membrane.File.SeekEvent
is available,Membrane.WAV.Serializer
will emit event that updates invalid WAV header. Otherwise,Membrane.WAV.Serializer
works like before.