MediaProcessors
CODECS_MUXERS_LOOPBACK_EXAMPLE

Application code analysis

The example source code: codecs_muxers_loopback.c

This example implements a full video coding and muxing loopback path:

The application flow in this example is analogue to the one shown in figure 2, but with the following modified thread scheme:

Initializing application

Initialization consists in the same steps as the ones enumerated in the "typical application prologue" (see the API documentation):

  1. Initialize (open) the processors (PROCS) module (this is done by calling function 'procs_module_open()');
  2. Register the processor types we support in the application (performed using the operation 'PROCS_REGISTER_TYPE' provided through the function 'procs_module_opt()');
  3. Create (open) an instance of the PROCS module (using the function 'procs_open()' which returns an instance handler);
  4. Creates specific processors instances (using the operation 'PROCS_POST' with the function 'procs_opt()'; a unique processor identifier is supplied for each instance):
    • Create a video encoder instance
    • Create a video decoder instance
    • Create a RTSP multiplexor instance
    • Create a RTSP de-multiplexor instance

Nevertheless, there are some new considerations when initializing and handling the multiplexer and demultiplexer processors. We have an insight on this in the next subsections.

Initializing and handling the multiplexer

After instantiating the multiplexer, we must register all the elementary streams we will be serving (in this example only a video stream is served, but we may add other video or audio streams).
This is done using the following API call:

1 ret_code= procs_opt(procs_ctx, "PROCS_ID_ES_MUX_REGISTER", mux_proc_id, mime_setting, &rest_str);

If succeed, the elementary stream identifier will be returned in a JSON of the form:

1 {"elementary_stream_id":number}

Knowing the elementary stream Id. is essential for multiplexing; is a unique number used to discriminate to which of the multiplexed streams an input frame is to be sent.

This is implemented in the code of the multiplexer thread (function 'mux_thr()'). The important detail to remark is that the elementary stream Id. must be specified in the input frame using the proc_frame_ctx_s::es_id field.

1 proc_frame_ctx->es_id= thr_ctx->elem_strem_id_video_server;
2 ret_code= procs_send_frame(thr_ctx->procs_ctx, thr_ctx->mux_proc_id, proc_frame_ctx);

If more than one source is used (e.g. video and audio), you must use the corresponding elementary stream Id. for sending the frames of each source.

Initializing and handling the de-multiplexer

Regarding to the de-multiplexer initialization, the RTSP client must be provided with the listening URL in the instantiation (e.g. "rtsp_url=rtsp://127.0.0.1:8574/session").

When handling the de-mutiplexer, client application does not know at first instance how many sources are carried in a session. Thus, once the session is established and the multimedia streaming started -that is, when receiving the first frame-, the de-multiplexer API should be used to know the elementary streams carried and the identifiers assigned to each one. It is important to remark that the elementary streams identifiers used at the multiplexer are decoupled of the ones used at the de-multiplexer (in fact, in the case of the RTSP implementation, the de-multiplexer uses the service port as the Id., and the multiplexer use an incrementing counter).
We ask then for the state of the demutiplexer when receiving the first frame (see de-multiplexer thread function 'dmux_thr()'):

1 ret_code= procs_opt(thr_ctx->procs_ctx, "PROCS_ID_GET", thr_ctx->dmux_proc_id, &rest_str);

The answer will be of the form:

1 {
2  "settings":{
3  "rtsp_url":"rtsp://127.0.0.1:8574/session"
4  },
5  "elementary_streams":[
6  {
7  "sdp_mimetype":"video/MP2V",
8  "port":59160,
9  "elementary_stream_id":59160
10  }
11  ]
12 }

This response should be parsed to obtain the elementary stream Id. It will be used to identify which decoder/processor to send each received frame (the stream identifier will be specified in the proc_frame_ctx_s::es_id fied of the received frame).
In this example, we just have one video stream so all the frames received from the de-multiplexer will have the same stream Id.

Running the application

Just type in a shell:

LD_LIBRARY_PATH=<...>/MediaProcessors_selfcontained/_install_dir_x86/lib <...>/MediaProcessors_selfcontained/_install_dir_x86/bin/mediaprocs_codecs_muxers_loopback

Example:

