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

Change caps #9

Closed
wants to merge 16 commits into from
6 changes: 5 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
## 0.4.0
### Release notes:
* Use new buffer dts field instead of metadata.timestamp
* Allow vp8/vp9 input caps #9
## 0.3.0
### Release notes:
* fix lint and bump membrane_core
* fix lint and bump membrane_core
## 0.2.0
### Release notes:
* serialization and deserialization of IVF files #4
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ The package can be installed by adding `membrane_ivf_plugin` to your list of dep
```elixir
def deps do
[
{:membrane_ivf_plugin, "~> 0.3.0"}
{:membrane_ivf_plugin, "~> 0.4.0"}
]
end
```
Expand Down
3 changes: 3 additions & 0 deletions config/config.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import Config

import_config("#{config_env()}.exs")
3 changes: 3 additions & 0 deletions config/dev.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import Config

config :logger, level: :debug
3 changes: 3 additions & 0 deletions config/prod.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import Config

config :logger, level: :warn
3 changes: 3 additions & 0 deletions config/test.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import Config

config :logger, level: :debug
36 changes: 19 additions & 17 deletions lib/deserializer.ex
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,15 @@ defmodule Membrane.Element.IVF.Deserializer do
use Membrane.Filter
use Ratio

alias Membrane.{Time, RemoteStream, Buffer}
alias Membrane.{Time, Buffer}
alias Membrane.{VP9, VP8}
alias Membrane.Element.IVF.Headers
alias Membrane.Element.IVF.Headers.FrameHeader

def_input_pad :input, caps: :any, demand_unit: :buffers

def_output_pad :output,
caps: {RemoteStream, content_format: one_of([VP9, VP8]), type: :packetized}
caps: [VP9, VP8]

defmodule State do
@moduledoc false
Expand Down Expand Up @@ -45,8 +45,17 @@ defmodule Membrane.Element.IVF.Deserializer do
{:ok, buffer, rest} <- get_buffer(rest, file_header.scale <|> file_header.rate) do
caps =
case file_header.four_cc do
"VP90" -> %Membrane.RemoteStream{content_format: VP9, type: :packetized}
"VP80" -> %Membrane.RemoteStream{content_format: VP8, type: :packetized}
"VP90" ->
%VP9{
width: file_header.width,
height: file_header.height
}

"VP80" ->
%VP8{
width: file_header.width,
height: file_header.height
}
end

{{:ok, caps: {:output, caps}, buffer: {:output, buffer}, redemand: :output},
Expand All @@ -56,7 +65,7 @@ defmodule Membrane.Element.IVF.Deserializer do
timebase: file_header.scale <|> file_header.rate
}}
else
{:error_too_short, _payload} ->
:error_too_short ->
{{:ok, redemand: :output}, state}

_error ->
Expand All @@ -67,22 +76,15 @@ defmodule Membrane.Element.IVF.Deserializer do
def handle_process(:input, buffer, _ctx, state) do
state = %State{state | frame_acc: state.frame_acc <> buffer.payload}

case flush_acc(state, []) do
{:ok, buffers, state} ->
{{:ok, buffer: {:output, buffers}, redemand: :output}, state}
{:ok, buffers, state} = flush_acc(state, [])

{:error_too_short, payload} ->
{{:ok, redemand: :output}, %State{state | frame_acc: payload}}

error ->
error
end
{{:ok, buffer: {:output, buffers}, redemand: :output}, state}
end

defp flush_acc(state, buffers) do
case get_buffer(state.frame_acc, state.timebase) do
{:ok, buffer, rest} -> flush_acc(%State{state | frame_acc: rest}, [buffer | buffers])
_error -> {:ok, buffers |> Enum.reverse(), state}
_error -> {:ok, Enum.reverse(buffers), state}
end
end

Expand All @@ -91,9 +93,9 @@ defmodule Membrane.Element.IVF.Deserializer do
Headers.parse_ivf_frame_header(payload),
<<frame::binary-size(size_of_frame), rest::binary()>> <- rest do
timestamp = timestamp * (timebase * Time.second())
{:ok, %Buffer{metadata: %{timestamp: timestamp}, payload: frame}, rest}
{:ok, %Buffer{dts: timestamp, payload: frame}, rest}
else
_error -> {:error_too_short, payload}
_error -> :error_too_short
end
end
end
2 changes: 2 additions & 0 deletions lib/membrane_element_ivf/headers.ex
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,9 @@ defmodule Membrane.Element.IVF.Headers do
codec_four_cc =
case caps do
%Membrane.RemoteStream{content_format: VP9} -> "VP90"
%VP9{} -> "VP90"
%Membrane.RemoteStream{content_format: VP8} -> "VP80"
%VP8{} -> "VP80"
_unknown -> "\0\0\0\0"
end

