Synchronization in NMM
----------------------

1. Time representation - Time, Interval and UserTime

There are two types for representation of time in NMM.
Time represents a point of time, Interval stands for a duration 
(which can be considered as the difference between two points of time.) 
Both Time and Interval have a precision of one nanosecond. They are
internally stored as

  struct Time {               struct Interval {
    long int sec;               long int sec;
    long int nsec;              long int nsec;
  };                          };

But a set of standard operators comes along with the two types, and so it 
should not be necessary to manipulate them at that low level.
There is also a type to provide a human readable time representation:
UserTime.

  struct UserTime {
    int hour;
    int min;
    int sec;
    int msec;
  };


2. How to get the time - Clock and TimedElement

Objects that should have access to a time source are inherited from 
TimedElement. All TimedElements of one application share one clock and 
therefore have the same time. They can get the time with the method 

  Time getTime();

They don't have to care about the creation or destruction of the clock.


3. Time information in the data stream - Timestamp

In lots of applications it is necessary to send some time information with 
the data stream. For this reason the class Message has a timestamp. A 
timestamp is build up like this:

  struct Timestamp {
    Time sync_time;
    long int stream_counter;
    bool ct_valid;
  };

The sync_time carries the time information. It should be usually mark 
the beginning time of the media data (for exampele for an audio buffer of 
0.02 seconds duration). 
The stream_counter simply counts the data buffer in the stream.
The flag ct_valid indicates if the time value has been set correctly. This 
is necessary because you do not always have enough information to timestamp 
each outgoing buffer correctly. With this flag you can indicate that a 
buffer's timestamp contains no useful time information.
You can get and set a message's timestamp with the two methods 

  void setTimestamp(const Timestamp timestamp);
  Timestamp getTimestamp();

from the class Message.


4. How to create Timestamps - StreamTimer

Nodes that want to create timestamps for a data stream can use a stream-timer.
A stream-timer has two different modes, REAL_TIME and CONST_RATE. 
You can choose the mode with the method 

  Result setMode(const Mode mode);

In the REAL_TIME - Mode the stream-timer looks at the common clock to create 
the timestamps, in the CONST_RATE - Mode it computes the timestamps regarding 
to the framerate (set by the 

  Result setRate(const float rate);
  Result setInterval(const Interval interval);

methods). The timestamps are set into the messages with 

  Result setTimestamp(Message* message);


5. The sink nodes - GenericSyncSinkNode

All sink nodes that should be synchronized are inherited from 
GenericSyncSinkNode. Instead of the process - or procuce - methods from the 
other Generics, these nodes have the methods 

  prepareBuffer();
  presentBuffer();

In the prepare-method, all time-wasting preparations for the presentation of 
the buffer should happen. In lots of cases, this method is not used and then 
simply should not be overwritten. In the present-method the presentation
should happen as soon as possible.
The method 
  setSynchronized(bool);
can be used to turn the synchronization on and off. Per default it is
turned off.


6. Events related to synchronization

At the moment there are three events that refer to synchronization.
They are called 

  sync_enable,
  sync_disable and
  sync_reset.

The events sync_enable and sync_disable have only an effect on
synchronized sink nodes (i.e. all subclasses of
GenericSyncSinkNode). They have the same functionality as
setSynchronized(true) and setSynchronized(false). This way it is
possible to disable or enable the synchronization after or before the
presentation of a certain buffer. 
The third event, sync_reset, is also handled by many other nodes
(e.g. MPEGVideoDecodeNode, MPEGAudioDecodeNode, AC3DecodeNode etc.).
It is used to indicate that the parameters of synchronization should
be reset. For example, this is necessary if you switch to another
channel on TV or if you choose a new chapter in the DVD application. 


7. Synchronization in the application code

At first, the synchronization has to be enabled at the sink nodes: 

  XDisplayNode display = new XDisplayNode();
  display -> setSynchronized( true );

  PlaybackNode audio_play = new PlaybackNode();
  audio_play -> setSynchronized( true );

Then an AudioVideoSynchronizer has to be created and connected to the audio 
and video sinks. This is done by the following lines of code: 

  AudioVideoSynchronizer synchronizer = new AudioVideoSynchronizer();
  synchronizer -> setVideoSink(display);
  synchronizer -> setAudioSink(audio_play);

Note: Most of the sink nodes have an input queue. Synchronization only works 
      if this queue's mode is set to MODE_SUSPEND.

Last modified: Fri May  3 14:06:27 CET 2002


