Class AudioMasterClock
- All Implemented Interfaces:
VideoClock.MasterClock
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 Summary
Constructors -
Method Summary
-
Constructor Details
-
AudioMasterClock
- Parameters:
playback- the handle to follow, includingPlayback.NONE, which reports zero forever and is therefore detected as a stalled master rather than treated as an error- Throws:
NullPointerException- ifplaybackis null
-
-
Method Details
-
playback
- Returns:
- the handle being followed, never null
-
positionMicros
public long positionMicros()- Specified by:
positionMicrosin interfaceVideoClock.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
-