1 MediaProcessors_selfcontained$ LD_LIBRARY_PATH=./_install_dir_x86/lib ./_install_dir_x86/bin/mediaprocs_codecs_muxers_loopback
2 Created new TCP socket 4 for connection
3 Starting server...
4 live555_rtsp.cpp 2290 Got a SDP description: v=0
5 o=- 1508643478136254 1 IN IP4 192.168.1.37
6 s=n/a
7 i=n/a
8 t=0 0
9 a=tool:LIVE555 Streaming Media v2017.07.18
10 a=type:broadcast
11 a=control:*
12 a=range:npt=0-
13 a=x-qt-text-nam:n/a
14 a=x-qt-text-inf:n/a
15 m=video 0 RTP/AVP 96
16 c=IN IP4 0.0.0.0
17 b=AS:3000
18 a=rtpmap:96 mp2v/90000
19 a=control:track1
20 
21 live555_rtsp.cpp 2358 [URL: 'rtsp://127.0.0.1:8574/session/'] Initiated the sub-session 'video/MP2V' (client port[s] 59160, 59161)
22 live555_rtsp.cpp 2417 [URL: 'rtsp://127.0.0.1:8574/session/'] Set up the sub-session 'video/MP2V' (client port[s] 59160, 59161)
23 live555_rtsp.cpp 2459 [URL: 'rtsp://127.0.0.1:8574/session/'] Started playing session...

A window should appear rendering a colorful animation:

Figure 4: Rendering window; mediaprocs_codecs_muxers_loopback example.

Using the RESTful API

In the following lines we attach some examples on how to perform RESTful requests in run-time.
For this purpose, we will use CURL HTTP client commands from a shell.

We assume the 'mediaprocs_codecs_muxers_loopback' application is running.

To get the general representation of the running processors, type:

1 $ curl -H "Content-Type: application/json" -X GET -d '{}' "127.0.0.1:8088/procs.json"
2 {
3  "code":200,
4  "status":"OK",
5  "message":null,
6  "data":{
7  "procs":[
8  {
9  "proc_id":0,
10  "proc_name":"ffmpeg_m2v_enc",
11  "links":[
12  {
13  "rel":"self",
14  "href":"/procs/0.json"
15  }
16  ]
17  },
18  {
19  "proc_id":1,
20  "proc_name":"ffmpeg_m2v_dec",
21  "links":[
22  {
23  "rel":"self",
24  "href":"/procs/1.json"
25  }
26  ]
27  },
28  {
29  "proc_id":2,
30  "proc_name":"live555_rtsp_mux",
31  "links":[
32  {
33  "rel":"self",
34  "href":"/procs/2.json"
35  }
36  ]
37  },
38  {
39  "proc_id":3,
40  "proc_name":"live555_rtsp_dmux",
41  "links":[
42  {
43  "rel":"self",
44  "href":"/procs/3.json"
45  }
46  ]
47  }
48  ]
49  }
50 }

In the response above you will find the list of the instantiated processors specifying:

To get the representational state of any of the processors:

1 $curl -H "Content-Type: application/json" -X GET -d '{}' "127.0.0.1:8088/procs/0.json"
2 {
3  "code":200,
4  "status":"OK",
5  "message":null,
6  "data":{
7  "latency_avg_usec":35502,
8  "settings":{
9  "proc_name":"ffmpeg_m2v_enc",
10  "bit_rate_output":307200,
11  "frame_rate_output":15,
12  "width_output":352,
13  "height_output":288,
14  "gop_size":15,
15  "conf_preset":null
16  }
17  }
18 }
1 $curl -H "Content-Type: application/json" -X GET -d '{}' "127.0.0.1:8088/procs/1.json"
2 {
3  "code":200,
4  "status":"OK",
5  "message":null,
6  "data":{
7  "latency_avg_usec":0,
8  "settings":{
9  "proc_name":"ffmpeg_m2v_dec"
10  }
11  }
12 }
1 $curl -H "Content-Type: application/json" -X GET -d '{}' "127.0.0.1:8088/procs/2.json"
2 {
3  "code":200,
4  "status":"OK",
5  "message":null,
6  "data":{
7  "settings":{
8  "proc_name":"live555_rtsp_mux",
9  "rtsp_port":8574,
10  "time_stamp_freq":9000,
11  "rtsp_streaming_session_name":"session"
12  },
13  "elementary_streams":[
14  {
15  "sdp_mimetype":"video/mp2v",
16  "rtp_timestamp_freq":9000,
17  "elementary_stream_id":0
18  }
19  ]
20  }
21 }
1 $curl -H "Content-Type: application/json" -X GET -d '{}' "127.0.0.1:8088/procs/3.json"
2 {
3  "code":200,
4  "status":"OK",
5  "message":null,
6  "data":{
7  "settings":{
8  "proc_name":"live555_rtsp_dmux",
9  "rtsp_url":"rtsp://127.0.0.1:8574/session"
10  },
11  "elementary_streams":[
12  {
13  "sdp_mimetype":"video/MP2V",
14  "port":40014,
15  "elementary_stream_id":40014
16  }
17  ]
18  }
19 }

If you want to change some of the video encoder parameters, let's say the ouput width and height, do:

