summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJaromil <jaromil@dyne.org>2012-04-11 17:40:07 (GMT)
committer Jaromil <jaromil@dyne.org>2012-04-11 17:40:07 (GMT)
commit9bc2a05171a2b0f184d95c5c345b9b9c6723d32e (patch)
tree34916a995cf7bf8c99c8da506f72264451e98a10
parentf96e719f004cc40a4cc6e8821d4cd295d094b766 (diff)
integrated 0mq handshake in hdsync. dryrun test syncs well
-rw-r--r--src/Makefile.am36
-rw-r--r--src/avremote.cpp (renamed from src/avremote.c)4
-rw-r--r--src/handshake.cpp45
-rw-r--r--src/hdsync_cli.c228
-rw-r--r--src/hdsync_cli.cpp494
-rw-r--r--src/parsers.cpp (renamed from src/parsers.c)0
6 files changed, 531 insertions, 276 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
index ff10dda..6b17d1b 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -2,41 +2,11 @@ INCLUDES = -I$(top_srcdir)/src/include
SUBDIRS = uuid pgm 0mq
-bin_PROGRAMS = netcat broadcaster hdsync avremote handshake
-
-
-handshake_SOURCES = handshake.cpp
-
-handshake_LDADD = uuid/libuuid.la 0mq/libzmq.la pgm/libpgm.la -lpthread
-
-netcat_SOURCES = \
- core.c \
- flagset.c \
- misc.c \
- netcat.c \
- network.c \
- telnet.c \
- udphelper.c
+bin_PROGRAMS = hdsync
noinst_HEADERS = avremote.h discover.h intl.h misc.h netcat.h \
parsers.h proto.h
-broadcaster_SOURCES = broadcaster.cpp
-
-hdsync_SOURCES = avremote.c avremote.h hdsync_cli.c parsers.c parsers.h
-
-avremote_SOURCES = avremote.c avremote.h avremote_cli.c parsers.c parsers.h
-
-netcat_nc = $(DESTDIR)$(bindir)/nc
-
-install-exec-hook:
- @if [ ! -L $(netcat_nc) -a ! -e $(netcat_nc) ]; then \
- echo "Creating symlink to the shorter executable name"; \
- ln -s netcat $(netcat_nc); \
- fi
+hdsync_SOURCES = avremote.cpp avremote.h hdsync_cli.cpp parsers.cpp parsers.h
-uninstall-hook:
- @if [ -L $(netcat_nc) ]; then \
- echo "Removing symlink to the shorter executable name"; \
- rm -f $(netcat_nc); \
- fi
+hdsync_LDADD = uuid/libuuid.la 0mq/libzmq.la pgm/libpgm.la -lpthread
diff --git a/src/avremote.c b/src/avremote.cpp
index 70360b1..a8c805e 100644
--- a/src/avremote.c
+++ b/src/avremote.cpp
@@ -53,8 +53,8 @@
upnp_t *create_upnp() {
upnp_t *upnp;
- upnp = calloc(1,sizeof(upnp_t));
- upnp->hostname = calloc(MAX_HOSTNAME_SIZE,sizeof(char));
+ upnp = (upnp_t*)calloc(1,sizeof(upnp_t));
+ upnp->hostname = (char*)calloc(MAX_HOSTNAME_SIZE,sizeof(char));
upnp->port = -1;
upnp->sockfd = -1;
diff --git a/src/handshake.cpp b/src/handshake.cpp
index d3a4a23..8dd5967 100644
--- a/src/handshake.cpp
+++ b/src/handshake.cpp
@@ -1,5 +1,5 @@
-/* HDSync
-
+/* Simple handshake on LAN program using TCP broadcast (PGM)
+
(c) 2012 Denis Roio <jaromil@dyne.org>
This program is free software: you can redistribute it and/or modify
@@ -43,13 +43,14 @@ int main (int argc, char **argv)
char **listeners = NULL;
int listindex = 0;
int listmax = 0;
+ char res[128];
// discover own IP address
struct ifaddrs * ifAddrStruct=NULL;
struct ifaddrs * ifa=NULL;
void * tmpAddrPtr=NULL;
char IPv4[24];
- printf("Discovering self assigned IPv4 addresses\n");
+ printf("Handshake on LAN\n");
getifaddrs(&ifAddrStruct);
pgmaddr[0] = NULL; // zero if iface not found
@@ -106,7 +107,6 @@ int main (int argc, char **argv)
snprintf(message,255,"OFFER;%s;",IPv4);
while(1) {
- char res[128];
zmq_msg_init_size (&request, strlen(message));
memcpy (zmq_msg_data (&request), message, strlen(message));
@@ -126,8 +126,7 @@ int main (int argc, char **argv)
if( strncmp(res,"ACK",3) == 0) {
int c;
char *p, *pp;
- printf("%s\n",res);
-
+ // quick and secure string parsing
p = res; while(*p != ';') p++; p++;
pp = p; while(*pp != ';') pp++; *pp='\0';
@@ -141,10 +140,19 @@ int main (int argc, char **argv)
listindex++;
}
if(listindex==listmax) { // goal reached
- printf("All listeners found:\n");
- for(c=0;c<listmax;c++)
- printf("%s\n",listeners[c]);
- break;
+ printf("Sending ready signals to listeners:\n");
+ ///////////////////////////////////////////////
+ for(c=0;c<listmax;c++) {
+ char tmpaddr[256];
+ snprintf(tmpaddr,255, "tcp://%s:%s",listeners[c],PGMPORT);
+ printf (" %s\n", tmpaddr);
+ zmq_connect(sock_out, tmpaddr);
+ zmq_msg_init_size (&request, 5);
+ memcpy (zmq_msg_data (&request), "READY", 5);
+ zmq_send (sock_out, &request, 0);
+ zmq_msg_close (&request);
+ }
+ break; // break offer loop
}
} // if ACK response
@@ -166,10 +174,13 @@ int main (int argc, char **argv)
// Wait for next request from client
zmq_msg_init (&request);
zmq_recv (sock_in, &request, 0);
- // printf ("Received offer\n");
- printf ("%s\n",zmq_msg_data(&request));
- zmq_msg_close (&request);
+ snprintf(res,127,"%s",zmq_msg_data(&request));
+ zmq_msg_close (&request);
+ if(strncmp(res,"READY",5) == 0) {
+ printf ("Received ready signal\n");
+ break;
+ }
sleep (1);
// Send reply back to client
@@ -185,6 +196,14 @@ int main (int argc, char **argv)
}
+ printf("Clean exit.\n");
+
+ if(listeners) {
+ for(int c; c<listmax; c++)
+ free(listeners[c]);
+ free(listeners);
+ }
+
if(sock_out) zmq_close (sock_out);
if(sock_in) zmq_close (sock_in);
diff --git a/src/hdsync_cli.c b/src/hdsync_cli.c
deleted file mode 100644
index f57835c..0000000
--- a/src/hdsync_cli.c
+++ /dev/null
@@ -1,228 +0,0 @@
-/* HDSync
-
- (c) 2011 Nederlands Instituut voor Mediakunst (NIMk)
- 2011 Denis Roio <jaromil@nimk.nl>
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU Affero General Public License as
- published by the Free Software Foundation, either version 3 of the
- License, or (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Affero General Public License for more details.
-
- You should have received a copy of the GNU Affero General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>
-
-*/
-
-#include <config.h>
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <netdb.h>
-
-#include <errno.h>
-
-#include <avremote.h>
-#include <parsers.h>
-
-// our exit codes are shell style: 1 is error, 0 is success
-#define ERR 1
-
-// uncomment to debug
-#define DEBUG 1
-
-
-char filename[512];
-char command[64];
-char server[512];
-int port = 0;
-
-parser_f *parser = NULL;
-
-// we use only getopt, no _long
-static const char *short_options = "-hvs:p:";
-
-void cmdline(int argc, char **argv) {
- command[0] = 0;
- filename[0] = 0;
- server[0] = 0;
-
- int res, optlen;
- do {
- res = getopt(argc, argv, short_options);
- switch(res) {
- case 'h':
- fprintf(stderr,
- "%s %s - prepare and start synced playback of video across devices\n"
- "\n"
- " Copyright (C) 2011 Jaromil @ NIMk.nl Artlab , License GNU AGPL v3+\n"
- " This is free software: you are free to change and redistribute it.\n"
- " The latest HDSync sourcecode is published on <%s>\n"
- "\n"
- "Syntax: hdsync [options] [command] [args...]\n"
- "\n"
- "Commands:\n"
- "\n"
- " prepare prepare a file ready to sync\n"
- " start start the video that was prepared\n"
- "\n"
- "Options:\n"
- "\n"
- " -s network address or hostname of the media server\n"
- " -p port on which the UPNP AVTransport daemon is listening\n"
- " -t dry run to test without a server (print out rendered xml)\n"
- "\n"
- " -h print this help\n"
- " -v version information for this tool\n"
- "\n"
- "For more informations on HDSync see: http://nimk.nl/syncstarter\n"
- "Please report bugs on <http://bugs.dyne.org>.\n",
- PACKAGE, VERSION, PACKAGE_URL);
- exit(0);
-
- case 'v':
- fprintf(stderr,"%s - simple commandline tool to send AVTransport commands over UPNP\n"
- "version %s by Jaromil - Netherlands Media Art Institute\n"
- "Copyright (C) 2011 NIMk Artlab, License GNU AGPL v3+\n"
- "This is free software: you are free to change and redistribute it\n",
- PACKAGE, VERSION);
- exit(0);
-
- case 's':
- snprintf(server,511,"%s",optarg);
- break;
-
- case 'p':
- sscanf (optarg, "%u", &port);
- break;
-
- case '?':
- fprintf(stderr,"unrecognized option: %s\n",optarg);
- break;
-
- case 1:
- if(!command[0]) {
- snprintf(command,63,"%s",optarg);
- } else
- snprintf(filename,511,"%s",optarg);
- break;
- default:
- break;
- }
-
- } while(res != -1);
-}
-
-
-int main(int argc, char **argv) {
- upnp_t *upnp;
- int found;
-
- cmdline(argc, argv);
-
- upnp = create_upnp();
-
- // no server specified, force localhost
- if(!server[0]) sprintf(server,"localhost");
-
- // commandline or detection found explicit addresses
- snprintf(upnp->hostname, MAX_HOSTNAME_SIZE-1,"%s",server);
- upnp->port = port;
-
- if ( connect_upnp (upnp) < 0 )
- {
- fprintf(stderr,"can't connect to %s:%u: operation aborted.\n", server, port);
- exit(ERR);
- }
-
- /* command parsing is a cascade switch on single letters
- this is supposedly faster than strcmp. mapping:
-
- P repare
- S tart
-
- */
- switch(command[0]) {
-
- case 'p': // Prepare
-
- // load, play and pause in sequence
- // break and reopen connection in between
-
- // was connected already
-
- // load
- render_uri_meta(upnp,filename);
- render_upnp(upnp,"SetAVTransportURI", upnp->meta);
-
- // must re-connect socket between commands
- send_upnp(upnp);
- recv_upnp(upnp, 1000);
- close(upnp->sockfd);
- upnp->sockfd = 0;
-
- connect_upnp(upnp);
- render_upnp(upnp,"Play","<Speed>1</Speed>");
-
- send_upnp(upnp);
- recv_upnp(upnp, 1000);
- close(upnp->sockfd);
- upnp->sockfd = 0;
-
- connect_upnp(upnp);
- render_upnp(upnp,"Pause","");
- send_upnp(upnp);
- recv_upnp(upnp, 1000);
- break;
-
- case 's': // start sync
- // was connected already
- render_upnp(upnp,"Play","<Speed>1</Speed>");
- send_upnp(upnp);
- recv_upnp(upnp, 1000);
- break;
-
- /*
- case 'g': // dump a parsable full state of the device
- render_upnp(upnp,"GetTransportInfo","");
- parser = GetTransportInfo;
-
- break;
-
- case 'm': // set the playmode:
- // "NORMAL", "REPEAT_ONE", "REPEAT_ALL", "RANDOM"
- {
- char tmp[256];
- snprintf(tmp,255,"<NewPlayMode>%s</NewPlayMode>",filename);
- render_upnp(upnp,"SetPlayMode",tmp);
- }
- break;
-
- case 'j': // jump aka seek
- // <SeekMode> and <SeekTarget>
- {
- char tmp[512];
- snprintf(tmp,511,"<Unit>REL_TIME</Unit><Target>%s</Target>",filename);
- render_upnp(upnp,"Seek",tmp);
- }
- break;
- */
- default:
- if(!command[0]) break;
- fprintf(stderr,"warning: command not recognized\n");
- exit(1);
- }
-
- free_upnp(upnp);
-
- exit(0);
-}
diff --git a/src/hdsync_cli.cpp b/src/hdsync_cli.cpp
new file mode 100644
index 0000000..88109a8
--- /dev/null
+++ b/src/hdsync_cli.cpp
@@ -0,0 +1,494 @@
+/* HDSync
+
+ (c) 2011-2012 Montevideo / Time Based Arts (aka NIMk.nl)
+ 2011-2012 Denis Roio <jaromil@dyne.org>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as
+ published by the Free Software Foundation, either version 3 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>
+
+*/
+
+#include <config.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netdb.h>
+
+#include <errno.h>
+
+#include <sys/types.h>
+#include <ifaddrs.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <avremote.h>
+#include <parsers.h>
+#include <zmq.h>
+
+// our exit codes are shell style: 1 is error, 0 is success
+#define ERR 1
+
+// uncomment to debug
+#define DEBUG 1
+
+// network settings
+#define ETHDEV "eth0"
+#define PGMPORT "5555"
+
+
+// commandline switches
+char filename[512];
+char server[512];
+int port = 0;
+int dryrun = 0;
+int chanID = 0;
+int chanTOT = 0;
+
+// upnp reply parser
+parser_f *parser = NULL;
+
+// peers found
+char **listeners = NULL;
+
+
+// own IPv4 address
+char IPv4[24];
+
+// 0mq
+void *zcontext;
+void *sock_in = NULL;
+void *sock_out = NULL;
+char pgmaddr[64];
+zmq_msg_t request;
+
+// we use only getopt, no _long
+static const char *short_options = "-hvts:p:";
+
+void cmdline(int argc, char **argv) {
+ filename[0] = 0;
+ server[0] = 0;
+
+ int res, optlen;
+ do {
+ res = getopt(argc, argv, short_options);
+ switch(res) {
+ case 'h':
+ fprintf(stderr,
+ "%s %s - prepare and start synced playback of video across devices\n"
+ "\n"
+ " Copyright (C) 2011-2012 Jaromil @ Dyne.org , License GNU AGPL v3+\n"
+ " This is free software: you are free to change and redistribute it.\n"
+ " The latest HDSync sourcecode is published on <%s>\n"
+ "\n"
+ "Syntax: hdsync [options] channel_id channel_total filename\n"
+ "\n"
+ "Commands:\n"
+ "\n"
+ " prepare prepare a file ready to sync\n"
+ " start start the video that was prepared\n"
+ "\n"
+ "Options:\n"
+ "\n"
+ " -s network address or hostname of the media server\n"
+ " -p port on which the UPNP AVTransport daemon is listening\n"
+ " -t dry run to test without a server (print out rendered xml)\n"
+ "\n"
+ " -h print this help\n"
+ " -v version information for this tool\n"
+ "\n"
+ "For more informations on HDSync see <http://syncstarter.org>\n"
+ "Please report bugs on <http://bugs.dyne.org>.\n",
+ PACKAGE, VERSION, PACKAGE_URL);
+ exit(0);
+
+ case 'v':
+ fprintf(stderr,"%s - simple commandline tool to send AVTransport commands over UPNP\n"
+ "version %s distributed by Montevideo/NIMk\n"
+ "Copyright (C) 2011-2012 Jaromil @ Dyne.org, License GNU AGPL v3+\n"
+ "This is free software: you are free to change and redistribute it\n",
+ PACKAGE, VERSION);
+ exit(0);
+
+ case 's':
+ snprintf(server,511,"%s",optarg);
+ break;
+
+ case 'p':
+ sscanf (optarg, "%u", &port);
+ break;
+
+ case 't':
+ dryrun = 1;
+ break;
+
+ case '?':
+ fprintf(stderr,"unrecognized option: %s\n",optarg);
+ break;
+
+ case 1:
+ if(!chanID) {
+ sscanf(optarg,"%u",&chanID);
+ } else if(!chanTOT) {
+ sscanf(optarg,"%u",&chanTOT);
+ } else if(!filename[0]) {
+ snprintf(filename,511,"%s",optarg);
+ }
+ break;
+ default:
+ break;
+ }
+
+ } while(res != -1);
+}
+
+int handshake() {
+ int request_nbr;
+ zmq_msg_t reply;
+ char message[256];
+ int listindex = 0;
+ int listmax = 0;
+ char res[128];
+
+ // discover own IP address
+ struct ifaddrs * ifAddrStruct=NULL;
+ struct ifaddrs * ifa=NULL;
+ void * tmpAddrPtr=NULL;
+
+ printf("Handshake on LAN\n");
+
+ getifaddrs(&ifAddrStruct);
+
+ pgmaddr[0] = NULL; // zero if iface not found
+
+ for (ifa = ifAddrStruct; ifa != NULL; ifa = ifa->ifa_next) {
+ if (ifa ->ifa_addr->sa_family==AF_INET) { // check it is IP4
+ // is a valid IP4 Address
+ tmpAddrPtr=&((struct sockaddr_in *)ifa->ifa_addr)->sin_addr;
+ char addressBuffer[INET_ADDRSTRLEN];
+ inet_ntop(AF_INET, tmpAddrPtr, addressBuffer, INET_ADDRSTRLEN);
+ // printf("%s IP Address %s\n", ifa->ifa_name, addressBuffer);
+ if( strcmp(ifa->ifa_name, ETHDEV) == 0) {
+ strncpy(IPv4, addressBuffer, 16);
+ snprintf(pgmaddr,63,"pgm://%s:%s",ifa->ifa_name, PGMPORT);
+ }
+ }
+ }
+ if (ifAddrStruct!=NULL) freeifaddrs(ifAddrStruct);
+
+ if(!pgmaddr[0]) {
+ printf("Error: ethernet interface %s is not configured\n",ETHDEV);
+ return(0);
+ }
+
+
+ // create output socket
+ sock_out = zmq_socket (zcontext, ZMQ_PUB);
+ zmq_connect(sock_out, pgmaddr);
+
+ // create input socket
+ sock_in = zmq_socket (zcontext, ZMQ_SUB);
+ zmq_bind (sock_in, pgmaddr);
+
+ if(chanID==1) { // first channel is the one to offer
+ int i;
+
+ ////////
+ // OFFER
+ //////////////////////////////
+ printf ("Offering on %s from address %s\n",pgmaddr, IPv4);
+
+ listmax = chanTOT; // number of desired peers
+ listmax--; // we count outrselves in
+ printf("looking to connect %u peers\n",listmax);
+ // allocate space for listener array
+ listeners = (char**) calloc(listmax,sizeof(char*));
+ for(i=0; i<listmax; i++) listeners[i] = (char*)calloc(16, sizeof(char));
+
+ // offer subscribes to all broadcasts by default
+ // somehow listening to ACK only doesn't works
+ zmq_setsockopt(sock_in,ZMQ_SUBSCRIBE,"",0);
+ snprintf(message,255,"OFFER;%s;",IPv4);
+ while(1) {
+
+ // broadcast offer
+ zmq_msg_init_size (&request, strlen(message));
+ memcpy (zmq_msg_data (&request), message, strlen(message));
+ // printf ("Sending offer...\n", request_nbr);
+ zmq_send (sock_out, &request, 0);
+ zmq_msg_close (&request);
+
+ sleep(1);
+
+ // listen for answers
+ zmq_msg_init (&reply);
+ zmq_recv (sock_in, &reply, 0);
+ // printf ("Received response\n");
+ snprintf(res,127,"%s",zmq_msg_data(&reply));
+ zmq_msg_close (&reply);
+ // parse the response and if ACK insert it in uniq list
+ if( strncmp(res,"ACK",3) == 0) {
+ int c;
+ char *p, *pp;
+ // quick and secure string parsing
+ p = res; while(*p != ';') p++; p++;
+ pp = p; while(*pp != ';') pp++; *pp='\0';
+
+ // *p has the ip string
+ for(c=0; c<listindex; c++)
+ if(strncmp(listeners[c], p, 16) == 0)
+ break; // found a duplicate
+ if(c==listindex) { // there was no duplicate
+ snprintf(listeners[c], 16, "%s", p);
+ printf("New listener: %s\n",p);
+ listindex++;
+ }
+ if(listindex==listmax) { // goal reached
+ printf("Sending ready signals to listeners:\n");
+ ///////////////////////////////////////////////
+ for(c=0;c<listmax;c++) {
+ char tmpaddr[256];
+ snprintf(tmpaddr,255, "tcp://%s:%s",listeners[c],PGMPORT);
+ printf (" %s\n", tmpaddr);
+ zmq_connect(sock_out, tmpaddr);
+ zmq_msg_init_size (&request, 5);
+ memcpy (zmq_msg_data (&request), "READY", 5);
+ zmq_send (sock_out, &request, 0);
+ zmq_msg_close (&request);
+ }
+ break; // break offer loop
+ } // if listmax reached
+
+ } // if ACK response
+
+ } // offer while loop
+
+ } else { // all other channels listen
+
+ /////////
+ // LISTEN
+
+
+ printf ("Listening on %s from address %s\n",pgmaddr, IPv4);
+
+ // listen subscribes only to offers and ready
+ zmq_setsockopt(sock_in,ZMQ_SUBSCRIBE,"OFFER",5);
+ zmq_setsockopt(sock_in,ZMQ_SUBSCRIBE,"READY",5);
+
+
+ snprintf(message,255,"ACK;%s;",IPv4);
+
+ while (1) {
+ // Wait for next request from client
+ zmq_msg_init (&request);
+ zmq_recv (sock_in, &request, 0);
+
+ snprintf(res,127,"%s",zmq_msg_data(&request));
+ zmq_msg_close (&request);
+ if(strncmp(res,"READY",5) == 0) {
+ printf ("Received ready signal\n");
+ break; // quit loop on ready signal
+ }
+ sleep (1);
+
+ // Send reply back to client
+ zmq_msg_init_size (&reply, strlen(message));
+ memcpy (zmq_msg_data (&reply), message, strlen(message));
+ zmq_send (sock_out, &reply, 0);
+ zmq_msg_close (&reply);
+
+ }
+
+ }
+
+ if(sock_out) {
+ zmq_close (sock_out);
+ sock_out = NULL;
+ }
+ if(sock_in) {
+ zmq_close (sock_in);
+ sock_in = NULL;
+ }
+
+ return(1);
+}
+
+int main(int argc, char **argv) {
+ upnp_t *upnp;
+ int found;
+
+ cmdline(argc, argv);
+
+ if(!filename[0]) {
+ fprintf(stderr,"not enough args specified on commandline, see help.");
+ exit(ERR);
+ }
+
+ fprintf(stderr,"HDSync starting for channel %u of %u\n",chanID, chanTOT);
+ fprintf(stderr,"will sync and play video: %s\n", filename);
+
+ upnp = create_upnp();
+
+ // no server specified, force localhost
+ if(!server[0]) sprintf(server,"localhost");
+
+ // commandline or detection found explicit addresses
+ snprintf(upnp->hostname, MAX_HOSTNAME_SIZE-1,"%s",server);
+ upnp->port = port;
+
+ if(!dryrun) {
+ if ( connect_upnp (upnp) < 0 )
+ {
+ fprintf(stderr,"can't connect to %s:%u: operation aborted.\n", server, port);
+ free_upnp(upnp);
+ exit(ERR);
+ }
+ }
+
+ // initialize 0mq
+ zcontext = zmq_init (1);
+
+ if(! handshake()) {
+ // might never get here since while(1) tries endlessly
+ fprintf(stderr,"error establishing contact with all desired channels, aborting.\n");
+ exit(ERR);
+ }
+
+ //////////////////////
+ if(dryrun) { // DRYRUN
+ // testing sync with no real playback
+ sleep(1);
+
+ if(chanID==1) { // offer should send sync
+ sleep(3);
+ // create output socket
+ sock_out = zmq_socket (zcontext, ZMQ_PUB);
+ zmq_connect(sock_out, pgmaddr);
+ // broadcast offer
+ zmq_msg_init_size (&request, 4);
+ memcpy (zmq_msg_data (&request), "SYNC", 4);
+ // printf ("Sending offer...\n", request_nbr);
+ zmq_send (sock_out, &request, 0);
+ fprintf(stderr,"SYNC\n");
+ zmq_msg_close (&request);
+
+ } else { // listener should prepare to receive sync
+
+ // create input socket
+ sock_in = zmq_socket (zcontext, ZMQ_SUB);
+ zmq_bind (sock_in, pgmaddr);
+ zmq_setsockopt(sock_in,ZMQ_SUBSCRIBE,"",0);
+ // Wait for next request from client
+ zmq_msg_init (&request);
+ zmq_recv (sock_in, &request, 0);
+ if(strncmp((char*)zmq_msg_data(&request),"SYNC",4)==0) {
+ fprintf(stderr,"SYNC\n");
+ }
+ zmq_msg_close (&request);
+
+ }
+
+ //////////////////////
+ } else { // RUN FOR REAL
+
+
+ // Prepare
+
+ // load, play and pause in sequence
+ // break and reopen connection in between
+
+ // was connected already
+
+ // load
+ render_uri_meta(upnp,filename);
+ render_upnp(upnp,"SetAVTransportURI", upnp->meta);
+
+ // must re-connect socket between commands
+ send_upnp(upnp);
+ recv_upnp(upnp, 1000);
+ close(upnp->sockfd);
+ upnp->sockfd = 0;
+
+ connect_upnp(upnp);
+ render_upnp(upnp,"Play","<Speed>1</Speed>");
+
+ send_upnp(upnp);
+ recv_upnp(upnp, 1000);
+ close(upnp->sockfd);
+ upnp->sockfd = 0;
+
+ connect_upnp(upnp);
+ render_upnp(upnp,"Pause","");
+ send_upnp(upnp);
+ recv_upnp(upnp, 1000);
+
+ // START SYNC
+
+ // was connected already
+ render_upnp(upnp,"Play","<Speed>1</Speed>");
+ send_upnp(upnp);
+ recv_upnp(upnp, 1000);
+
+ /*
+ case 'g': // dump a parsable full state of the device
+ render_upnp(upnp,"GetTransportInfo","");
+ parser = GetTransportInfo;
+
+ break;
+
+ case 'm': // set the playmode:
+ // "NORMAL", "REPEAT_ONE", "REPEAT_ALL", "RANDOM"
+ {
+ char tmp[256];
+ snprintf(tmp,255,"<NewPlayMode>%s</NewPlayMode>",filename);
+ render_upnp(upnp,"SetPlayMode",tmp);
+ }
+ break;
+
+ case 'j': // jump aka seek
+ // <SeekMode> and <SeekTarget>
+ {
+ char tmp[512];
+ snprintf(tmp,511,"<Unit>REL_TIME</Unit><Target>%s</Target>",filename);
+ render_upnp(upnp,"Seek",tmp);
+ }
+ break;
+ */
+
+ free_upnp(upnp);
+ }
+
+ if(sock_out) {
+ zmq_close (sock_out);
+ sock_out = NULL;
+ }
+ if(sock_in) {
+ zmq_close (sock_in);
+ sock_in = NULL;
+ }
+
+ zmq_term (zcontext);
+
+ if(listeners) {
+ for(int c; c<chanTOT; c++)
+ free(listeners[c]);
+ free(listeners);
+ }
+
+
+ exit(0);
+}
diff --git a/src/parsers.c b/src/parsers.cpp
index 0063c6f..0063c6f 100644
--- a/src/parsers.c
+++ b/src/parsers.cpp