Memory Streaming
As outlined above, the roMediaStreamer object provides a means of specifying a single flexible pipeline of multimedia processing. However, the pipeline is constrained to having a single input and a single output. The memory streaming feature adds a means of bifurcating the data stream so that multiple outputs can be created, and more than one thread of processing can be realized.
Note that memory streaming is especially important when serving live-encoded media such as HDMI® Input. Memory streaming ensures that only a single encoder is needed to distribute a live-encoded stream to multiple clients.
HTTP Multiple Unicasting
To unicast a source stream to multiple clients, output the source (in this case, encoded HDMI Input) to a memory stream:
m = CreateObject("roMediaStreamer")
m.SetPipeline("hdmi:, encoder:, mem:/livehdmi")
m.Start()
Now a large number of clients can receive the same stream named "livehdmi" (the name is arbitrary but must not conflict with any other memory stream).
When a client accesses the following URL, the HTTP media-server code creates a separate roMediaServer instance reading from the source mem:/livehdmi/stream.ts
and forwards it to the HTTP socket:
http://IP_address:port/mem:/livehdmi/stream.ts
Notice that /stream.ts
must be appended to the memory stream name to denote that the entire stream should be used.
Note
In the above instance, the media streamer code will actually create the source component memsimple:/livehdmi/stream.ts as it uses fewer resources and is more efficient.
Once the memory stream has been created, it can also be simultaneously multicast using another Media Streamer instance:
m1 = CreateObject("roMediaStreamer")
m1.SetPipeline("memsimple:/livehdmi/stream.ts,rtpsimple://239.192.0.0:5004/")
m1.Start()
mem: and memsimple:
As with certain other components, there is a "simple" and a "non-simple" version of memory streaming. The simple version (memsimple:
) can only be used in a pipeline with other simple components, although simple memory streams can read from non-simple mem:
memory streams (as shown above, where the memsimple:/livehdmi
component reads from the mem:/livehdmi
component). However, the converse is not possible: The mem:
source component cannot be used to read from a memory stream that was created with the memsimple:
destination component.
Because simple memory streams are not indexed, they cannot support HLS streaming operations downstream (other components will not receive information about the HLS segments).
As is the case with other "simple" components, simple memory streams should be used wherever possible because they consume fewer resources.
HLS Multiple Unicasting
HLS multiple unicasting functions similarly to the HTTP multicast example given above, but with one exception: Stream indexing filters only work on streams read into the transport stream processor, not on streams read into the encoder (both of which are handled by the mem:
component). As a result, encoded streams such as HDMI Input must be passed to the mem:
component twice before they can be multicast. This is easily accomplished using two SetPipeline()
calls:
Note that a larger memory buffer (30MB in this instance) must be specified for the second mem:
component because it must hold at least five complete 5-second segments at any time. Clients generally require 5 complete segments at all times for HLS streams, so they will often need to wait at least 25 seconds before the stream can be played successfully.
Clients can then access HLS streams using the following URL:
Like HTTP multicasting, the media streamer will generate additional roMediaStreamer instances for each client that connects. In addition to HLS clients, HTTP and RTSP clients can also connect to the indexed stream using the following URLs:
HTTP
RTSP
Note
The indexed stream can also be multicast by another roMediaStreamer instance.
Accessing an Indexed Memory Stream
As shown in previous examples, appending stream.ts
to the memory stream identifier denotes the entire buffer, while appending index.m3u8
to an indexed (HLS) stream denotes the entire index file. Similarly, appending the name of a segment listed in the index file will access that segment for the duration of its lifetime in the memory stream buffer. For example, if the index file lists a segment named 000179.ts
as available in the mem:/livestream component
, then the following source component can be used to fetch that segment:
Note
The memsimple: component can also be used to fetch the segment.
Memory Stream Pacing
The flow of data into the memory stream is always paced in real time to give the reading clients time to keep up. An roMediaStreamer instance that is writing will never wait for any of the reading clients; if any client gets too far behind (which is detected by the writing edge of the FIFO overtaking the client's reading edge), the client will abort and issue an EVENT_EOS_ERROR message.
Because of this limitation, it is important to note the following: If a memory stream is used to work around the fact that data cannot be passed directly from an encoder to a decoder (as in the below example), then processing cannot happen faster than in real time.
Example
Memory Stream States
A memory stream is only created when the Media Streamer that names it as a destination is in at least the CONNECTED state. This means it is possible to connect the pipeline (using Connect()
) that creates the memory stream but not start it. Any number of Media Streamers reading from a memory stream can be created (and even started) before the original Media Streamer is itself started, beginning the flow of data into the memory stream.
Encryption and Copy Protection
Generally speaking, an roMediaStreamer instance reading from a memory stream will adopt the same encryption/copy-protection as the associated roMediaStreamer instance that is writing to it. The specific rules are as follows:
Simple writer: A
memsimple:
destination cannot be marked as requiring protection (nor can it receive such a stream).Simple reader: A
memsimple:
source will only read from a memory stream that was marked as unencrypted (requiring no protection) when it was created.Non-simple writer: A
mem:
destination will be marked as requiring protection when it is created according to the source of the pipeline. If a stream becomes protected later, it will continue to operate if it was marked as requiring protection initially (optionally with theencrypt
parameter), otherwise it will stop.Non-simple reader: A
mem:
source inherits the protection status of its associated writer, and therefore its output will ultimately require HDCP or DTCP protection if the source was similarly marked.