Expand Down
56 changes: 41 additions & 15 deletions lib/serializer.ex
Original file line number Diff line number Diff line change
Expand Up @@ -4,27 +4,32 @@ defmodule Membrane.Element.IVF.Serializer do
"""

use Membrane.Filter
use Membrane.Log

alias Membrane.Element.IVF
alias Membrane.{Buffer, RemoteStream}
alias Membrane.{VP9, VP8}

def_options width: [spec: [integer], description: "width of frame"],
height: [spec: [integer], description: "height of frame"],
require Membrane.Logger

def_options width: [spec: [integer], default: 0, description: "width of frame"],
height: [spec: [integer], default: 0, description: "height of frame"],
scale: [spec: [integer], default: 1, description: "scale"],
rate: [spec: [integer], default: 1_000_000, description: "rate"],
frame_count: [spec: [integer], default: 0, description: "number of frames"]

def_input_pad :input,
caps: {RemoteStream, content_format: one_of([VP9, VP8]), type: :packetized},
caps: [
VP8,
VP9,
{RemoteStream, content_format: one_of([VP8, VP9]), type: :packetized}
],
demand_unit: :buffers

def_output_pad :output, caps: :any

defmodule State do
@moduledoc false
defstruct [:width, :height, :timebase, :first_frame, :frame_count]
defstruct [:width, :height, :scale, :rate, :timebase, :frame_count, first_frame: true]
end

@impl true
Expand All @@ -41,29 +46,50 @@ defmodule Membrane.Element.IVF.Serializer do
}}
end

@impl true
def handle_caps(:input, %VP8{} = caps, _ctx, state) do
{:ok, %State{state | height: caps.height, width: caps.width}}
end

@impl true
def handle_caps(:input, %VP9{} = caps, _ctx, state) do
{:ok, %State{state | height: caps.height, width: caps.width}}
end

@impl true
def handle_caps(_pad, _caps, _ctx, state) do
{:ok, state}
end

@impl true
def handle_demand(:output, size, :buffers, _ctx, state) do
{{:ok, demand: {:input, size}}, state}
end

@impl true
def handle_process(:input, buffer, ctx, state) do
%Buffer{payload: frame, metadata: %{timestamp: timestamp}} = buffer
%Buffer{payload: frame, dts: timestamp} = buffer

ivf_frame =
IVF.Headers.create_ivf_frame_header(byte_size(frame), timestamp, state.timebase) <>
frame

ivf_file_header =
if state.first_frame,
do:
IVF.Headers.create_ivf_header(
state.width,
state.height,
state.timebase,
state.frame_count,
ctx.pads.input.caps
)
if state.first_frame do
if state.width == 0 or state.height == 0,
do:
Membrane.Logger.warn(
"Serializing stream to IVF without width or height. These parameters can be passed via options or caps"
)

IVF.Headers.create_ivf_header(
state.width,
state.height,
state.timebase,
state.frame_count,
ctx.pads.input.caps
)
end

ivf_buffer = (ivf_file_header || "") <> ivf_frame

Expand Down
10 changes: 7 additions & 3 deletions mix.exs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
defmodule Membrane.IVF.Plugin.MixProject do
use Mix.Project

@version "0.3.0"
@version "0.4.0"
@github_url "https://github.com/membraneframework/membrane_ivf_plugin"

def project do
Expand All @@ -27,7 +27,7 @@ defmodule Membrane.IVF.Plugin.MixProject do

def application do
[
extra_applications: []
extra_applications: [:logger]
]
end

Expand All @@ -36,7 +36,11 @@ defmodule Membrane.IVF.Plugin.MixProject do

defp deps do
[
{:membrane_core, "~> 0.8.0"},
{:membrane_core, "~> 0.9.0", override: true},
{:membrane_vp8_format,
github: "membraneframework/membrane_vp8_format", branch: "add-stream-params"},
{:membrane_vp9_format,
github: "membraneframework/membrane_vp9_format", branch: "add-stream-params"},
{:ex_doc, "~> 0.24", only: :dev, runtime: false},
{:dialyxir, "~> 1.1.0", only: :dev, runtime: false},
{:credo, "~> 1.4", only: :dev, runtime: false},
Expand Down
16 changes: 9 additions & 7 deletions mix.lock
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,24 @@
"bunch": {:hex, :bunch, "1.3.0", "51b4423088b7fb9e21eae6d6bc5e5d219d955ea5556fbd6130bfb6213df4be32", [:mix], [], "hexpm", "9ad233a2bacc0dae8aa6553a9b9057f27446443b1c5903c3479b6f9f3820ce2d"},
"bunt": {:hex, :bunt, "0.2.0", "951c6e801e8b1d2cbe58ebbd3e616a869061ddadcc4863d0a2182541acae9a38", [:mix], [], "hexpm", "7af5c7e09fe1d40f76c8e4f9dd2be7cebd83909f31fee7cd0e9eadc567da8353"},
"coerce": {:hex, :coerce, "1.0.1", "211c27386315dc2894ac11bc1f413a0e38505d808153367bd5c6e75a4003d096", [:mix], [], "hexpm", "b44a691700f7a1a15b4b7e2ff1fa30bebd669929ac8aa43cffe9e2f8bf051cf1"},
"credo": {:hex, :credo, "1.6.0", "2400b2d1f821d32f08bbb7ddf56ec6395daa5ca7f5d2d50c240354f57ac785b1", [:mix], [{:bunt, "~> 0.2.0", [hex: :bunt, repo: "hexpm", optional: false]}, {:file_system, "~> 0.2.8", [hex: :file_system, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "8a8bd206c426e836cdadb81eee8c703c7a8f1114013dba886358a012957012c3"},
"credo": {:hex, :credo, "1.6.3", "0a9f8925dbc8f940031b789f4623fc9a0eea99d3eed600fe831e403eb96c6a83", [:mix], [{:bunt, "~> 0.2.0", [hex: :bunt, repo: "hexpm", optional: false]}, {:file_system, "~> 0.2.8", [hex: :file_system, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "1167cde00e6661d740fc54da2ee268e35d3982f027399b64d3e2e83af57a1180"},
"dialyxir": {:hex, :dialyxir, "1.1.0", "c5aab0d6e71e5522e77beff7ba9e08f8e02bad90dfbeffae60eaf0cb47e29488", [:mix], [{:erlex, ">= 0.2.6", [hex: :erlex, repo: "hexpm", optional: false]}], "hexpm", "07ea8e49c45f15264ebe6d5b93799d4dd56a44036cf42d0ad9c960bc266c0b9a"},
"earmark_parser": {:hex, :earmark_parser, "1.4.17", "6f3c7e94170377ba45241d394389e800fb15adc5de51d0a3cd52ae766aafd63f", [:mix], [], "hexpm", "f93ac89c9feca61c165b264b5837bf82344d13bebc634cd575cb711e2e342023"},
"earmark_parser": {:hex, :earmark_parser, "1.4.19", "de0d033d5ff9fc396a24eadc2fcf2afa3d120841eb3f1004d138cbf9273210e8", [:mix], [], "hexpm", "527ab6630b5c75c3a3960b75844c314ec305c76d9899bb30f71cb85952a9dc45"},
"erlex": {:hex, :erlex, "0.2.6", "c7987d15e899c7a2f34f5420d2a2ea0d659682c06ac607572df55a43753aa12e", [:mix], [], "hexpm", "2ed2e25711feb44d52b17d2780eabf998452f6efda104877a3881c2f8c0c0c75"},
"ex_doc": {:hex, :ex_doc, "0.25.5", "ac3c5425a80b4b7c4dfecdf51fa9c23a44877124dd8ca34ee45ff608b1c6deb9", [:mix], [{:earmark_parser, "~> 1.4.0", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.14", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1", [hex: :makeup_erlang, repo: "hexpm", optional: false]}], "hexpm", "688cfa538cdc146bc4291607764a7f1fcfa4cce8009ecd62de03b27197528350"},
"ex_doc": {:hex, :ex_doc, "0.28.0", "7eaf526dd8c80ae8c04d52ac8801594426ae322b52a6156cd038f30bafa8226f", [:mix], [{:earmark_parser, "~> 1.4.19", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.14", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1", [hex: :makeup_erlang, repo: "hexpm", optional: false]}], "hexpm", "e55cdadf69a5d1f4cfd8477122ebac5e1fadd433a8c1022dafc5025e48db0131"},
"file_system": {:hex, :file_system, "0.2.10", "fb082005a9cd1711c05b5248710f8826b02d7d1784e7c3451f9c1231d4fc162d", [:mix], [], "hexpm", "41195edbfb562a593726eda3b3e8b103a309b733ad25f3d642ba49696bf715dc"},
"jason": {:hex, :jason, "1.2.2", "ba43e3f2709fd1aa1dce90aaabfd039d000469c05c56f0b8e31978e03fa39052", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "18a228f5f0058ee183f29f9eae0805c6e59d61c3b006760668d8d18ff0d12179"},
"jason": {:hex, :jason, "1.3.0", "fa6b82a934feb176263ad2df0dbd91bf633d4a46ebfdffea0c8ae82953714946", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "53fc1f51255390e0ec7e50f9cb41e751c260d065dcba2bf0d08dc51a4002c2ac"},
"makeup": {:hex, :makeup, "1.0.5", "d5a830bc42c9800ce07dd97fa94669dfb93d3bf5fcf6ea7a0c67b2e0e4a7f26c", [:mix], [{:nimble_parsec, "~> 0.5 or ~> 1.0", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "cfa158c02d3f5c0c665d0af11512fed3fba0144cf1aadee0f2ce17747fba2ca9"},
"makeup_elixir": {:hex, :makeup_elixir, "0.15.2", "dc72dfe17eb240552857465cc00cce390960d9a0c055c4ccd38b70629227e97c", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.1", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "fd23ae48d09b32eff49d4ced2b43c9f086d402ee4fd4fcb2d7fad97fa8823e75"},
"makeup_erlang": {:hex, :makeup_erlang, "0.1.1", "3fcb7f09eb9d98dc4d208f49cc955a34218fc41ff6b84df7c75b3e6e533cc65f", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "174d0809e98a4ef0b3309256cbf97101c6ec01c4ab0b23e926a9e17df2077cbb"},
"membrane_core": {:hex, :membrane_core, "0.8.1", "33df0e4c76c05a2be57042893b05516886462baefdf95b7299e4d2f5db32dea3", [:mix], [{:bunch, "~> 1.3", [hex: :bunch, repo: "hexpm", optional: false]}, {:qex, "~> 0.3", [hex: :qex, repo: "hexpm", optional: false]}, {:ratio, "~> 2.0", [hex: :ratio, repo: "hexpm", optional: false]}, {:telemetry, "~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "5574db35f08de1e95648f9c3acce38c5f318216c5a5ae5e3f253f7edad8fdc1b"},
"membrane_core": {:hex, :membrane_core, "0.9.0", "91a79636c6b2fcc6ae2e611670cdca33a0b38ff4dabe8fb7c1d56cba426db787", [:mix], [{:bunch, "~> 1.3", [hex: :bunch, repo: "hexpm", optional: false]}, {:qex, "~> 0.3", [hex: :qex, repo: "hexpm", optional: false]}, {:ratio, "~> 2.0", [hex: :ratio, repo: "hexpm", optional: false]}, {:telemetry, "~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "ab252e7aa944d23fbb22ce0e8bc75b088cfe3ed1c2c7ba203b1cc2fd794a85ed"},
"membrane_file_plugin": {:hex, :membrane_file_plugin, "0.7.0", "200e416e50e1545473fcfc9020253a797bf884f7d238227466e67266e7e71e5e", [:mix], [{:membrane_core, "~> 0.8.0", [hex: :membrane_core, repo: "hexpm", optional: false]}, {:mockery, "~> 2.2", [hex: :mockery, repo: "hexpm", optional: false]}], "hexpm", "9efe63c780a021552293f3d7ea8b62c93675a8cb9aca34f71e6e979df490cf21"},
"membrane_vp8_format": {:git, "https://github.com/membraneframework/membrane_vp8_format.git", "5a954c6023606731a041934b15cfc26feab6469b", [branch: "add-stream-params"]},
"membrane_vp9_format": {:git, "https://github.com/membraneframework/membrane_vp9_format.git", "af223b0f32ad67f367d32259f69af904736a9e6f", [branch: "add-stream-params"]},
"mockery": {:hex, :mockery, "2.3.1", "a02fd60b10ac9ed37a7a2ecf6786c1f1dd5c75d2b079a60594b089fba32dc087", [:mix], [], "hexpm", "1d0971d88ebf084e962da3f2cfee16f0ea8e04ff73a7710428500d4500b947fa"},
"nimble_parsec": {:hex, :nimble_parsec, "1.2.0", "b44d75e2a6542dcb6acf5d71c32c74ca88960421b6874777f79153bbbbd7dccc", [:mix], [], "hexpm", "52b2871a7515a5ac49b00f214e4165a40724cf99798d8e4a65e4fd64ebd002c1"},
"nimble_parsec": {:hex, :nimble_parsec, "1.2.1", "264fc6864936b59fedb3ceb89998c64e9bb91945faf1eb115d349b96913cc2ef", [:mix], [], "hexpm", "23c31d0ec38c97bf9adde35bc91bc8e1181ea5202881f48a192f4aa2d2cf4d59"},
"numbers": {:hex, :numbers, "5.2.4", "f123d5bb7f6acc366f8f445e10a32bd403c8469bdbce8ce049e1f0972b607080", [:mix], [{:coerce, "~> 1.0", [hex: :coerce, repo: "hexpm", optional: false]}, {:decimal, "~> 1.9 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "eeccf5c61d5f4922198395bf87a465b6f980b8b862dd22d28198c5e6fab38582"},
"qex": {:hex, :qex, "0.5.0", "5a3a9becf67d4006377c4c247ffdaaa8ae5b3634a0caadb788dc24d6125068f4", [:mix], [], "hexpm", "4ad6f6421163cd8204509a119a5c9813cbb969cfb8d802a9dc49b968bffbac2a"},
"qex": {:hex, :qex, "0.5.1", "0d82c0f008551d24fffb99d97f8299afcb8ea9cf99582b770bd004ed5af63fd6", [:mix], [], "hexpm", "935a39fdaf2445834b95951456559e9dc2063d0a055742c558a99987b38d6bab"},
"ratio": {:hex, :ratio, "2.4.2", "c8518f3536d49b1b00d88dd20d49f8b11abb7819638093314a6348139f14f9f9", [:mix], [{:decimal, "~> 1.6 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}, {:numbers, "~> 5.2.0", [hex: :numbers, repo: "hexpm", optional: false]}], "hexpm", "441ef6f73172a3503de65ccf1769030997b0d533b1039422f1e5e0e0b4cbf89e"},
"telemetry": {:hex, :telemetry, "1.0.0", "0f453a102cdf13d506b7c0ab158324c337c41f1cc7548f0bc0e130bbf0ae9452", [:rebar3], [], "hexpm", "73bc09fa59b4a0284efb4624335583c528e07ec9ae76aca96ea0673850aec57a"},
}
Binary file modified test/fixtures/capture.dump
Binary file not shown.
6 changes: 1 addition & 5 deletions test/integration_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,7 @@ defmodule Membrane.Element.IVF.IntegrationTest do
children: [
file_source: %Membrane.File.Source{location: options.input.path},
deserializer: IVF.Deserializer,
serializer: %IVF.Serializer{
width: options.input.width,
height: options.input.height,
rate: 30
},
serializer: %IVF.Serializer{rate: 30},
file_sink: %Membrane.File.Sink{location: options.result_file}
],
links: [
Expand Down
6 changes: 3 additions & 3 deletions test/serializer_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,11 @@ defmodule Membrane.Element.IVF.SerializerTest do

spec = %ParentSpec{
children: [
ivf_serializer: %IVF.Serializer{width: 1080, height: 720, rate: 30},
source: %Testing.Source{
output: Testing.Source.output_from_buffers(options.buffers),
caps: %RemoteStream{content_format: VP9, type: :packetized}
},
ivf_serializer: %IVF.Serializer{width: 1080, height: 720, rate: 30},
sink: sink
],
links: [
Expand All @@ -55,8 +55,8 @@ defmodule Membrane.Element.IVF.SerializerTest do
correctly calculates timestamp in frame header.
"""
test "appends headers correctly" do
buffer_1 = %Buffer{payload: @frame, metadata: %{timestamp: 0}}
buffer_2 = %Buffer{payload: @frame, metadata: %{timestamp: 100_000_000 <|> 3}}
buffer_1 = %Buffer{payload: @frame, dts: 0}
buffer_2 = %Buffer{payload: @frame, dts: 100_000_000 <|> 3}

{:ok, pipeline} =
%Testing.Pipeline.Options{
Expand Down