1 $curl -X PUT "127.0.0.1:8088/procs/0.json?width_output=720&height_output=480"
2 
3 $curl -H "Content-Type: application/json" -X GET -d '{}' "127.0.0.1:8088/procs/0.json"
4 {
5  "code":200,
6  "status":"OK",
7  "message":null,
8  "data":{
9  "latency_avg_usec":40751,
10  "settings":{
11  "proc_name":"ffmpeg_m2v_enc",
12  "bit_rate_output":307200,
13  "frame_rate_output":15,
14  "width_output":720,
15  "height_output":480,
16  "gop_size":15,
17  "conf_preset":null
18  }
19  }
20 }

You can even change the processor type on tun time. You have to be very careful of changing both, encoder and decoder sides.
In the following code we switch from MPEG2-video to H.264 video coding:

1 $ curl -X PUT "127.0.0.1:8088/procs/0.json?proc_name=ffmpeg_x264_enc"; curl -X PUT "127.0.0.1:8088/procs/1.json?proc_name=ffmpeg_x264_dec";
2 
3 $ curl -H "Content-Type: application/json" -X GET -d '{}' "127.0.0.1:8088/procs/0.json"; curl -H "Content-Type: application/json" -X GET -d '{}' "127.0.0.1:8088/procs/1.json"
4 {
5  "code":200,
6  "status":"OK",
7  "message":null,
8  "data":{
9  "latency_avg_usec":941490,
10  "settings":{
11  "proc_name":"ffmpeg_x264_enc",
12  "bit_rate_output":307200,
13  "frame_rate_output":15,
14  "width_output":352,
15  "height_output":288,
16  "gop_size":15,
17  "conf_preset":null,
18  "flag_zerolatency":false
19  }
20  }
21 }
22 {
23  "code":200,
24  "status":"OK",
25  "message":null,
26  "data":{
27  "latency_avg_usec":0,
28  "settings":{
29  "proc_name":"ffmpeg_x264_dec"
30  }
31  }
32 }

Changing RTSP multiplexer / de-multiplexer settings is also possible. We have to take into account:

Get the multiplexer and de-mutiplexer representational state:

1 $ curl -H "Content-Type: application/json" -X GET -d '{}' "127.0.0.1:8088/procs/2.json" &&
2 curl -H "Content-Type: application/json" -X GET -d '{}' "127.0.0.1:8088/procs/3.json"
3 {
4  "code":200,
5  "status":"OK",
6  "message":null,
7  "data":{
8  "settings":{
9  "rtsp_port":8574,
10  "time_stamp_freq":9000,
11  "rtsp_streaming_session_name":"session"
12  },
13  "elementary_streams":[
14  {
15  "sdp_mimetype":"video/mp2v",
16  "rtp_timestamp_freq":9000,
17  "elementary_stream_id":0
18  }
19  ]
20  }
21 }
22 {
23  "code":200,
24  "status":"OK",
25  "message":null,
26  "data":{
27  "settings":{
28  "rtsp_url":"rtsp://127.0.0.1:8574/session"
29  },
30  "elementary_streams":[
31  {
32  "sdp_mimetype":"video/MP2V",
33  "port":40924,
34  "elementary_stream_id":40924
35  }
36  ]
37  }
38 }

We will change firstly the port and the session name on the client side:

1 $ curl -X PUT "127.0.0.1:8088/procs/3.json?rtsp_url=rtsp://127.0.0.1:8575/session2"
2 $ curl -H "Content-Type: application/json" -X GET -d '{}' "127.0.0.1:8088/procs/3.json"
3 {
4  "code":200,
5  "status":"OK",
6  "message":null,
7  "data":{
8  "settings":{
9  "rtsp_url":"rtsp://127.0.0.1:8575/session2"
10  },
11  "elementary_streams":[
12 
13  ]
14  }
15 }

As can be seen, the new client fail and closes the session. Now we will change the server accordingly, set again the client (to have the effect of restarting it), and check that a new session was successfully established:

1 $curl -X PUT "127.0.0.1:8088/procs/2.json?rtsp_port=8575&rtsp_streaming_session_name=session2"
2 $curl -X PUT "127.0.0.1:8088/procs/3.json?rtsp_url=rtsp://127.0.0.1:8575/session2"
3 $ curl -H "Content-Type: application/json" -X GET -d '{}' "127.0.0.1:8088/procs/3.json"
4 {
5  "code":200,
6  "status":"OK",
7  "message":null,
8  "data":{
9  "settings":{
10  "rtsp_url":"rtsp://127.0.0.1:8575/session2"
11  },
12  "elementary_streams":[
13  {
14  "sdp_mimetype":"video/MP2V",
15  "port":32916,
16  "elementary_stream_id":32916
17  }
18  ]
19  }
20 }