Package limn.video

Class AudioMasterClock

java.lang.Object
limn.video.AudioMasterClock
All Implemented Interfaces:
VideoClock.MasterClock

public final class AudioMasterClock extends Object implements VideoClock.MasterClock
Reads a sounding audio playback's position as a VideoClock.MasterClock, so pictures are timed by the device that is actually making noise rather than by the wall clock. Audio is the right master because a dropped picture is invisible for a sixtieth of a second and a gap in sound is audible at a millisecond; video therefore follows audio, never the reverse.

 Playback track = Sounds.stream(audioSource, PlayOptions.DEFAULTS.withBus(AudioBus.MUSIC));
 clock.setMaster(new AudioMasterClock(track));
 

Units. Playback.positionSeconds() reports seconds as a double and a master reports whole microseconds, so every reading is rounded to the nearest microsecond. The error is bounded at half a microsecond and does not accumulate: each reading is an absolute position and nothing here adds an interval to a previous one, which is the arithmetic that turns a rate like 30000/1001 into a picture of drift every few minutes.

Timeline. The position is measured from the start of the audio track. Pictures must be timed on that same timeline: a stream whose presentation times start at a container's native base, paired with this, is out by the whole base and no threshold recovers it.

What it does when the position is not a position. A handle from a machine with no audio device reports a constant zero, a finished playback reports zero, and a playback that has not started producing one yet reports zero. All three read as a master that is not advancing, which VideoClock already answers: after VideoClock.MASTER_STALL_MICROS it declares the master stalled and drives the timeline from the wall clock instead, so the video plays at the right rate rather than not at all. A reading that is not a finite positive number (which no shipped engine produces, and which a broken one would turn into either a frozen picture or a burst of dropped ones) is reported as zero for the same reason.

A looping track wraps. An engine that rewinds a stream at the end of its data reports in-track time, so the position falls back to near zero at each wrap. That is a backwards move of the whole track, far above VideoClock.MASTER_JUMP_MICROS, and the clock counts it as a seek and re-anchors, which is correct only if the pictures wrapped at that instant too. Two tracks of different lengths cannot share one timeline across a wrap; whoever loops them decides what to do about it, and doing nothing means the video holds until the audio catches up to it.

Cost. Reading a position is not free and is not lock-free: a streaming engine composes it from what its service thread has accounted for plus the device's offset into what is still queued, under the engine's own monitor. VideoClock reads a master exactly once per decision for that reason. An engine may also report in steps coarser than the display refreshes, so a caller will see the same reading twice in a row; the clock's stall and jump timers are dated from the last reading that changed rather than from the last one taken, which is what keeps a coarse-stepping device from being counted as one that seeks.

Immutable and stateless, so any thread may read it and two clocks may share one.

  • Constructor Details

    • AudioMasterClock

      public AudioMasterClock(Playback playback)
      Parameters:
      playback - the handle to follow, including Playback.NONE, which reports zero forever and is therefore detected as a stalled master rather than treated as an error
      Throws:
      NullPointerException - if playback is null
  • Method Details

    • playback

      public Playback playback()
      Returns:
      the handle being followed, never null
    • positionMicros

      public long positionMicros()
      Specified by:
      positionMicros in interface VideoClock.MasterClock
      Returns:
      the playback's position in microseconds from the start of its track, rounded to nearest, or 0 when it reports nothing usable
    • toString

      public String toString()
      Overrides:
      toString in class Object