Skip to content

Commit 4fcab69

Browse files
authored
Add onMetaData command support (#84)
1 parent ae54b64 commit 4fcab69

File tree

7 files changed

+102
-3
lines changed

7 files changed

+102
-3
lines changed

lib/membrane_rtmp_plugin/rtmp/source/message.ex

+4-2
Original file line numberDiff line numberDiff line change
@@ -28,11 +28,13 @@ defmodule Membrane.RTMP.Message do
2828
"FCPublish" => Messages.FCPublish,
2929
"createStream" => Messages.CreateStream,
3030
"publish" => Messages.Publish,
31-
"@setDataFrame" => Messages.SetDataFrame
31+
"@setDataFrame" => Messages.SetDataFrame,
32+
"onMetaData" => Messages.OnMetaData
3233
}
3334

3435
@amf_data_to_module %{
35-
"@setDataFrame" => Messages.SetDataFrame
36+
"@setDataFrame" => Messages.SetDataFrame,
37+
"onMetaData" => Messages.OnMetaData
3638
}
3739

3840
@spec deserialize_message(type_id :: integer(), binary()) :: struct()

lib/membrane_rtmp_plugin/rtmp/source/message_handler.ex

+11
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,17 @@ defmodule Membrane.RTMP.MessageHandler do
154154
end
155155
end
156156

157+
@validation_stage :on_meta_data
158+
defp do_handle_client_message(%Messages.OnMetaData{} = on_meta_data, _header, state) do
159+
case MessageValidator.validate_on_meta_data(state.validator, on_meta_data) do
160+
{:ok, _msg} = result ->
161+
{:cont, validation_action(state, @validation_stage, result)}
162+
163+
{:error, _reason} = error ->
164+
{:halt, {:error, :stream_validation, validation_action(state, @validation_stage, error)}}
165+
end
166+
end
167+
157168
# According to ffmpeg's documentation, this command should prepare the server to receive media streams
158169
# We are simply acknowledging the message
159170
defp do_handle_client_message(%Messages.FCPublish{}, _header, state) do

lib/membrane_rtmp_plugin/rtmp/source/message_validator.ex

+6
Original file line numberDiff line numberDiff line change
@@ -31,4 +31,10 @@ defprotocol Membrane.RTMP.MessageValidator do
3131
"""
3232
@spec validate_set_data_frame(t(), Messages.SetDataFrame.t()) :: validation_result_t()
3333
def validate_set_data_frame(impl, message)
34+
35+
@doc """
36+
Validates the `t:Membrane.RTMP.Messages.OnMetaData.t/0` message.
37+
"""
38+
@spec validate_on_meta_data(t(), Messages.OnMetaData.t()) :: validation_result_t()
39+
def validate_on_meta_data(impl, message)
3440
end

lib/membrane_rtmp_plugin/rtmp/source/message_validator/default.ex

+3
Original file line numberDiff line numberDiff line change
@@ -18,4 +18,7 @@ defimpl Membrane.RTMP.MessageValidator, for: Membrane.RTMP.MessageValidator.Defa
1818

1919
@impl true
2020
def validate_set_data_frame(_impl, _message), do: {:ok, "set data frame success"}
21+
22+
@impl true
23+
def validate_on_meta_data(_impl, _message), do: {:ok, "on meta data success"}
2124
end
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
defmodule Membrane.RTMP.Messages.OnMetaData do
2+
@moduledoc """
3+
Defines the RTMP `onMetaData` command (sent by nginx client).
4+
"""
5+
6+
@behaviour Membrane.RTMP.Message
7+
8+
alias Membrane.RTMP.AMF.Encoder
9+
10+
@attributes_to_keys %{
11+
"duration" => :duration,
12+
"width" => :width,
13+
"height" => :height,
14+
"videocodecid" => :video_codec_id,
15+
"videodatarate" => :video_data_rate,
16+
"framerate" => :framerate,
17+
"audiocodecid" => :audio_codec_id,
18+
"audiodatarate" => :audio_data_rate
19+
}
20+
21+
@keys_to_attributes Map.new(@attributes_to_keys, fn {key, value} -> {value, key} end)
22+
23+
defstruct Map.keys(@keys_to_attributes)
24+
25+
@type t :: %__MODULE__{
26+
duration: number(),
27+
# video related
28+
width: number(),
29+
height: number(),
30+
video_codec_id: number(),
31+
video_data_rate: number(),
32+
framerate: number(),
33+
# audio related
34+
audio_codec_id: number(),
35+
audio_data_rate: number()
36+
}
37+
38+
@impl true
39+
def from_data(["onMetaData", properties]) do
40+
new(properties)
41+
end
42+
43+
@spec new([{String.t(), any()}]) :: t()
44+
def new(options) do
45+
params =
46+
options
47+
|> Map.new()
48+
|> Map.take(Map.keys(@attributes_to_keys))
49+
|> Enum.map(fn {key, value} ->
50+
{Map.fetch!(@attributes_to_keys, key), value}
51+
end)
52+
53+
struct!(__MODULE__, params)
54+
end
55+
56+
# helper for message serialization
57+
@doc false
58+
@spec to_map(t()) :: map()
59+
def to_map(%__MODULE__{} = message) do
60+
Map.new(message, fn {key, value} -> {Map.fetch!(@keys_to_attributes, key), value} end)
61+
end
62+
63+
defimpl Membrane.RTMP.Messages.Serializer do
64+
require Membrane.RTMP.Header
65+
66+
@impl true
67+
def serialize(%@for{} = message) do
68+
Encoder.encode([@for.to_map(message)])
69+
end
70+
71+
@impl true
72+
def type(%@for{}), do: Membrane.RTMP.Header.type(:amf_data)
73+
end
74+
end

mix.exs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
defmodule Membrane.RTMP.Mixfile do
22
use Mix.Project
33

4-
@version "0.20.2"
4+
@version "0.21.0"
55
@github_url "https://github.com/membraneframework/membrane_rtmp_plugin"
66

77
def project do

test/support/test_validator.ex

+3
Original file line numberDiff line numberDiff line change
@@ -32,4 +32,7 @@ defimpl Membrane.RTMP.MessageValidator, for: Support.TestValidator do
3232

3333
@impl true
3434
def validate_set_data_frame(_impl, _message), do: {:ok, "set data frame success"}
35+
36+
@impl true
37+
def validate_on_meta_data(_impl, _message), do: {:ok, "on meta data success"}
3538
end

0 commit comments

Comments
 (0)