[MPlayer-dev-eng] [PATCH] Network synchronized playback using UDP

Jason Holt jholt at google.com
Fri Mar 27 01:17:50 CET 2009


> while True:
>    closebehind = master.pts - THRESHOLD < self.pts <= master.pts
>    # If nonblocking we just check whether we already received a new UDP
> packet
>    master.update_pts(blocking=not closebehind)
>    if (abs(self.pts - master.pts) > THRESHOLD):
>        self.seek(master.pts)
>        break
>    if (self.pts <= master.pts):
>        break


Let's say the master and slave are at 10 seconds, with a 10fps video and
threshold of 0.15s.  The master seeks back to 8 seconds.  The slave calls
seek, and let's say that lands us at 5, because that's the nearest keyframe
before the requested position.

!closebehind, so we block.  Now the master is at 8.1, so we seek again and
we're still at 5.  That continues until the master passes the next keyframe
on the slave, at which point we hopefully jump to within THRESHOLD of the
master.  If we don't, then the client ends up showing the frozen keyframe
nearest behind the master until we get lucky, which might be never if the
frames are spaced just right.

If the slave's nearest keyframe is at 12 instead of 5, then the slave blocks
and seeks repeatedly to 12s until the master catches up, so that's okay.

I tried yours and a couple of other approaches, and I'm pretty sure the one
below is the most correct (and simplest).  A key observation for me was that
the slave needs to keep its pts just ahead of the master.  The master is
telling it the pts of the frame it's about to play, so the slave needs to
not just catch up with the last thing it heard from the master, but also
decode the next frame and be ready to play it.

So here, we play frames with no sleeping until we've decoded just ahead of
the last message from the master.  Then we wait for a message, which should
match up almost exactly with the frame we just decoded.  If not, the master
must have seeked, so we likewise seek.  If we land ahead of the master, we
keep seeking and landing there until it catches up.  If we land behind, the
first conditional ensures we play iframes until we catch up.

I also now flush the input udp buffer after each message, so that datagrams
don't pile up on a slow machine, causing them to run ever further behind.
Slaves always get the most recent message, so they'll seek as often as
necessary to keep up.

I tried it in my video cave and it works much better than before.

Here's the central logic for the slaves:

    // the master tells us what frame it's about to play,
    // so my_position had better start out ahead of the last thing
    // we heard from the master.  that way we're ready to play the frame
    // the master is about to play.
    ...

      if (my_position < udp_master_position + udp_timing_tolerance) {
        // play immediately
      } else {
        float timing_error, absolute_timing_error;
        float master_position = wait_for_udp(udp_port);

        if (master_position == -1.0) {
          // udp timed out.  use the old value
        } else {
          udp_master_position = master_position;
        }

        // timing_error < 0 means we're behind the master
        timing_error = mpctx->sh_video->pts - udp_master_position;
        absolute_timing_error = (timing_error < 0) ? -timing_error:
timing_error;

        if (absolute_timing_error > udp_timing_tolerance ) {
          abs_seek_pos=SEEK_ABSOLUTE;
          rel_seek_secs=master_position;
        }
      }

Updated patch attached.



More information about the MPlayer-dev-eng mailing list