Skip to content

Commit

Permalink
Initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
Noarkhh committed Sep 11, 2024
1 parent 31a5aaf commit 9f3a105
Show file tree
Hide file tree
Showing 6 changed files with 240 additions and 24 deletions.
15 changes: 15 additions & 0 deletions lib/membrane/simple_rtsp_server.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
defmodule Membrane.SimpleRTSPServer do
@moduledoc """
Module for starting a simple RTSP Server that streams a MP4 file.
"""

@spec start_link(String.t(), :inet.port_number(), :inet.ip4_address()) :: GenServer.on_start()
def start_link(mp4_path, rtsp_port \\ 554, address \\ {127, 0, 0, 1}) do
Membrane.RTSP.Server.start_link(
handler: Membrane.SimpleRTSPServer.Handler,
handler_config: %{mp4_path: mp4_path},
port: rtsp_port,
address: address
)
end
end
88 changes: 88 additions & 0 deletions lib/membrane/simple_rtsp_server/handler.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
defmodule Membrane.SimpleRTSPServer.Handler do
@moduledoc false

use Membrane.RTSP.Server.Handler

require Membrane.Logger

alias Membrane.RTSP.Response
@video_pt 96
@video_clock_rate 90_000
@audio_pt 97
@audio_clock_rate 90_000

@impl true
def init(config) do
config
|> Map.put(:pipeline_pid, nil)
|> Map.put(:socket, nil)
end

@impl true
def handle_open_connection(conn, state) do
%{state | socket: conn}
end

@impl true
def handle_describe(_req, state) do
sdp = """
v=0
m=video 0 RTP/AVP #{@video_pt}
a=control:video
a=rtpmap:#{@video_pt} H264/#{@video_clock_rate}
a=fmtp:#{@video_pt} packetization-mode=1
"""

# m=audio 0 RTP/AVP #{@audio_pt}
# a=control:audio
# a=rtpmap:#{@audio_pt} MP4A-LATM/#{@audio_clock_rate}

response =
Response.new(200)
|> Response.with_header("Content-Type", "application/sdp")
|> Response.with_body(sdp)

{response, state}
end

@impl true
def handle_setup(_req, state) do
{Response.new(200), state}
end

@impl true
def handle_play(configured_media_context, state) do
media_context = configured_media_context |> Map.values() |> List.first()

{client_rtp_port, _client_rtcp_port} = media_context.client_port

arg = %{
socket: state.socket,
ssrc: media_context.ssrc,
pt: @video_pt,
clock_rate: @video_clock_rate,
client_port: client_rtp_port,
client_ip: media_context.address,
server_rtp_socket: media_context.rtp_socket,
mp4_path: state.mp4_path
}

{:ok, _sup_pid, pipeline_pid} =
Membrane.SimpleRTSPServer.Pipeline.start_link(arg)

{Response.new(200), %{state | pipeline_pid: pipeline_pid}}
end

@impl true
def handle_pause(state) do
{Response.new(501), state}
end

@impl true
def handle_teardown(state) do
{Response.new(200), state}
end

@impl true
def handle_closed_connection(_state), do: :ok
end
78 changes: 78 additions & 0 deletions lib/membrane/simple_rtsp_server/pipeline.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
defmodule Membrane.SimpleRTSPServer.Pipeline do
@moduledoc false

use Membrane.Pipeline

@spec start_link(map()) :: Membrane.Pipeline.on_start()
def start_link(config) do
Membrane.Pipeline.start_link(__MODULE__, config)
end

@impl true
def handle_init(_ctx, opts) do
spec =
child(:mp4_in_file_source, %Membrane.File.Source{
location: opts.mp4_path,
seekable?: true
})
|> child(:mp4_demuxer, %Membrane.MP4.Demuxer.ISOM{optimize_for_non_fast_start?: true})

{[spec: spec], opts}
end

