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

Reimar Döffinger Reimar.Doeffinger at gmx.de
Tue Mar 2 23:40:32 CET 2010


On Thu, Feb 25, 2010 at 01:36:06PM -0800, Jason Holt wrote:
> > Making it public and using it from anywhere else. I don't want a signal
> > handler
> > called from normal code.
> 
> I recall now why I added the exit_sighandler and EINTR code.  Without
> checking for EINTR, if you ^C mplayer while it's waiting for a datagram from
> the master, it doesn't die, since the signal got caught by the system.  So
> in that case, you have to send mplayer a second signal before it dies.  So
> that's why I'm calling the sighandler if we get EINTR.  I've added a comment
> to that effect.

The problem with this comment:
> +    // if you send mplayer a signal while it's waiting for a recvfrom, the
> +    // system intercepts the signal and recvfrom aborts with EINTR.
> +    if (n == -1 && errno == EINTR) {
> +        exit_sighandler(SIGINT); // actual signal number isn't available
> +    }

is that it's just nonsense.
After testing with this program:
#include <stdio.h>
#include <signal.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>

static void sighandler(int sig) {
  printf("got signal %i\n", sig);
}

int main(void) {
  int r;
  int s = socket(AF_INET, SOCK_STREAM, 0);
  listen(s, 0);
  signal(SIGINT, sighandler);
  siginterrupt(SIGINT, 1);
  r = accept(s, NULL, NULL);
  printf("returned %i %i\n", r, errno);
}

The correct comment would be:
// if you send mplayer a signal while it's waiting for a recvfrom,
// recvfrom aborts with EINTR and the signal handler is called.
// Now for the pure fun of it we call the signal handler once more
// with a bogus value, causing MPlayer to panic and just abort the
// whole program instead of cleaning up properly.
// Has the side effect that ^C "works" on some systems without having
// to do it the proper way e.g. by checking async_quit_request.
// However it's all quite pointless since a lot of kernels (Ubuntu 9.10
// at least) do not abort the syscall and do not return EINTR anyway
// even if a signal arrived.

> I'm happy to take it out if you prefer; it just means it's a little harder
> to kill mplayer when using -udp-slave.

On my system at least tests indicate it doesn't make any difference anyway,
not without additional changes.

> Sorry, I somehow imagined it was the variable you used for the return
> > value.
> > In this case it seems like long unfortunately is the right type :-(
> >
> 
> reverted

Doesn't matter, but I just noticed what's so confusing: the fcntl argument
is long, but the return value is int.
Since the flags must fit in both there is no "correct type" and either one works.

> @@ -94,6 +96,11 @@
>  
>  #include "input/input.h"
>  
> +#if !HAVE_WINSOCK2_H
> +#include <netinet/in.h>
> +#endif
> +

I can't quite see what this is needed for.

> +// globals for UDP sync
> +float udp_master_position = -1.0; // remember where the master is in the file
> +float udp_timing_tolerance = 0.02; // how far off is still considered equal

At least for udp_timing_tolerance I fail to see where it is used outside this
file or modified?

> +    static struct sockaddr_in servaddr, cliaddr;

Can't see why those are static.

> +    static char mesg[1000];

Since we shouldn't really need it to be that large, this
could be on stack as well.

> +        setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(struct timeval));

sizeof(tv) ?

> +    n = recvfrom(sockfd, mesg, 999, 0, (struct sockaddr *)&cliaddr, &len);

sizeof(mesg) - 1 ?

> +    while (-1 != recvfrom(sockfd, mesg, 999, 0, (struct sockaddr *)&cliaddr, &len)) {
> +        mesg[999] = 0;
> +        mesg[n] = 0;

I'm not really happy about the code duplication and the two different ways
of doing the 0-termination

> +            udp_master_position = master_position;

Making it a return value would probably make the main function a lot less confusing.
Or at least a pointer argument if the case where udp_master_position should not
change would be too messy with a return value.
Also this function is not used outside this file, so it should be static.

> +        // but if we've fallen behind a few seconds, we're "close behind"
> +        // and should run to catch up
> +        if ((my_position > udp_master_position - udp_seek_threshold) &&
> +                (my_position < udp_master_position + udp_timing_tolerance)) {

But that comment does not even describe what the code does!
It also triggers as "close behind" if we are only a slight bit ahead (well, by the
numbers, maybe not in reality).
There's also no explanation why the udp_timing_tolerance is only applied to
the lower check, but not to any of the udp_seek_threshold ones for example.
Probably it's a simplification assuming udp_timing_tolerance << udp_seek_threshold,
but that's at least not a documented assumption.
Or it is because the udp_timing_tolerance actually should be named udp_transfer_delay.

> +        // we're either right with the master, or close behind.  play
> +        // the frame that's currently ready
> +        if (my_position < udp_master_position + udp_timing_tolerance) {

And this _is_ checking the same thing as the condition on the next loop around.
Which leads me to the question why the code is not like

// try updating our info without waiting
exit = get_udp(0, &udp_master_position);

while (!exit) {
    // we are too far off, seek
    if (FFABS(mpctx->sh_video->pts - udp_master_position) > udp_seek_threshold) {
...
    }
    // time to display, master has (hopefully just) passed us
    if (udp_master_position + udp_delay > my_position)
        break;
    // wait for master/update information
    exit = get_udp(1, &udp_master_position);
}

I also have to add that it would be nicer if the exit wasn't hidden all the
way in get_udp but instead the information was passed all the way up to main().



More information about the MPlayer-dev-eng mailing list