Figure illustrates the method invocations that shape the local, intra-application
data flow.
Figure: The local intra-application data flow and
involved method invocations.
Producer and Consumer objects are active multimedia objects in the sense of Gibbs, that is, to each Producer or Consumer is assigned an exclusive thread of
control. Besides, Producers and Consumers are required to have a
Start()
, Stop()
and
Finish()
method.
A Producer's only task is to generate multimedia data. The distribution of this data is handled by
an associated StreamExporter object: a Producer calls the exportChunk()
method of its
StreamExporter for each generated media element. In turn, the StreamExporter distributes the
media elements among the various Consumers by calling the importChunk()
method of every
registered Stream object. A Stream object queues the imported media elements until the
Consumer extracts the elements by calling a get()
method. Thereby, the activities of
the Producer and Consumers become asynchronous. To avoid memory overflow, the Stream object
can implement flow control algorithms, which can, if necessary, drop Chunks or refuse new
Chunks. The decision, which Chunk to drop or refuse, depends highly only the media type.
For example, the deletion of MPEG I-frames renders following B- and P-frames useless, hence no
I-frames should get lost.
A Consumer object that desires to import a multimedia data stream from a particular
StreamExporter invokes the importStream()
method of the StreamManager, passing the
desired StreamExporter and Stream class as arguments. The StreamManager instantiates a Stream
object of the requested class and registers it with the StreamExporter. The registration causes
the StreamExporter to export any new media elements from that time on to the new Stream object, too.
The consumer obtains the newly instantiated Stream object as a result of the
importStream()
method invocation, and can now receive new media
elements by repeatedly invoking one of the get()
methods of its Stream object.
The get()
methods of a Stream object differ in their semantics. All the get()
methods extract the first Chunk element of the Stream's internal media element queue if a Chunk is
available, or block the caller until a new Chunks arrives.
The differences lie in the type of the returned media elements and the time-related behaviour:
the returned object can either be simple Chunk
object or a media element object of a
more complex class; furthermore, a get()
method may or may not delay the caller for
the purpose of playout synchronisation.
As mentioned above, TimedObjects are used to facilitate inter- and intra-stream playout
synchronisation. The necessary timing information is extracted from the timestamps of the
Chunks, from which the TimedObjects are constructed. To map the timestamps to instants
measurable in local wall-clock time, each Stream contains a Clock object, whose
timestamp--to--wall-clock-time ratio is controlled by the StreamExporter's
Master_Clock object. Each Clock may have an individual playout offset delay,
however. Inter-stream synchronisation is achieved by adjusting the playout delay of one
Stream's Clock to the playout delay of another related Stream. This adjustment requires
a mapping between the Chunks' timestamps and the points of
time of the generation of the Chunks. For example, RTCP sender reports
provide this correspondence by means of an RTP timestamp and an NTP timestamp, which both
denominate the same point of time, as described in Section .