@impl true
def handle_child_notification({:new_tracks, tracks}, :mp4_demuxer, _ctx, state) do
spec =
Enum.map(tracks, fn
{id, %Membrane.AAC{}} ->
get_child(:mp4_demuxer)
|> via_out(Pad.ref(:output, id))
|> child(Membrane.Debug.Sink)

{id, %Membrane.H264{}} ->
get_child(:mp4_demuxer)
|> via_out(Pad.ref(:output, id))
|> child(:parser, %Membrane.H264.Parser{
output_alignment: :nalu,
repeat_parameter_sets: true,
skip_until_keyframe: true,
output_stream_structure: :annexb
})
|> via_in(Pad.ref(:input, state.ssrc),
options: [payloader: Membrane.RTP.H264.Payloader]
)
|> child(:rtp, Membrane.RTP.SessionBin)
|> via_out(Pad.ref(:rtp_output, state.ssrc),
options: [
payload_type: state.pt,
clock_rate: state.clock_rate
]
)
|> child(:realtimer, Membrane.Realtimer)
|> child(:udp_sink, %Membrane.UDP.Sink{
destination_address: state.client_ip,
destination_port_no: state.client_port,
local_socket: state.server_rtp_socket
})
end)

{[spec: spec], state}
end

@impl true
def handle_child_notification(_notification, _element, _ctx, state) do
{[], state}
end

@impl true
def handle_element_end_of_stream(:udp_sink, :input, _ctx, state) do
Process.sleep(50)
:gen_tcp.close(state.socket)
{[terminate: :normal], state}
end

@impl true
def handle_element_end_of_stream(_child, _pad, _ctx, state) do
{[], state}
end
end
2 changes: 0 additions & 2 deletions lib/membrane_template.ex

This file was deleted.

23 changes: 17 additions & 6 deletions mix.exs
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
defmodule Membrane.Template.Mixfile do
defmodule Membrane.SimpleRTSPServer.Mixfile do
use Mix.Project

@version "0.1.0"
@github_url "https://github.com/membraneframework/membrane_template_plugin"
@github_url "https://github.com/membraneframework-labs/membrane_simple_rtsp_server"

def project do
[
app: :membrane_template_plugin,
app: :membrane_simple_rtsp_server,
version: @version,
elixir: "~> 1.13",
elixirc_paths: elixirc_paths(Mix.env()),
Expand All @@ -15,11 +15,11 @@ defmodule Membrane.Template.Mixfile do
dialyzer: dialyzer(),

# hex
description: "Template Plugin for Membrane Framework",
description: "Simple RTSP Server based on Membrane Framework",
package: package(),

# docs
name: "Membrane Template plugin",
name: "Membrane Simple RTSP Server",
source_url: @github_url,
docs: docs(),
homepage_url: "https://membrane.stream"
Expand All @@ -38,6 +38,17 @@ defmodule Membrane.Template.Mixfile do
defp deps do
[
{:membrane_core, "~> 1.0"},
{:membrane_rtsp,
github: "membraneframework/membrane_rtsp", branch: "server-ports-optional"},
# {:membrane_rtsp, "~> 0.9.0"},
{:membrane_rtp_plugin, "~> 0.29.0"},
{:membrane_rtp_h264_plugin, "~> 0.19.0"},
{:membrane_rtp_aac_plugin, "~> 0.9.0"},
{:membrane_file_plugin, "~> 0.17.0"},
{:membrane_mp4_plugin, "~> 0.35.0"},
{:membrane_h26x_plugin, "~> 0.10.0"},
{:membrane_udp_plugin, "~> 0.14.0"},
{:membrane_realtimer_plugin, "~> 0.9.0"},
{:ex_doc, ">= 0.0.0", only: :dev, runtime: false},
{:dialyxir, ">= 0.0.0", only: :dev, runtime: false},
{:credo, ">= 0.0.0", only: :dev, runtime: false}
Expand Down Expand Up @@ -74,7 +85,7 @@ defmodule Membrane.Template.Mixfile do
extras: ["README.md", "LICENSE"],
formatters: ["html"],
source_ref: "v#{@version}",
nest_modules_by_prefix: [Membrane.Template]
nest_modules_by_prefix: [Membrane.SimpleRTSPServer]
]
end
end
Loading

0 comments on commit 9f3a105

Please sign in to comment.