Skip to content

Commit c6b09d2

Browse files
committed
Tutorials update WIP
1 parent 1ecc2ad commit c6b09d2

File tree

7 files changed

+68
-140
lines changed

7 files changed

+68
-140
lines changed
File renamed without changes.
File renamed without changes.
Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,15 @@
11
# Architecture
22

33
Now let's discuss how the architecture of our solution will look like.
4-
It will be a little different from the RTMP to HLS architecture.
5-
The main component will be the pipeline, which will ingest RTP stream and convert it to HLS. Beyond that we will also need a Connection Manager, which will be responsible for establishing an RTSP connection with the server.
4+
It will be a little different from the RTMP to HLS architecture. In most cases communication with a RTSP server is split into two phases:
65

7-
![image](assets/rtsp_architecture.drawio.png)
6+
- Negotiation of the stream parameters over RTSP.
7+
- Receiving RTP stream(s) that the client and server have agreed upon.
88

9-
When initializing, the pipeline will start a Connection Manager which starts an RTSP connection with the server. Once the connection is fully established, the pipeline will be notified.
9+
Both of these phases are handled by RTSP Source. Let's take a closer look how each of them folds out:
1010

11-
Let's take a closer look on each of those components:
12-
13-
## Connection Manager
14-
The role of the connection manager is to initialize RTSP session and start playing the stream.
11+
## Establishing the connection
12+
When establishing a connection the source will act as a connection manager, initializing the RTSP session and starting the stream playback.
1513
It communicates with the server using the [RTSP requests](https://antmedia.io/rtsp-explained-what-is-rtsp-how-it-works/#RTSP_requests). In fact, we won't need many requests to start playing the stream - take a look at the desired message flow:
1614

1715
![image](assets/connection_manager.drawio.png)
@@ -20,18 +18,21 @@ First we want to get the details of the video we will be playing, by sending the
2018
Then we call the `SETUP` method, defining the transport protocol (RTP) and client port used for receiving the stream.
2119
Now we can start the stream using `PLAY` method.
2220

23-
## Pipeline
21+
## Receiving the stream
2422

25-
The pipeline consists of a couple elements, each of them performing a specific media processing task. You can definitely notice some similarities to the pipeline described in the [RTMP architecture](02_RTMP_Introduction.md). However, we will only be processing video so only the video processing elements will be necessary.
23+
The source is a bin containing a few elements, each of them performing a specific media processing task. You can definitely notice some similarities to the pipeline described in the [RTMP architecture](02_RTMP_Introduction.md). However, we will only be processing video so only the video processing elements will be necessary.
2624

2725
![image](assets/rtsp_pipeline.drawio.png)
2826

29-
We have already used the, `H264 Parser`, `MP4 H264 Payloader`, `CMAF Muxer` and `HLS Sink` elements in the RTMP pipeline, take a look at the [RTMP to HLS architecture](03_RTMP_SystemArchitecture.md) chapter for details of the purpose of those elements.
27+
We have already used the, `H264 Parser` and `HLS Sink Bin` elements in the RTMP pipeline, take a look at the [RTMP to HLS architecture](03_RTMP_SystemArchitecture.md) chapter for details of the purpose of those elements.
3028

3129
Let us describe briefly what is the purpose of the other components:
3230

3331
### UDP Source
3432
This element is quite simple - it receives UDP packets from the network and sends their payloads to the next element.
3533

36-
### RTP SessionBin
37-
RTP SessionBin is a Membrane's Bin, which is a Membrane's container used for creating reusable groups of elements. In our case the Bin handles the RTP session with the server, which has been set up by the Connection Manager.
34+
### RTP Demuxer
35+
This element is responsible for getting media packets out of the RTP packets they were transported in and routing them according to their [SSRC](https://datatracker.ietf.org/doc/html/rfc3550#section-3). In our case we only receive a single video stream, so only one output will be used.
36+
37+
### RTP H264 Depayloader
38+
When transporting H264 streams over RTP they need to be split into chunks and have some additional metadata included. This element's role is to unpack the RTP packets it receives from the Demuxer into a pure H264 stream that can be processed further.
Lines changed: 15 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,53 +1,43 @@
11
In the tutorial we won't explain how to implement the solution from the ground up - instead, we will run the existing code from [Membrane demos](https://github.com/membraneframework/membrane_demo).
22

33
To run the RTSP to HLS converter first clone the demos repo:
4-
```console
4+
```bash
55
git clone https://github.com/membraneframework/membrane_demo.git
66
```
77

8-
```console
8+
```bash
99
cd membrane_demo/rtsp_to_hls
1010
```
1111

1212
Install the dependencies
13-
```console
13+
```bash
1414
mix deps.get
1515
```
1616

17-
Make sure you have those libraries installed as well:
18-
- gcc
19-
- libc-dev
20-
- ffmpeg
21-
22-
On ubuntu:
23-
```console
24-
apt-get install gcc libc-dev ffmpeg
25-
```
26-
2717
Take a look inside the `lib/application.ex` file. It's responsible for starting the pipeline.
2818
We need to give a few arguments to the pipeline:
2919
```elixir
30-
@rtsp_stream_url "rtsp://rtsp.membrane.work:554/testsrc.264"
31-
@output_path "hls_output"
32-
@rtp_port 20000
20+
rtsp_stream_url = "rtsp://localhost:30001"
21+
output_path = "hls_output"
22+
rtp_port = 20000
3323
```
3424

35-
The `@output_path` attribute defines the storage directory for hls files and the `@rtp_port` defines on which port we will be expecting the rtp stream, once the RTSP connection is established.
25+
The `output_path` attribute defines the storage directory for hls files and the `rtp_port` defines on which port we will be expecting the rtp stream, once the RTSP connection is established.
3626

37-
The `@rtsp_stream_url` attribute contains the address of the stream, which we will be converting. It is a sample stream prepared for the purpose of the demo.
27+
The `rtsp_stream_url` attribute contains the address of the stream, which we will be converting. If you want to receive a stream from some accessible RTSP server, you can pass it's URL here. In this demo we'll run our own, simple server:
28+
29+
```bash
30+
mix run server.exs
31+
```
3832

3933
Now we can start the application:
40-
```console
34+
```bash
4135
mix run --no-halt
4236
```
4337

44-
The pipeline will start playing, after a couple of seconds the HLS files should appear in the `@output_path` directory. In order to play the stream we need to first serve them. We can do it using simple python server.
45-
46-
```console
47-
python3 -m http.server 8000
48-
```
38+
The pipeline will start playing, after a couple of seconds the HLS files should appear in the `@output_path` directory.
4939

5040
Then we can play the stream using [ffmpeg](https://ffmpeg.org/), by pointing to the location of the manifest file:
51-
```console
41+
```bash
5242
ffplay http://YOUR_MACHINE_IP:8000/rtsp_to_hls/hls_output/index.m3u8
5343
```

broadcasting/10_ConnectionManager.md

Lines changed: 0 additions & 71 deletions
This file was deleted.

broadcasting/11_RTSP_Pipeline.md

Lines changed: 39 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,46 +1,54 @@
1-
As explained in the [Architecture chapter](08_RTSP_Architecture.md), the pipeline will consist of a couple of elements, that will be processing the RTP stream.
1+
As explained in the [Architecture chapter](08_RTSP_Architecture.md), the pipeline will consist of a RTSP Source and a HLS Sink.
22

3-
The flow of the pipeline will consist of three steps. First, when the pipeline is initialized we will start the Connection Manager, which will set up the RTP stream via the RTSP.
4-
Once that is finished, we will set up two initial elements in the pipeline - the `UDP Source` and `RTP SessionBin`, which will allow us to receive RTP packets and process them.
5-
When the SessionBin detects that the RTP stream has been started, it will notify the pipeline with the `:new_rtp_stream` notification. Later on, we will add the remaining elements to the pipeline, allowing for the whole conversion process to take place.
3+
The initial pipeline will consist only of the `RTSP Source` and it'll start establishing the connection with the RTSP server.
64

7-
Those steps take place, respectively, in the: `handle_init/1`, `handle_other/3` and `handle_notification/4` callbacks. While the `handle_init/1` is rather intuitive, we will describe in detail what's happening in the other callbacks.
5+
<!--The flow of the pipeline will consist of three steps. First, when the pipeline is initialized we will start the Connection Manager, which will set up the RTP stream via the RTSP.-->
6+
<!--Once that is finished, we will set up two initial elements in the pipeline - the `UDP Source` and `RTP SessionBin`, which will allow us to receive RTP packets and process them.-->
7+
<!--When the SessionBin detects that the RTP stream has been started, it will notify the pipeline with the `:new_rtp_stream` notification. Later on, we will add the remaining elements to the pipeline, allowing for the whole conversion process to take place.-->
88

9-
Let us explain what's going on in the `handle_other` callback:
9+
<!--Those steps take place, respectively, in the: `handle_init/1`, `handle_other/3` and `handle_notification/4` callbacks. While the `handle_init/1` is rather intuitive, we will describe in detail what's happening in the other callbacks.-->
10+
11+
<!--Let us explain what's going on in the `handle_other` callback:-->
1012

1113
##### lib/pipeline.ex
1214
```elixir
1315
@impl true
14-
def handle_other({:rtsp_setup_complete, options}, _ctx, state) do
15-
children = %{
16-
app_source: %Membrane.UDP.Source{
17-
local_port_no: state[:port],
18-
recv_buffer_size: 500_000
19-
},
20-
rtp: %Membrane.RTP.SessionBin{
21-
fmt_mapping: %{96 => {:H264, 90_000}}
22-
},
23-
hls: %Membrane.HTTPAdaptiveStream.Sink{
24-
manifest_module: Membrane.HTTPAdaptiveStream.HLS,
25-
target_window_duration: 120 |> Membrane.Time.seconds(),
26-
target_segment_duration: 4 |> Membrane.Time.seconds(),
27-
storage: %Membrane.HTTPAdaptiveStream.Storages.FileStorage{
28-
directory: state[:output_path]
29-
}
30-
}
31-
}
16+
def handle_init(_context, options) do
17+
Logger.debug("Source handle_init options: #{inspect(options)}")
18+
19+
spec = [
20+
child(:source, %Membrane.RTSP.Source{
21+
transport: {:udp, options.port, options.port + 5},
22+
allowed_media_types: [:video, :audio],
23+
stream_uri: options.stream_url,
24+
on_connection_closed: :send_eos
25+
})
26+
]
3227

33-
links = [
34-
link(:app_source)
35-
|> via_in(Pad.ref(:rtp_input, make_ref()))
36-
|> to(:rtp)
37-
]
28+
{[spec: spec],
29+
%{
30+
output_path: options.output_path,
31+
parent_pid: options.parent_pid,
32+
tracks_left_to_link: nil,
33+
track_specs: []
34+
}}
35+
end
36+
```
37+
38+
Once we receive the `{:set_up_tracks, tracks}` notification from the source we have the information what tracks have been set up during connection establishment and what we should expect. We take this information and store it, so that we link the source to the `HLS Sink Bin` correctly.
39+
40+
```elixir
41+
@impl true
42+
def handle_child_notification({:set_up_tracks, tracks}, :source, _ctx, state) do
43+
tracks_left_to_link =
44+
[:audio, :video]
45+
|> Enum.filter(fn media_type -> Enum.any?(tracks, &(&1.type == media_type)) end)
3846

39-
spec = %ParentSpec{children: children, links: links}
40-
{ {:ok, spec: spec}, %{state | video: %{sps: options[:sps], pps: options[:pps]}} }
47+
{[], %{state | tracks_left_to_link: tracks_left_to_link}}
4148
end
4249
```
4350

51+
When a `PLAY` request is eventually sent by the source, we should be prepared to receive streams that have been set up. When a new RTP stream is received and identified by the source, a message is set to the parent - the pipeline in our case - containing information necessary to handle the stream.
4452
When we receive the `rtsp_setup_complete` message, we first define the new children for the pipeline, and links between them - the UDP Source and the RTP SessionBin. We also create the HLS Sink, however we won't be linking it just yet. With the message we receive the sps and pps inside the options, and we add them to the pipeline's state.
4553

4654
Only after we receive the `:new_rtp_stream` notification we add the rest of the elements and link them with each other:
-86.9 KB
Loading

0 commit comments

Comments
 (0)