Index: libs/libmythhdhomerun/hdhomerun.h
===================================================================
--- libs/libmythhdhomerun/hdhomerun.h	(revision 23413)
+++ libs/libmythhdhomerun/hdhomerun.h	(working copy)
@@ -3,7 +3,7 @@
 /*
  * hdhomerun.h
  *
- * Copyright © 2006-2008 Silicondust USA Inc. <www.silicondust.com>.
+ * Copyright © 2006-2010 Silicondust USA Inc. <www.silicondust.com>.
  *
  * This library is free software; you can redistribute it and/or 
  * modify it under the terms of the GNU Lesser General Public
@@ -35,6 +35,7 @@
 #include "hdhomerun_os.h"
 #include "hdhomerun_types.h"
 #include "hdhomerun_pkt.h"
+#include "hdhomerun_sock.h"
 #include "hdhomerun_debug.h"
 #include "hdhomerun_discover.h"
 #include "hdhomerun_control.h"
Index: libs/libmythhdhomerun/hdhomerun_debug.c
===================================================================
--- libs/libmythhdhomerun/hdhomerun_debug.c	(revision 23413)
+++ libs/libmythhdhomerun/hdhomerun_debug.c	(working copy)
@@ -1,7 +1,7 @@
 /*
  * hdhomerun_debug.c
  *
- * Copyright © 2006 Silicondust USA Inc. <www.silicondust.com>.
+ * Copyright © 2006-2010 Silicondust USA Inc. <www.silicondust.com>.
  *
  * This library is free software; you can redistribute it and/or 
  * modify it under the terms of the GNU Lesser General Public
@@ -44,9 +44,13 @@
 #define HDHOMERUN_DEBUG_HOST "debug.silicondust.com"
 #endif
 #if !defined(HDHOMERUN_DEBUG_PORT)
-#define HDHOMERUN_DEBUG_PORT "8002"
+#define HDHOMERUN_DEBUG_PORT 8002
 #endif
 
+#define HDHOMERUN_DEBUG_CONNECT_RETRY_TIME 30000
+#define HDHOMERUN_DEBUG_CONNECT_TIMEOUT 10000
+#define HDHOMERUN_DEBUG_SEND_TIMEOUT 10000
+
 struct hdhomerun_debug_message_t
 {
 	struct hdhomerun_debug_message_t *next;
@@ -73,7 +77,7 @@
 
 	char *file_name;
 	FILE *file_fp;
-	int sock;
+	hdhomerun_sock_t sock;
 };
 
 static THREAD_FUNC_PREFIX hdhomerun_debug_thread_execute(void *arg);
@@ -85,7 +89,7 @@
 		return NULL;
 	}
 
-	dbg->sock = -1;
+	dbg->sock = HDHOMERUN_SOCK_INVALID;
 
 	pthread_mutex_init(&dbg->print_lock, NULL);
 	pthread_mutex_init(&dbg->queue_lock, NULL);
@@ -117,8 +121,8 @@
 	if (dbg->file_fp) {
 		fclose(dbg->file_fp);
 	}
-	if (dbg->sock != -1) {
-		close(dbg->sock);
+	if (dbg->sock != HDHOMERUN_SOCK_INVALID) {
+		hdhomerun_sock_destroy(dbg->sock);
 	}
 
 	free(dbg);
@@ -132,9 +136,9 @@
 		dbg->file_fp = NULL;
 	}
 
-	if (dbg->sock != -1) {
-		close(dbg->sock);
-		dbg->sock = -1;
+	if (dbg->sock != HDHOMERUN_SOCK_INVALID) {
+		hdhomerun_sock_destroy(dbg->sock);
+		dbg->sock = HDHOMERUN_SOCK_INVALID;
 	}
 }
 
@@ -251,7 +255,7 @@
 			return;
 		}
 
-		msleep(10);
+		msleep_approx(10);
 	}
 }
 
@@ -372,54 +376,40 @@
 }
 
 /* Send lock held by caller */
-#if defined(__CYGWIN__)
 static bool_t hdhomerun_debug_output_message_sock(struct hdhomerun_debug_t *dbg, struct hdhomerun_debug_message_t *message)
 {
-	return TRUE;
-}
-#else
-static bool_t hdhomerun_debug_output_message_sock(struct hdhomerun_debug_t *dbg, struct hdhomerun_debug_message_t *message)
-{
-	if (dbg->sock == -1) {
+	if (dbg->sock == HDHOMERUN_SOCK_INVALID) {
 		uint64_t current_time = getcurrenttime();
 		if (current_time < dbg->connect_delay) {
 			return FALSE;
 		}
-		dbg->connect_delay = current_time + 30*1000;
+		dbg->connect_delay = current_time + HDHOMERUN_DEBUG_CONNECT_RETRY_TIME;
 
-		dbg->sock = (int)socket(AF_INET, SOCK_STREAM, 0);
-		if (dbg->sock == -1) {
+		dbg->sock = hdhomerun_sock_create_tcp();
+		if (dbg->sock == HDHOMERUN_SOCK_INVALID) {
 			return FALSE;
 		}
 
-		struct addrinfo hints;
-		memset(&hints, 0, sizeof(hints));
-		hints.ai_family = AF_INET;
-		hints.ai_socktype = SOCK_STREAM;
-		hints.ai_protocol = IPPROTO_TCP;
-
-		struct addrinfo *sock_info;
-		if (getaddrinfo(HDHOMERUN_DEBUG_HOST, HDHOMERUN_DEBUG_PORT, &hints, &sock_info) != 0) {
+		uint32_t remote_addr = hdhomerun_sock_getaddrinfo_addr(dbg->sock, HDHOMERUN_DEBUG_HOST);
+		if (remote_addr == 0) {
 			hdhomerun_debug_close_internal(dbg);
 			return FALSE;
 		}
-		if (connect(dbg->sock, sock_info->ai_addr, (int)sock_info->ai_addrlen) != 0) {
-			freeaddrinfo(sock_info);
+
+		if (!hdhomerun_sock_connect(dbg->sock, remote_addr, HDHOMERUN_DEBUG_PORT, HDHOMERUN_DEBUG_CONNECT_TIMEOUT)) {
 			hdhomerun_debug_close_internal(dbg);
 			return FALSE;
 		}
-		freeaddrinfo(sock_info);
 	}
 
 	size_t length = strlen(message->buffer);
-	if (send(dbg->sock, (char *)message->buffer, (int)length, 0) != length) {
+	if (!hdhomerun_sock_send(dbg->sock, message->buffer, length, HDHOMERUN_DEBUG_SEND_TIMEOUT)) {
 		hdhomerun_debug_close_internal(dbg);
 		return FALSE;
 	}
 
 	return TRUE;
 }
-#endif
 
 static bool_t hdhomerun_debug_output_message(struct hdhomerun_debug_t *dbg, struct hdhomerun_debug_message_t *message)
 {
@@ -466,7 +456,7 @@
 		pthread_mutex_unlock(&dbg->queue_lock);
 
 		if (!message) {
-			msleep(250);
+			msleep_approx(250);
 			continue;
 		}
 
@@ -476,7 +466,7 @@
 		}
 
 		if (!hdhomerun_debug_output_message(dbg, message)) {
-			msleep(250);
+			msleep_approx(250);
 			continue;
 		}
 
Index: libs/libmythhdhomerun/libmythhdhomerun.pro
===================================================================
--- libs/libmythhdhomerun/libmythhdhomerun.pro	(revision 23413)
+++ libs/libmythhdhomerun/libmythhdhomerun.pro	(working copy)
@@ -10,20 +10,29 @@
 
 QMAKE_CLEAN += $(TARGET) $(TARGETA) $(TARGETD) $(TARGET0) $(TARGET1) $(TARGET2)
 
-HEADERS += hdhomerun.h  hdhomerun_os.h  hdhomerun_types.h
+HEADERS += hdhomerun.h  hdhomerun_os.h  hdhomerun_sock.h  hdhomerun_types.h
 
 HEADERS += hdhomerun_channels.h  hdhomerun_channelscan.h  hdhomerun_control.h
-HEADERS += hdhomerun_debug.h     hdhomerun_device.h       hdhomerun_dhcp.h
-HEADERS += hdhomerun_discover.h  hdhomerun_pkt.h          hdhomerun_video.h 
+HEADERS += hdhomerun_debug.h     hdhomerun_device.h
 HEADERS += hdhomerun_device_selector.h
+HEADERS += hdhomerun_discover.h  hdhomerun_os_posix.h
+HEADERS += hdhomerun_pkt.h       hdhomerun_video.h 
 
+SOURCES += hdhomerun_config.c
+
 SOURCES += hdhomerun_channels.c  hdhomerun_channelscan.c  hdhomerun_control.c
-SOURCES += hdhomerun_debug.c     hdhomerun_device.c       hdhomerun_dhcp.c
-SOURCES += hdhomerun_discover.c  hdhomerun_pkt.c          hdhomerun_video.c
+SOURCES += hdhomerun_debug.c     hdhomerun_device.c
 SOURCES += hdhomerun_device_selector.c
+SOURCES += hdhomerun_discover.c  hdhomerun_os_posix.c
+SOURCES += hdhomerun_pkt.c       hdhomerun_video.c
 
+
+unix:SOURCES += hdhomerun_sock_posix.c
+
 mingw {
     HEADERS += hdhomerun_os_windows.h
+    SOURCES += hdhomerun_os_windows.c
+    SOURCES += hdhomerun_sock_windows.c
     LIBS += -lws2_32 -liphlpapi -lpthread
 }
 
Index: libs/libmythhdhomerun/hdhomerun_config.c
===================================================================
--- libs/libmythhdhomerun/hdhomerun_config.c	(revision 23413)
+++ libs/libmythhdhomerun/hdhomerun_config.c	(working copy)
@@ -286,7 +286,7 @@
 		}
 
 		cmd_scan_printf(fp, "SCANNING: %lu (%s)\n",
-			result.frequency, result.channel_str
+			(unsigned long)result.frequency, result.channel_str
 		);
 
 		ret = hdhomerun_device_channelscan_detect(hd, &result);
@@ -364,7 +364,7 @@
 		size_t actual_size;
 		uint8_t *ptr = hdhomerun_device_stream_recv(hd, VIDEO_DATA_BUFFER_SIZE_1S, &actual_size);
 		if (!ptr) {
-			msleep(64);
+			msleep_approx(64);
 			continue;
 		}
 
@@ -381,6 +381,12 @@
 				next_progress = loop_start_time + 1000;
 			}
 
+			/* Windows - indicate activity to suppress auto sleep mode. */
+			#if defined(__WINDOWS__)
+			SetThreadExecutionState(ES_SYSTEM_REQUIRED);
+			#endif
+
+			/* Video stats. */
 			hdhomerun_device_get_video_stats(hd, &stats_cur);
 
 			if (stats_cur.overflow_error_count > stats_old.overflow_error_count) {
@@ -404,7 +410,7 @@
 			continue;
 		}
 
-		msleep(delay);
+		msleep_approx(delay);
 	}
 
 	if (fp) {
@@ -440,10 +446,10 @@
 		fclose(fp);
 		return -1;
 	}
-	sleep(2);
+	msleep_minimum(2000);
 
 	printf("upgrading firmware...\n");
-	sleep(8);
+	msleep_minimum(8000);
 
 	printf("rebooting...\n");
 	int count = 0;
@@ -460,7 +466,7 @@
 			return -1;
 		}
 
-		sleep(1);
+		msleep_minimum(1000);
 	}
 
 	printf("upgrade complete - now running firmware %s\n", version_str);
Index: libs/libmythhdhomerun/hdhomerun_video.c
===================================================================
--- libs/libmythhdhomerun/hdhomerun_video.c	(revision 23413)
+++ libs/libmythhdhomerun/hdhomerun_video.c	(working copy)
@@ -34,21 +34,27 @@
 
 struct hdhomerun_video_sock_t {
 	pthread_mutex_t lock;
+	struct hdhomerun_debug_t *dbg;
+
+	int sock;
+	uint32_t multicast_ip;
+
+	volatile size_t head;
+	volatile size_t tail;
 	uint8_t *buffer;
 	size_t buffer_size;
-	volatile size_t head;
-	volatile size_t tail;
 	size_t advance;
+
+	pthread_t thread;
 	volatile bool_t terminate;
-	pthread_t thread;
-	int sock;
-	uint32_t rtp_sequence;
-	struct hdhomerun_debug_t *dbg;
+
 	volatile uint32_t packet_count;
 	volatile uint32_t transport_error_count;
 	volatile uint32_t network_error_count;
 	volatile uint32_t sequence_error_count;
 	volatile uint32_t overflow_error_count;
+
+	volatile uint32_t rtp_sequence;
 	volatile uint8_t sequence[0x2000];
 };
 
@@ -64,7 +70,7 @@
 	}
 
 	vs->dbg = dbg;
-	vs->sock = -1;
+	vs->sock = HDHOMERUN_SOCK_INVALID;
 	pthread_mutex_init(&vs->lock, NULL);
 
 	/* Reset sequence tracking. */
@@ -86,8 +92,8 @@
 	}
 	
 	/* Create socket. */
-	vs->sock = (int)socket(AF_INET, SOCK_DGRAM, 0);
-	if (vs->sock == -1) {
+	vs->sock = hdhomerun_sock_create_udp();
+	if (vs->sock == HDHOMERUN_SOCK_INVALID) {
 		hdhomerun_debug_printf(dbg, "hdhomerun_video_create: failed to allocate socket\n");
 		goto error;
 	}
@@ -96,17 +102,8 @@
 	int rx_size = 1024 * 1024;
 	setsockopt(vs->sock, SOL_SOCKET, SO_RCVBUF, (char *)&rx_size, sizeof(rx_size));
 
-	/* Set timeouts. */
-	setsocktimeout(vs->sock, SOL_SOCKET, SO_SNDTIMEO, 1000);
-	setsocktimeout(vs->sock, SOL_SOCKET, SO_RCVTIMEO, 1000);
-
 	/* Bind socket. */
-	struct sockaddr_in sock_addr;
-	memset(&sock_addr, 0, sizeof(sock_addr));
-	sock_addr.sin_family = AF_INET;
-	sock_addr.sin_addr.s_addr = htonl(INADDR_ANY);
-	sock_addr.sin_port = htons(listen_port);
-	if (bind(vs->sock, (struct sockaddr *)&sock_addr, sizeof(sock_addr)) != 0) {
+	if (!hdhomerun_sock_bind(vs->sock, INADDR_ANY, listen_port)) {
 		hdhomerun_debug_printf(dbg, "hdhomerun_video_create: failed to bind socket (port %u)\n", listen_port);
 		goto error;
 	}
@@ -121,8 +118,8 @@
 	return vs;
 
 error:
-	if (vs->sock != -1) {
-		close(vs->sock);
+	if (vs->sock != HDHOMERUN_SOCK_INVALID) {
+		hdhomerun_sock_destroy(vs->sock);
 	}
 	if (vs->buffer) {
 		free(vs->buffer);
@@ -136,7 +133,7 @@
 	vs->terminate = TRUE;
 	pthread_join(vs->thread, NULL);
 
-	close(vs->sock);
+	hdhomerun_sock_destroy(vs->sock);
 	free(vs->buffer);
 
 	free(vs);
@@ -144,16 +141,54 @@
 
 uint16_t hdhomerun_video_get_local_port(struct hdhomerun_video_sock_t *vs)
 {
-	struct sockaddr_in sock_addr;
-	socklen_t sockaddr_size = sizeof(sock_addr);
-	if (getsockname(vs->sock, (struct sockaddr*)&sock_addr, &sockaddr_size) != 0) {
-		hdhomerun_debug_printf(vs->dbg, "hdhomerun_video_get_local_port: getsockname failed (%d)\n", sock_getlasterror);
+	uint16_t port = hdhomerun_sock_getsockname_port(vs->sock);
+	if (port == 0) {
+		hdhomerun_debug_printf(vs->dbg, "hdhomerun_video_get_local_port: getsockname failed (%d)\n", hdhomerun_sock_getlasterror());
 		return 0;
 	}
 
-	return ntohs(sock_addr.sin_port);
+	return port;
 }
 
+int hdhomerun_video_join_multicast_group(struct hdhomerun_video_sock_t *vs, uint32_t multicast_ip, uint32_t local_ip)
+{
+	if (vs->multicast_ip != 0) {
+		hdhomerun_video_leave_multicast_group(vs);
+	}
+
+	struct ip_mreq imr;
+	memset(&imr, 0, sizeof(imr));
+	imr.imr_multiaddr.s_addr  = htonl(multicast_ip);
+	imr.imr_interface.s_addr  = htonl(local_ip);
+
+	if (setsockopt(vs->sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, (const char *)&imr, sizeof(imr)) != 0) {
+		hdhomerun_debug_printf(vs->dbg, "hdhomerun_video_join_multicast_group: setsockopt failed (%d)\n", hdhomerun_sock_getlasterror());
+		return -1;
+	}
+
+	vs->multicast_ip = multicast_ip;
+	return 1;
+}
+
+int hdhomerun_video_leave_multicast_group(struct hdhomerun_video_sock_t *vs)
+{
+	if (vs->multicast_ip == 0) {
+		return 1;
+	}
+
+	struct ip_mreq imr;
+	memset(&imr, 0, sizeof(imr));
+	imr.imr_multiaddr.s_addr  = htonl(vs->multicast_ip);
+	imr.imr_interface.s_addr  = htonl(INADDR_ANY);
+
+	if (setsockopt(vs->sock, IPPROTO_IP, IP_DROP_MEMBERSHIP, (const char *)&imr, sizeof(imr)) != 0) {
+		hdhomerun_debug_printf(vs->dbg, "hdhomerun_video_leave_multicast_group: setsockopt failed (%d)\n", hdhomerun_sock_getlasterror());
+	}
+
+	vs->multicast_ip = 0;
+	return 1;
+}
+
 static void hdhomerun_video_stats_ts_pkt(struct hdhomerun_video_sock_t *vs, uint8_t *ptr)
 {
 	uint16_t packet_identifier = ((uint16_t)(ptr[1] & 0x1F) << 8) | (uint16_t)ptr[2];
@@ -168,23 +203,22 @@
 		return;
 	}
 
-	uint8_t continuity_counter = ptr[3] & 0x0F;
+	uint8_t sequence = ptr[3] & 0x0F;
+
 	uint8_t previous_sequence = vs->sequence[packet_identifier];
+	vs->sequence[packet_identifier] = sequence;
 
-	if (continuity_counter == ((previous_sequence + 1) & 0x0F)) {
-		vs->sequence[packet_identifier] = continuity_counter;
+	if (previous_sequence == 0xFF) {
 		return;
 	}
-	if (previous_sequence == 0xFF) {
-		vs->sequence[packet_identifier] = continuity_counter;
+	if (sequence == ((previous_sequence + 1) & 0x0F)) {
 		return;
 	}
-	if (continuity_counter == previous_sequence) {
+	if (sequence == previous_sequence) {
 		return;
 	}
 
 	vs->sequence_error_count++;
-	vs->sequence[packet_identifier] = continuity_counter;
 }
 
 static void hdhomerun_video_parse_rtp(struct hdhomerun_video_sock_t *vs, struct hdhomerun_pkt_t *pkt)
@@ -193,19 +227,27 @@
 	uint32_t rtp_sequence = hdhomerun_pkt_read_u16(pkt);
 	pkt->pos += 8;
 
-	if (rtp_sequence != ((vs->rtp_sequence + 1) & 0xFFFF)) {
-		if (vs->rtp_sequence != 0xFFFFFFFF) {
-			vs->network_error_count++;
+	uint32_t previous_rtp_sequence = vs->rtp_sequence;
+	vs->rtp_sequence = rtp_sequence;
 
-			/* restart pid sequence check */
-			/* can't use memset bcs sequence is volatile */
-			int i;
-			for (i = 0; i < sizeof(vs->sequence) / sizeof(uint8_t) ; i++)
-				vs->sequence[i] = 0xFF;
-		}
+	/* Initial case - first packet received. */
+	if (previous_rtp_sequence == 0xFFFFFFFF) {
+		return;
 	}
 
-	vs->rtp_sequence = rtp_sequence;
+	/* Normal case - next sequence number. */
+	if (rtp_sequence == ((previous_rtp_sequence + 1) & 0xFFFF)) {
+		return;
+	}
+
+	/* Error case - sequence missed. */
+	vs->network_error_count++;
+
+	/* Restart pid sequence check after packet loss. */
+	int i;
+	for (i = 0; i < 0x2000; i++) {
+		vs->sequence[i] = 0xFF;
+	}
 }
 
 static THREAD_FUNC_PREFIX hdhomerun_video_thread_execute(void *arg)
@@ -218,7 +260,11 @@
 		hdhomerun_pkt_reset(pkt);
 
 		/* Receive. */
-		int length = recv(vs->sock, (char *)pkt->end, VIDEO_RTP_DATA_PACKET_SIZE, 0);
+		size_t length = VIDEO_RTP_DATA_PACKET_SIZE;
+		if (!hdhomerun_sock_recv(vs->sock, pkt->end, &length, 25)) {
+			continue;
+		}
+
 		pkt->end += length;
 
 		if (length == VIDEO_RTP_DATA_PACKET_SIZE) {
@@ -227,16 +273,8 @@
 		}
 
 		if (length != VIDEO_DATA_PACKET_SIZE) {
-			if (length > 0) {
-				/* Data received but not valid - ignore. */
-				continue;
-			}
-			if (sock_getlasterror_socktimeout) {
-				/* Wait for more data. */
-				continue;
-			}
-			vs->terminate = TRUE;
-			return NULL;
+			/* Data received but not valid - ignore. */
+			continue;
 		}
 
 		pthread_mutex_lock(&vs->lock);
@@ -269,7 +307,6 @@
 			continue;
 		}
 
-		/* Atomic update. */
 		vs->head = head;
 
 		pthread_mutex_unlock(&vs->lock);
@@ -291,7 +328,6 @@
 			tail -= vs->buffer_size;
 		}
 	
-		/* Atomic update. */
 		vs->tail = tail;
 	}
 
@@ -334,13 +370,12 @@
 	vs->tail = vs->head;
 	vs->advance = 0;
 
-	/* can't use memset bcs sequence is volatile */
+	vs->rtp_sequence = 0xFFFFFFFF;
 	int i;
-	for (i = 0; i < sizeof(vs->sequence) / sizeof(uint8_t) ; i++)
+	for (i = 0; i < 0x2000; i++) {
 		vs->sequence[i] = 0xFF;
+	}
 
-	vs->rtp_sequence = 0xFFFFFFFF;
-
 	vs->packet_count = 0;
 	vs->transport_error_count = 0;
 	vs->network_error_count = 0;
@@ -355,10 +390,10 @@
 	struct hdhomerun_video_stats_t stats;
 	hdhomerun_video_get_stats(vs, &stats);
 
-	hdhomerun_debug_printf(vs->dbg, "video sock: pkt=%ld net=%ld te=%ld miss=%ld drop=%ld\n",
-		stats.packet_count, stats.network_error_count,
-		stats.transport_error_count, stats.sequence_error_count,
-		stats.overflow_error_count
+	hdhomerun_debug_printf(vs->dbg, "video sock: pkt=%lu net=%lu te=%lu miss=%lu drop=%lu\n",
+		(unsigned long)stats.packet_count, (unsigned long)stats.network_error_count,
+		(unsigned long)stats.transport_error_count, (unsigned long)stats.sequence_error_count,
+		(unsigned long)stats.overflow_error_count
 	);
 }
 
Index: libs/libmythhdhomerun/hdhomerun_os_posix.h
===================================================================
--- libs/libmythhdhomerun/hdhomerun_os_posix.h	(revision 23413)
+++ libs/libmythhdhomerun/hdhomerun_os_posix.h	(working copy)
@@ -1,7 +1,7 @@
 /*
  * hdhomerun_os_posix.h
  *
- * Copyright © 2006-2008 Silicondust USA Inc. <www.silicondust.com>.
+ * Copyright © 2006-2010 Silicondust USA Inc. <www.silicondust.com>.
  *
  * This library is free software; you can redistribute it and/or 
  * modify it under the terms of the GNU Lesser General Public
@@ -52,44 +52,18 @@
 typedef int bool_t;
 
 #define LIBTYPE
-#define sock_getlasterror errno
-#define sock_getlasterror_socktimeout (errno == EAGAIN)
 #define console_vprintf vprintf
 #define console_printf printf
 #define THREAD_FUNC_PREFIX void *
 
-static inline uint64_t getcurrenttime(void)
-{
-	struct timeval t;
-	gettimeofday(&t, NULL);
-	return ((uint64_t)t.tv_sec * 1000) + (t.tv_usec / 1000);
-}
+#ifdef __cplusplus
+extern "C" {
+#endif
 
-static inline int msleep(unsigned int ms)
-{
-	uint64_t stop_time = getcurrenttime() + ms;
+extern LIBTYPE uint64_t getcurrenttime(void);
+extern LIBTYPE void msleep_approx(uint64_t ms);
+extern LIBTYPE void msleep_minimum(uint64_t ms);
 
-	while (1) {
-		uint64_t current_time = getcurrenttime();
-		if (current_time >= stop_time) {
-			return 0;
-		}
-
-		uint64_t delay_s = (stop_time - current_time) / 1000;
-		if (delay_s > 0) {
-			sleep((unsigned int)delay_s);
-			continue;
-		}
-
-		uint64_t delay_us = (stop_time - current_time) * 1000;
-		usleep((unsigned int)delay_us);
-	}
+#ifdef __cplusplus
 }
-
-static inline int setsocktimeout(int s, int level, int optname, uint64_t timeout)
-{
-	struct timeval t;
-	t.tv_sec = timeout / 1000;
-	t.tv_usec = (timeout % 1000) * 1000;
-	return setsockopt(s, level, optname, (char *)&t, sizeof(t));
-}
+#endif
Index: libs/libmythhdhomerun/hdhomerun_video.h
===================================================================
--- libs/libmythhdhomerun/hdhomerun_video.h	(revision 23413)
+++ libs/libmythhdhomerun/hdhomerun_video.h	(working copy)
@@ -71,6 +71,12 @@
 extern LIBTYPE uint16_t hdhomerun_video_get_local_port(struct hdhomerun_video_sock_t *vs);
 
 /*
+ * Join/leave multicast group.
+ */
+extern LIBTYPE int hdhomerun_video_join_multicast_group(struct hdhomerun_video_sock_t *vs, uint32_t multicast_ip, uint32_t local_ip);
+extern LIBTYPE int hdhomerun_video_leave_multicast_group(struct hdhomerun_video_sock_t *vs);
+
+/*
  * Read data from buffer.
  *
  * size_t max_size: The maximum amount of data to be returned.
Index: libs/libmythhdhomerun/hdhomerun_channelscan.c
===================================================================
--- libs/libmythhdhomerun/hdhomerun_channelscan.c	(revision 23413)
+++ libs/libmythhdhomerun/hdhomerun_channelscan.c	(working copy)
@@ -1,7 +1,7 @@
 /*
  * hdhomerun_channelscan.c
  *
- * Copyright © 2007-2008 Silicondust USA Inc. <www.silicondust.com>.
+ * Copyright © 2007-2010 Silicondust USA Inc. <www.silicondust.com>.
  *
  * This library is free software; you can redistribute it and/or 
  * modify it under the terms of the GNU Lesser General Public
@@ -99,7 +99,7 @@
 			return 1;
 		}
 
-		msleep(250);
+		msleep_approx(250);
 	}
 }
 
@@ -303,7 +303,7 @@
 			break;
 		}
 
-		msleep(250);
+		msleep_approx(250);
 	}
 
 	/* Lock => skip overlapping channels. */
Index: libs/libmythhdhomerun/hdhomerun_discover.c
===================================================================
--- libs/libmythhdhomerun/hdhomerun_discover.c	(revision 23413)
+++ libs/libmythhdhomerun/hdhomerun_discover.c	(working copy)
@@ -1,7 +1,7 @@
 /*
  * hdhomerun_discover.c
  *
- * Copyright © 2006-2007 Silicondust USA Inc. <www.silicondust.com>.
+ * Copyright © 2006-2010 Silicondust USA Inc. <www.silicondust.com>.
  *
  * This library is free software; you can redistribute it and/or 
  * modify it under the terms of the GNU Lesser General Public
@@ -44,17 +44,11 @@
 #endif
 #endif
 
-#include <sys/param.h>  // Defines BSD on FreeBSD and Mac OS X
-#if defined(__linux__) || defined(__APPLE__) || defined(BSD)
-#  include <ifaddrs.h>
-#  define USE_IFADDRS 1
-#  include <sys/select.h>
-#endif
-
 #define HDHOMERUN_DISOCVER_MAX_SOCK_COUNT 16
 
 struct hdhomerun_discover_sock_t {
-	int sock;
+	hdhomerun_sock_t sock;
+	bool_t detected;
 	uint32_t local_ip;
 	uint32_t subnet_mask;
 };
@@ -66,48 +60,74 @@
 	struct hdhomerun_pkt_t rx_pkt;
 };
 
-static bool_t hdhomerun_discover_sock_create(struct hdhomerun_discover_t *ds, uint32_t local_ip, uint32_t subnet_mask)
+static bool_t hdhomerun_discover_sock_add(struct hdhomerun_discover_t *ds, uint32_t local_ip, uint32_t subnet_mask)
 {
+	unsigned int i;
+	for (i = 1; i < ds->sock_count; i++) {
+		struct hdhomerun_discover_sock_t *dss = &ds->socks[i];
+
+		if ((dss->local_ip == local_ip) && (dss->subnet_mask == subnet_mask)) {
+			dss->detected = TRUE;
+			return TRUE;
+		}
+	}
+
 	if (ds->sock_count >= HDHOMERUN_DISOCVER_MAX_SOCK_COUNT) {
 		return FALSE;
 	}
 
 	/* Create socket. */
-	int sock = (int)socket(AF_INET, SOCK_DGRAM, 0);
-	if (sock == -1) {
+	hdhomerun_sock_t sock = hdhomerun_sock_create_udp();
+	if (sock == HDHOMERUN_SOCK_INVALID) {
 		return FALSE;
 	}
 
-	/* Set timeouts. */
-	setsocktimeout(sock, SOL_SOCKET, SO_SNDTIMEO, 1000);
-	setsocktimeout(sock, SOL_SOCKET, SO_RCVTIMEO, 1000);
-
-	/* Allow broadcast. */
-	int sock_opt = 1;
-	setsockopt(sock, SOL_SOCKET, SO_BROADCAST, (char *)&sock_opt, sizeof(sock_opt));
-
 	/* Bind socket. */
-	struct sockaddr_in sock_addr;
-	memset(&sock_addr, 0, sizeof(sock_addr));
-	sock_addr.sin_family = AF_INET;
-	sock_addr.sin_addr.s_addr = htonl(local_ip);
-	sock_addr.sin_port = htons(0);
-	if (bind(sock, (struct sockaddr *)&sock_addr, sizeof(sock_addr)) != 0) {
-		close(sock);
+	if (!hdhomerun_sock_bind(sock, local_ip, 0)) {
+		hdhomerun_sock_destroy(sock);
 		return FALSE;
 	}
 
 	/* Write sock entry. */
 	struct hdhomerun_discover_sock_t *dss = &ds->socks[ds->sock_count++];
 	dss->sock = sock;
+	dss->detected = TRUE;
 	dss->local_ip = local_ip;
 	dss->subnet_mask = subnet_mask;
 
 	return TRUE;
 }
 
+struct hdhomerun_discover_t *hdhomerun_discover_create(void)
+{
+	struct hdhomerun_discover_t *ds = (struct hdhomerun_discover_t *)calloc(1, sizeof(struct hdhomerun_discover_t));
+	if (!ds) {
+		return NULL;
+	}
+
+	/* Create a routable socket (always first entry). */
+	if (!hdhomerun_discover_sock_add(ds, 0, 0)) {
+		free(ds);
+		return NULL;
+	}
+
+	/* Success. */
+	return ds;
+}
+
+void hdhomerun_discover_destroy(struct hdhomerun_discover_t *ds)
+{
+	unsigned int i;
+	for (i = 0; i < ds->sock_count; i++) {
+		struct hdhomerun_discover_sock_t *dss = &ds->socks[i];
+		hdhomerun_sock_destroy(dss->sock);
+	}
+
+	free(ds);
+}
+
 #if defined(USE_IPHLPAPI)
-static void hdhomerun_discover_sock_detect(struct hdhomerun_discover_t *ds)
+static void hdhomerun_discover_sock_detect_internal(struct hdhomerun_discover_t *ds)
 {
 	PIP_ADAPTER_INFO pAdapterInfo = (IP_ADAPTER_INFO *)malloc(sizeof(IP_ADAPTER_INFO));
 	ULONG ulOutBufLen = sizeof(IP_ADAPTER_INFO);
@@ -138,7 +158,7 @@
 				continue;
 			}
 
-			hdhomerun_discover_sock_create(ds, local_ip, mask);
+			hdhomerun_discover_sock_add(ds, local_ip, mask);
 			pIPAddr = pIPAddr->Next;
 		}
 
@@ -150,12 +170,9 @@
 
 #else
 
-static void hdhomerun_discover_sock_detect(struct hdhomerun_discover_t *ds)
+static void hdhomerun_discover_sock_detect_internal(struct hdhomerun_discover_t *ds)
 {
-	int fd = socket(AF_INET, SOCK_DGRAM, 0);
-	if (fd == -1) {
-		return;
-	}
+	int sock = ds->socks[0].sock;
 
 	struct ifconf ifc;
 	uint8_t buf[8192];
@@ -164,8 +181,7 @@
 
 	memset(buf, 0, sizeof(buf));
 
-	if (ioctl(fd, SIOCGIFCONF, &ifc) != 0) {
-		close(fd);
+	if (ioctl(sock, SIOCGIFCONF, &ifc) != 0) {
 		return;
 	}
 
@@ -176,7 +192,7 @@
 		struct ifreq *ifr = (struct ifreq *)ptr;
 		ptr += _SIZEOF_ADDR_IFREQ(*ifr);
 
-		if (ioctl(fd, SIOCGIFADDR, ifr) != 0) {
+		if (ioctl(sock, SIOCGIFADDR, ifr) != 0) {
 			continue;
 		}
 		struct sockaddr_in *addr_in = (struct sockaddr_in *)&(ifr->ifr_addr);
@@ -185,48 +201,45 @@
 			continue;
 		}
 
-		if (ioctl(fd, SIOCGIFNETMASK, ifr) != 0) {
+		if (ioctl(sock, SIOCGIFNETMASK, ifr) != 0) {
 			continue;
 		}
 		struct sockaddr_in *mask_in = (struct sockaddr_in *)&(ifr->ifr_addr);
 		uint32_t mask = ntohl(mask_in->sin_addr.s_addr);
 
-		hdhomerun_discover_sock_create(ds, local_ip, mask);
+		hdhomerun_discover_sock_add(ds, local_ip, mask);
 	}
-
-	close(fd);
 }
 #endif
 
-static struct hdhomerun_discover_t *hdhomerun_discover_create(void)
+static void hdhomerun_discover_sock_detect(struct hdhomerun_discover_t *ds)
 {
-	struct hdhomerun_discover_t *ds = (struct hdhomerun_discover_t *)calloc(1, sizeof(struct hdhomerun_discover_t));
-	if (!ds) {
-		return NULL;
+	unsigned int i;
+	for (i = 1; i < ds->sock_count; i++) {
+		struct hdhomerun_discover_sock_t *dss = &ds->socks[i];
+		dss->detected = FALSE;
 	}
 
-	/* Create a routable socket. */
-	if (!hdhomerun_discover_sock_create(ds, 0, 0)) {
-		free(ds);
-		return NULL;
-	}
+	hdhomerun_discover_sock_detect_internal(ds);
 
-	/* Detect & create local sockets. */
-	hdhomerun_discover_sock_detect(ds);
-
-	/* Success. */
-	return ds;
-}
-
-static void hdhomerun_discover_destroy(struct hdhomerun_discover_t *ds)
-{
-	unsigned int i;
-	for (i = 0; i < ds->sock_count; i++) {
-		struct hdhomerun_discover_sock_t *dss = &ds->socks[i];
-		close(dss->sock);
+	struct hdhomerun_discover_sock_t *src = &ds->socks[1];
+	struct hdhomerun_discover_sock_t *dst = &ds->socks[1];
+	unsigned int count = 1;
+	for (i = 1; i < ds->sock_count; i++) {
+		if (!src->detected) {
+			hdhomerun_sock_destroy(src->sock);
+			src++;
+			continue;
+		}
+		if (dst != src) {
+			*dst = *src;
+		}
+		src++;
+		dst++;
+		count++;
 	}
 
-	free(ds);
+	ds->sock_count = count;
 }
 
 static bool_t hdhomerun_discover_send_internal(struct hdhomerun_discover_t *ds, struct hdhomerun_discover_sock_t *dss, uint32_t target_ip, uint32_t device_type, uint32_t device_id)
@@ -242,18 +255,7 @@
 	hdhomerun_pkt_write_u32(tx_pkt, device_id);
 	hdhomerun_pkt_seal_frame(tx_pkt, HDHOMERUN_TYPE_DISCOVER_REQ);
 
-	struct sockaddr_in sock_addr;
-	memset(&sock_addr, 0, sizeof(sock_addr));
-	sock_addr.sin_family = AF_INET;
-	sock_addr.sin_addr.s_addr = htonl(target_ip);
-	sock_addr.sin_port = htons(HDHOMERUN_DISCOVER_UDP_PORT);
-
-	int length = (int)(tx_pkt->end - tx_pkt->start);
-	if (sendto(dss->sock, (char *)tx_pkt->start, length, 0, (struct sockaddr *)&sock_addr, sizeof(sock_addr)) != length) {
-		return FALSE;
-	}
-
-	return TRUE;
+	return hdhomerun_sock_sendto(dss->sock, target_ip, HDHOMERUN_DISCOVER_UDP_PORT, tx_pkt->start, tx_pkt->end - tx_pkt->start, 0);
 }
 
 static bool_t hdhomerun_discover_send_wildcard_ip(struct hdhomerun_discover_t *ds, uint32_t device_type, uint32_t device_id)
@@ -313,38 +315,36 @@
 
 static bool_t hdhomerun_discover_send(struct hdhomerun_discover_t *ds, uint32_t target_ip, uint32_t device_type, uint32_t device_id)
 {
-	if (target_ip != 0) {
+	if (target_ip == 0) {
+		return hdhomerun_discover_send_wildcard_ip(ds, device_type, device_id);
+	} else {
 		return hdhomerun_discover_send_target_ip(ds, target_ip, device_type, device_id);
 	}
-
-	return hdhomerun_discover_send_wildcard_ip(ds, device_type, device_id);
 }
 
-static int hdhomerun_discover_recv_internal(struct hdhomerun_discover_t *ds, struct hdhomerun_discover_sock_t *dss, struct hdhomerun_discover_device_t *result)
+static bool_t hdhomerun_discover_recv_internal(struct hdhomerun_discover_t *ds, struct hdhomerun_discover_sock_t *dss, struct hdhomerun_discover_device_t *result)
 {
 	struct hdhomerun_pkt_t *rx_pkt = &ds->rx_pkt;
 	hdhomerun_pkt_reset(rx_pkt);
 
-	struct sockaddr_in sock_addr;
-	memset(&sock_addr, 0, sizeof(sock_addr));
-	socklen_t sockaddr_size = sizeof(sock_addr);
-
-	int rx_length = recvfrom(dss->sock, (char *)rx_pkt->end, (int)(rx_pkt->limit - rx_pkt->end), 0, (struct sockaddr *)&sock_addr, &sockaddr_size);
-	if (rx_length <= 0) {
-		/* Don't return error - windows machine on VPN can sometimes cause a sock error here but otherwise works. */
-		return 0;
+	uint32_t remote_addr;
+	uint16_t remote_port;
+	size_t length = rx_pkt->limit - rx_pkt->end;
+	if (!hdhomerun_sock_recvfrom(dss->sock, &remote_addr, &remote_port, rx_pkt->end, &length, 0)) {
+		return FALSE;
 	}
-	rx_pkt->end += rx_length;
 
+	rx_pkt->end += length;
+
 	uint16_t type;
 	if (hdhomerun_pkt_open_frame(rx_pkt, &type) <= 0) {
-		return 0;
+		return FALSE;
 	}
 	if (type != HDHOMERUN_TYPE_DISCOVER_RPY) {
-		return 0;
+		return FALSE;
 	}
 
-	result->ip_addr = ntohl(sock_addr.sin_addr.s_addr);
+	result->ip_addr = remote_addr;
 	result->device_type = 0;
 	result->device_id = 0;
 
@@ -378,63 +378,40 @@
 		rx_pkt->pos = next;
 	}
 
-	return 1;
+	return TRUE;
 }
 
-static int hdhomerun_discover_recv(struct hdhomerun_discover_t *ds, struct hdhomerun_discover_device_t *result)
+static bool_t hdhomerun_discover_recv(struct hdhomerun_discover_t *ds, struct hdhomerun_discover_device_t *result)
 {
-	struct timeval t;
-	t.tv_sec = 0;
-	t.tv_usec = 250000;
-
-	fd_set readfds;
-	FD_ZERO(&readfds);
-	int max_sock = -1;
-
 	unsigned int i;
 	for (i = 0; i < ds->sock_count; i++) {
 		struct hdhomerun_discover_sock_t *dss = &ds->socks[i];
-		FD_SET(dss->sock, &readfds);
-		if (dss->sock > max_sock) {
-			max_sock = dss->sock;
-		}
-	}
 
-	if (select(max_sock+1, &readfds, NULL, NULL, &t) < 0) {
-		return -1;
-	}
-
-	for (i = 0; i < ds->sock_count; i++) {
-		struct hdhomerun_discover_sock_t *dss = &ds->socks[i];
-		if (!FD_ISSET(dss->sock, &readfds)) {
-			continue;
+		if (hdhomerun_discover_recv_internal(ds, dss, result)) {
+			return TRUE;
 		}
-
-		if (hdhomerun_discover_recv_internal(ds, dss, result) <= 0) {
-			continue;
-		}
-
-		return 1;
 	}
 
-	return 0;
+	return FALSE;
 }
 
-static struct hdhomerun_discover_device_t *hdhomerun_discover_find_in_list(struct hdhomerun_discover_device_t result_list[], int count, uint32_t ip_addr)
+static struct hdhomerun_discover_device_t *hdhomerun_discover_find_in_list(struct hdhomerun_discover_device_t result_list[], int count, struct hdhomerun_discover_device_t *lookup)
 {
 	int index;
 	for (index = 0; index < count; index++) {
-		struct hdhomerun_discover_device_t *result = &result_list[index];
-		if (result->ip_addr == ip_addr) {
-			return result;
+		struct hdhomerun_discover_device_t *entry = &result_list[index];
+		if (memcmp(lookup, entry, sizeof(struct hdhomerun_discover_device_t)) == 0) {
+			return entry;
 		}
 	}
 
 	return NULL;
 }
 
-static int hdhomerun_discover_find_devices_internal(struct hdhomerun_discover_t *ds, uint32_t target_ip, uint32_t device_type, uint32_t device_id, struct hdhomerun_discover_device_t result_list[], int max_count)
+int hdhomerun_discover_find_devices(struct hdhomerun_discover_t *ds, uint32_t target_ip, uint32_t device_type, uint32_t device_id, struct hdhomerun_discover_device_t result_list[], int max_count)
 {
+	hdhomerun_discover_sock_detect(ds);
+
 	int count = 0;
 	int attempt;
 	for (attempt = 0; attempt < 2; attempt++) {
@@ -443,14 +420,14 @@
 		}
 
 		uint64_t timeout = getcurrenttime() + 200;
-		while (getcurrenttime() < timeout) {
+		while (1) {
 			struct hdhomerun_discover_device_t *result = &result_list[count];
 
-			int ret = hdhomerun_discover_recv(ds, result);
-			if (ret < 0) {
-				return -1;
-			}
-			if (ret == 0) {
+			if (!hdhomerun_discover_recv(ds, result)) {
+				if (getcurrenttime() >= timeout) {
+					break;
+				}
+				msleep_approx(10);
 				continue;
 			}
 
@@ -467,7 +444,7 @@
 			}
 
 			/* Ensure not already in list. */
-			if (hdhomerun_discover_find_in_list(result_list, count, result->ip_addr)) {
+			if (hdhomerun_discover_find_in_list(result_list, count, result)) {
 				continue;
 			}
 
@@ -484,12 +461,16 @@
 
 int hdhomerun_discover_find_devices_custom(uint32_t target_ip, uint32_t device_type, uint32_t device_id, struct hdhomerun_discover_device_t result_list[], int max_count)
 {
+	if (hdhomerun_discover_is_ip_multicast(target_ip)) {
+		return 0;
+	}
+
 	struct hdhomerun_discover_t *ds = hdhomerun_discover_create();
 	if (!ds) {
 		return -1;
 	}
 
-	int ret = hdhomerun_discover_find_devices_internal(ds, target_ip, device_type, device_id, result_list, max_count);
+	int ret = hdhomerun_discover_find_devices(ds, target_ip, device_type, device_id, result_list, max_count);
 
 	hdhomerun_discover_destroy(ds);
 	return ret;
@@ -513,3 +494,7 @@
 	return (checksum == 0);
 }
 
+bool_t hdhomerun_discover_is_ip_multicast(uint32_t ip_addr)
+{
+	return (ip_addr >= 0xE0000000) && (ip_addr < 0xF0000000);
+}
Index: libs/libmythhdhomerun/hdhomerun_discover.h
===================================================================
--- libs/libmythhdhomerun/hdhomerun_discover.h	(revision 23413)
+++ libs/libmythhdhomerun/hdhomerun_discover.h	(working copy)
@@ -44,9 +44,11 @@
  *
  * The device information is stored in caller-supplied array of hdhomerun_discover_device_t vars.
  * Multiple attempts are made to find devices.
- * Execution time is 1 second.
+ * Execution time is typically 400ms if max_count is not reached.
  *
  * Set target_ip to zero to auto-detect IP address.
+ * Set device_type to HDHOMERUN_DEVICE_TYPE_TUNER to detect HDHomeRun tuner devices.
+ * Set device_id to HDHOMERUN_DEVICE_ID_WILDCARD to detect all device ids.
  *
  * Returns the number of devices found.
  * Retruns -1 on error.
@@ -54,6 +56,13 @@
 extern LIBTYPE int hdhomerun_discover_find_devices_custom(uint32_t target_ip, uint32_t device_type, uint32_t device_id, struct hdhomerun_discover_device_t result_list[], int max_count);
 
 /*
+ * Optional: persistent discover instance available for discover polling use.
+ */
+extern LIBTYPE struct hdhomerun_discover_t *hdhomerun_discover_create(void);
+extern LIBTYPE void hdhomerun_discover_destroy(struct hdhomerun_discover_t *ds);
+extern LIBTYPE int hdhomerun_discover_find_devices(struct hdhomerun_discover_t *ds, uint32_t target_ip, uint32_t device_type, uint32_t device_id, struct hdhomerun_discover_device_t result_list[], int max_count);
+
+/*
  * Verify that the device ID given is valid.
  *
  * The device ID contains a self-check sequence that detects common user input errors including
@@ -64,6 +73,14 @@
  */
 extern LIBTYPE bool_t hdhomerun_discover_validate_device_id(uint32_t device_id);
 
+/*
+ * Detect if an IP address is multicast.
+ *
+ * Returns TRUE if multicast.
+ * Returns FALSE if zero, unicast, expermental, or broadcast.
+ */
+extern LIBTYPE bool_t hdhomerun_discover_is_ip_multicast(uint32_t ip_addr);
+
 #ifdef __cplusplus
 }
 #endif
Index: libs/libmythhdhomerun/Makefile.bin
===================================================================
--- libs/libmythhdhomerun/Makefile.bin	(revision 23413)
+++ libs/libmythhdhomerun/Makefile.bin	(working copy)
@@ -1,12 +1,15 @@
-LIBSRCS += hdhomerun_pkt.c
-LIBSRCS += hdhomerun_debug.c
-LIBSRCS += hdhomerun_discover.c
+
 LIBSRCS += hdhomerun_channels.c
 LIBSRCS += hdhomerun_channelscan.c
 LIBSRCS += hdhomerun_control.c
-LIBSRCS += hdhomerun_video.c
+LIBSRCS += hdhomerun_debug.c
 LIBSRCS += hdhomerun_device.c
 LIBSRCS += hdhomerun_device_selector.c
+LIBSRCS += hdhomerun_discover.c
+LIBSRCS += hdhomerun_os_posix.c
+LIBSRCS += hdhomerun_pkt.c
+LIBSRCS += hdhomerun_sock_posix.c
+LIBSRCS += hdhomerun_video.c
 
 CC    := $(CROSS_COMPILE)gcc
 STRIP := $(CROSS_COMPILE)strip
@@ -20,11 +23,15 @@
   LIBEXT := .dll
   LDFLAGS += -liphlpapi
 else
+  OS := $(shell uname -s)
   LIBEXT := .so
-  ifneq ($(findstring solaris,$(shell echo $$OSTYPE)),)
+  ifeq ($(OS),Linux)
+    LDFLAGS += -lrt
+  endif
+  ifeq ($(OS),SunOS)
     LDFLAGS += -lns -lsocket
   endif
-  ifneq ($(findstring darwin,$(shell echo $$OSTYPE)),)
+  ifeq ($(OS),Darwin)
     CFLAGS += -arch i386 -arch ppc
     LIBEXT := .dylib
     SHARED := -dynamiclib -install_name libhdhomerun$(LIBEXT)
Index: libs/libmythhdhomerun/hdhomerun_device_selector.c
===================================================================
--- libs/libmythhdhomerun/hdhomerun_device_selector.c	(revision 23413)
+++ libs/libmythhdhomerun/hdhomerun_device_selector.c	(working copy)
@@ -1,7 +1,7 @@
 /*
  * hdhomerun_device_selector.c
  *
- * Copyright Â© 2009 Silicondust USA Inc. <www.silicondust.com>.
+ * Copyright Â© 2009-2010 Silicondust USA Inc. <www.silicondust.com>.
  *
  * This library is free software; you can redistribute it and/or 
  * modify it under the terms of the GNU Lesser General Public
@@ -68,6 +68,11 @@
 	free(hds);
 }
 
+LIBTYPE int hdhomerun_device_selector_get_device_count(struct hdhomerun_device_selector_t *hds)
+{
+	return (int)hds->hd_count;
+}
+
 void hdhomerun_device_selector_add_device(struct hdhomerun_device_selector_t *hds, struct hdhomerun_device_t *hd)
 {
 	size_t index;
@@ -129,11 +134,11 @@
 	return NULL;
 }
 
-void hdhomerun_device_selector_load_from_file(struct hdhomerun_device_selector_t *hds, char *filename)
+int hdhomerun_device_selector_load_from_file(struct hdhomerun_device_selector_t *hds, char *filename)
 {
 	FILE *fp = fopen(filename, "r");
 	if (!fp) {
-		return;
+		return 0;
 	}
 
 	while(1) {
@@ -151,16 +156,17 @@
 	}
 
 	fclose(fp);
+	return (int)hds->hd_count;
 }
 
 #if defined(__WINDOWS__)
-void hdhomerun_device_selector_load_from_windows_registry(struct hdhomerun_device_selector_t *hds, wchar_t *wsource)
+int hdhomerun_device_selector_load_from_windows_registry(struct hdhomerun_device_selector_t *hds, wchar_t *wsource)
 {
 	HKEY tuners_key;
 	LONG ret = RegOpenKeyEx(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Silicondust\\HDHomeRun\\Tuners", 0, KEY_QUERY_VALUE | KEY_ENUMERATE_SUB_KEYS, &tuners_key);
 	if (ret != ERROR_SUCCESS) {
-		hdhomerun_debug_printf(hds->dbg, "hdhomerun_device_selector_load_from_windows_registry: failed to open tuners registry key (%ld)\n", ret);
-		return;
+		hdhomerun_debug_printf(hds->dbg, "hdhomerun_device_selector_load_from_windows_registry: failed to open tuners registry key (%ld)\n", (long)ret);
+		return 0;
 	}
 
 	DWORD index = 0;
@@ -177,7 +183,7 @@
 		HKEY device_key;
 		ret = RegOpenKeyEx(tuners_key, wdevice_name, 0, KEY_QUERY_VALUE, &device_key);
 		if (ret != ERROR_SUCCESS) {
-			hdhomerun_debug_printf(hds->dbg, "hdhomerun_device_selector_load_from_windows_registry: failed to open registry key for %S (%ld)\n", wdevice_name, ret);
+			hdhomerun_debug_printf(hds->dbg, "hdhomerun_device_selector_load_from_windows_registry: failed to open registry key for %S (%ld)\n", wdevice_name, (long)ret);
 			continue;
 		}
 
@@ -207,6 +213,7 @@
 	}
 
 	RegCloseKey(tuners_key);
+	return (int)hds->hd_count;
 }
 #endif
 
@@ -268,21 +275,16 @@
 	/*
 	 * Test local port.
 	 */
-	int test_sock = (int)socket(AF_INET, SOCK_DGRAM, 0);
-	if (test_sock == -1) {
+	hdhomerun_sock_t test_sock = hdhomerun_sock_create_udp();
+	if (test_sock == HDHOMERUN_SOCK_INVALID) {
 		hdhomerun_debug_printf(hds->dbg, "hdhomerun_device_selector_choose_test: device %s in use, failed to create test sock\n", name);
 		return FALSE;
 	}
 
-	struct sockaddr_in sock_addr;
-	memset(&sock_addr, 0, sizeof(sock_addr));
-	sock_addr.sin_family = AF_INET;
-	sock_addr.sin_addr.s_addr = htonl(INADDR_ANY);
-	sock_addr.sin_port = htons((uint16_t)target_port);
-	ret = bind(test_sock, (struct sockaddr *)&sock_addr, sizeof(sock_addr));
-	close(test_sock);
+	bool_t inuse = (hdhomerun_sock_bind(test_sock, INADDR_ANY, (uint16_t)target_port) == FALSE);
+	hdhomerun_sock_destroy(test_sock);
 
-	if (ret != 0) {
+	if (inuse) {
 		hdhomerun_debug_printf(hds->dbg, "hdhomerun_device_selector_choose_test: device %s in use by local machine\n", name);
 		return FALSE;
 	}
Index: libs/libmythhdhomerun/hdhomerun_device.c
===================================================================
--- libs/libmythhdhomerun/hdhomerun_device.c	(revision 23413)
+++ libs/libmythhdhomerun/hdhomerun_device.c	(working copy)
@@ -37,6 +37,8 @@
 	struct hdhomerun_video_sock_t *vs;
 	struct hdhomerun_debug_t *dbg;
 	struct hdhomerun_channelscan_t *scan;
+	uint32_t multicast_ip;
+	uint16_t multicast_port;
 	uint32_t device_id;
 	unsigned int tuner;
 	uint32_t lockkey;
@@ -44,53 +46,108 @@
 	char model[32];
 };
 
-static void hdhomerun_device_set_update(struct hdhomerun_device_t *hd)
+static int hdhomerun_device_set_device_normal(struct hdhomerun_device_t *hd, uint32_t device_id, uint32_t device_ip)
 {
-	/* Clear cached information. */
-	*hd->model = 0;
+	if (!hd->cs) {
+		hd->cs = hdhomerun_control_create(0, 0, hd->dbg);
+		if (!hd->cs) {
+			hdhomerun_debug_printf(hd->dbg, "hdhomerun_device_set_device: failed to create control object\n");
+			return -1;
+		}
+	}
 
-	/* New name. */
-	sprintf(hd->name, "%08lX-%u", (unsigned long)hd->device_id, hd->tuner);
-}
-
-void hdhomerun_device_set_device(struct hdhomerun_device_t *hd, uint32_t device_id, uint32_t device_ip)
-{
 	hdhomerun_control_set_device(hd->cs, device_id, device_ip);
 
 	if ((device_id == 0) || (device_id == HDHOMERUN_DEVICE_ID_WILDCARD)) {
 		device_id = hdhomerun_control_get_device_id(hd->cs);
 	}
 
+	hd->multicast_ip = 0;
+	hd->multicast_port = 0;
 	hd->device_id = device_id;
-	hdhomerun_device_set_update(hd);
+	hd->tuner = 0;
+	hd->lockkey = 0;
+
+	sprintf(hd->name, "%08lX-%u", (unsigned long)hd->device_id, hd->tuner);
+	hd->model[0] = 0; /* clear cached model string */
+
+	return 1;
 }
 
-void hdhomerun_device_set_tuner(struct hdhomerun_device_t *hd, unsigned int tuner)
+static int hdhomerun_device_set_device_multicast(struct hdhomerun_device_t *hd, uint32_t multicast_ip)
 {
+	if (hd->cs) {
+		hdhomerun_control_destroy(hd->cs);
+		hd->cs = NULL;
+	}
+
+	hd->multicast_ip = multicast_ip;
+	hd->multicast_port = 0;
+	hd->device_id = 0;
+	hd->tuner = 0;
+	hd->lockkey = 0;
+
+	unsigned long ip = multicast_ip;
+	sprintf(hd->name, "%lu.%lu.%lu.%lu", (ip >> 24) & 0xFF, (ip >> 16) & 0xFF, (ip >> 8) & 0xFF, (ip >> 0) & 0xFF);
+	sprintf(hd->model, "multicast");
+
+	return 1;
+}
+
+int hdhomerun_device_set_device(struct hdhomerun_device_t *hd, uint32_t device_id, uint32_t device_ip)
+{
+	if ((device_id == 0) && (device_ip == 0)) {
+		hdhomerun_debug_printf(hd->dbg, "hdhomerun_device_set_device: device not specified\n");
+		return -1;
+	}
+
+	if (hdhomerun_discover_is_ip_multicast(device_ip)) {
+		return hdhomerun_device_set_device_multicast(hd, device_ip);
+	}
+
+	return hdhomerun_device_set_device_normal(hd, device_id, device_ip);
+}
+
+int hdhomerun_device_set_tuner(struct hdhomerun_device_t *hd, unsigned int tuner)
+{
+	if (hd->multicast_ip != 0) {
+		if (tuner != 0) {
+			hdhomerun_debug_printf(hd->dbg, "hdhomerun_device_set_tuner: tuner cannot be specified in multicast mode\n");
+			return -1;
+		}
+
+		return 1;
+	}
+
 	hd->tuner = tuner;
-	hdhomerun_device_set_update(hd);
+	sprintf(hd->name, "%08lX-%u", (unsigned long)hd->device_id, hd->tuner);
+
+	return 1;
 }
 
 struct hdhomerun_device_t *hdhomerun_device_create(uint32_t device_id, uint32_t device_ip, unsigned int tuner, struct hdhomerun_debug_t *dbg)
 {
 	struct hdhomerun_device_t *hd = (struct hdhomerun_device_t *)calloc(1, sizeof(struct hdhomerun_device_t));
 	if (!hd) {
-		hdhomerun_debug_printf(dbg, "hdhomerun_device_create: failed to allocate device object\n");
+		hdhomerun_debug_printf(hd->dbg, "hdhomerun_device_create: failed to allocate device object\n");
 		return NULL;
 	}
 
 	hd->dbg = dbg;
 
-	hd->cs = hdhomerun_control_create(0, 0, hd->dbg);
-	if (!hd->cs) {
-		hdhomerun_debug_printf(hd->dbg, "hdhomerun_device_create: failed to create control object\n");
+	if ((device_id == 0) && (device_ip == 0) && (tuner == 0)) {
+		return hd;
+	}
+
+	if (hdhomerun_device_set_device(hd, device_id, device_ip) <= 0) {
 		free(hd);
 		return NULL;
 	}
+	if (hdhomerun_device_set_tuner(hd, tuner) <= 0) {
+		free(hd);
+		return NULL;
+	}
 
-	hdhomerun_device_set_device(hd, device_id, device_ip);
-	hdhomerun_device_set_tuner(hd, tuner);
-
 	return hd;
 }
 
@@ -104,7 +161,9 @@
 		hdhomerun_video_destroy(hd->vs);
 	}
 
-	hdhomerun_control_destroy(hd->cs);
+	if (hd->cs) {
+		hdhomerun_control_destroy(hd->cs);
+	}
 
 	free(hd);
 }
@@ -156,17 +215,24 @@
 static struct hdhomerun_device_t *hdhomerun_device_create_from_str_ip(const char *device_str, struct hdhomerun_debug_t *dbg)
 {
 	unsigned long a[4];
-	unsigned int tuner;
-
-	if (sscanf(device_str, "%lu.%lu.%lu.%lu-%u", &a[0], &a[1], &a[2], &a[3], &tuner) != 5) {
-		tuner = 0;
+	unsigned int port = 0;
+	if (sscanf(device_str, "%lu.%lu.%lu.%lu:%u", &a[0], &a[1], &a[2], &a[3], &port) != 5) {
 		if (sscanf(device_str, "%lu.%lu.%lu.%lu", &a[0], &a[1], &a[2], &a[3]) != 4) {
 			return NULL;
 		}
 	}
 
 	unsigned long device_ip = (a[0] << 24) | (a[1] << 16) | (a[2] << 8) | (a[3] << 0);
-	return hdhomerun_device_create(HDHOMERUN_DEVICE_ID_WILDCARD, (uint32_t)device_ip, tuner, dbg);
+	struct hdhomerun_device_t *hd = hdhomerun_device_create(HDHOMERUN_DEVICE_ID_WILDCARD, (uint32_t)device_ip, 0, dbg);
+	if (!hd) {
+		return NULL;
+	}
+
+	if (hd->multicast_ip != 0) {
+		hd->multicast_port = port;
+	}
+
+	return hd;
 }
 
 static struct hdhomerun_device_t *hdhomerun_device_create_from_str_dns(const char *device_str, struct hdhomerun_debug_t *dbg)
@@ -239,22 +305,43 @@
 
 uint32_t hdhomerun_device_get_device_id(struct hdhomerun_device_t *hd)
 {
-	return hdhomerun_control_get_device_id(hd->cs);
+	return hd->device_id;
 }
 
 uint32_t hdhomerun_device_get_device_ip(struct hdhomerun_device_t *hd)
 {
-	return hdhomerun_control_get_device_ip(hd->cs);
+	if (hd->multicast_ip != 0) {
+		return hd->multicast_ip;
+	}
+	if (hd->cs) {
+		return hdhomerun_control_get_device_ip(hd->cs);
+	}
+
+	return 0;
 }
 
 uint32_t hdhomerun_device_get_device_id_requested(struct hdhomerun_device_t *hd)
 {
-	return hdhomerun_control_get_device_id_requested(hd->cs);
+	if (hd->multicast_ip != 0) {
+		return 0;
+	}
+	if (hd->cs) {
+		return hdhomerun_control_get_device_id_requested(hd->cs);
+	}
+
+	return 0;
 }
 
 uint32_t hdhomerun_device_get_device_ip_requested(struct hdhomerun_device_t *hd)
 {
-	return hdhomerun_control_get_device_ip_requested(hd->cs);
+	if (hd->multicast_ip != 0) {
+		return hd->multicast_ip;
+	}
+	if (hd->cs) {
+		return hdhomerun_control_get_device_ip_requested(hd->cs);
+	}
+
+	return 0;
 }
 
 unsigned int hdhomerun_device_get_tuner(struct hdhomerun_device_t *hd)
@@ -273,7 +360,7 @@
 		return hd->vs;
 	}
 
-	hd->vs = hdhomerun_video_create(0, VIDEO_DATA_BUFFER_SIZE_1S * 2, hd->dbg);
+	hd->vs = hdhomerun_video_create(hd->multicast_port, VIDEO_DATA_BUFFER_SIZE_1S * 2, hd->dbg);
 	if (!hd->vs) {
 		hdhomerun_debug_printf(hd->dbg, "hdhomerun_device_get_video_sock: failed to create video object\n");
 		return NULL;
@@ -284,7 +371,11 @@
 
 uint32_t hdhomerun_device_get_local_machine_addr(struct hdhomerun_device_t *hd)
 {
-	return hdhomerun_control_get_local_addr(hd->cs);
+	if (hd->cs) {
+		return hdhomerun_control_get_local_addr(hd->cs);
+	}
+
+	return 0;
 }
 
 static uint32_t hdhomerun_device_get_status_parse(const char *status_str, const char *tag)
@@ -368,6 +459,11 @@
 
 int hdhomerun_device_get_tuner_status(struct hdhomerun_device_t *hd, char **pstatus_str, struct hdhomerun_tuner_status_t *status)
 {
+	if (!hd->cs) {
+		hdhomerun_debug_printf(hd->dbg, "hdhomerun_device_get_tuner_status: device not set\n");
+		return -1;
+	}
+
 	memset(status, 0, sizeof(struct hdhomerun_tuner_status_t));
 
 	char name[32];
@@ -414,6 +510,11 @@
 
 int hdhomerun_device_get_tuner_streaminfo(struct hdhomerun_device_t *hd, char **pstreaminfo)
 {
+	if (!hd->cs) {
+		hdhomerun_debug_printf(hd->dbg, "hdhomerun_device_get_tuner_streaminfo: device not set\n");
+		return -1;
+	}
+
 	char name[32];
 	sprintf(name, "/tuner%u/streaminfo", hd->tuner);
 	return hdhomerun_control_get(hd->cs, name, pstreaminfo, NULL);
@@ -421,6 +522,11 @@
 
 int hdhomerun_device_get_tuner_channel(struct hdhomerun_device_t *hd, char **pchannel)
 {
+	if (!hd->cs) {
+		hdhomerun_debug_printf(hd->dbg, "hdhomerun_device_get_tuner_channel: device not set\n");
+		return -1;
+	}
+
 	char name[32];
 	sprintf(name, "/tuner%u/channel", hd->tuner);
 	return hdhomerun_control_get(hd->cs, name, pchannel, NULL);
@@ -428,6 +534,11 @@
 
 int hdhomerun_device_get_tuner_channelmap(struct hdhomerun_device_t *hd, char **pchannelmap)
 {
+	if (!hd->cs) {
+		hdhomerun_debug_printf(hd->dbg, "hdhomerun_device_get_tuner_channelmap: device not set\n");
+		return -1;
+	}
+
 	char name[32];
 	sprintf(name, "/tuner%u/channelmap", hd->tuner);
 	return hdhomerun_control_get(hd->cs, name, pchannelmap, NULL);
@@ -435,6 +546,11 @@
 
 int hdhomerun_device_get_tuner_filter(struct hdhomerun_device_t *hd, char **pfilter)
 {
+	if (!hd->cs) {
+		hdhomerun_debug_printf(hd->dbg, "hdhomerun_device_get_tuner_filter: device not set\n");
+		return -1;
+	}
+
 	char name[32];
 	sprintf(name, "/tuner%u/filter", hd->tuner);
 	return hdhomerun_control_get(hd->cs, name, pfilter, NULL);
@@ -442,6 +558,11 @@
 
 int hdhomerun_device_get_tuner_program(struct hdhomerun_device_t *hd, char **pprogram)
 {
+	if (!hd->cs) {
+		hdhomerun_debug_printf(hd->dbg, "hdhomerun_device_get_tuner_program: device not set\n");
+		return -1;
+	}
+
 	char name[32];
 	sprintf(name, "/tuner%u/program", hd->tuner);
 	return hdhomerun_control_get(hd->cs, name, pprogram, NULL);
@@ -449,6 +570,11 @@
 
 int hdhomerun_device_get_tuner_target(struct hdhomerun_device_t *hd, char **ptarget)
 {
+	if (!hd->cs) {
+		hdhomerun_debug_printf(hd->dbg, "hdhomerun_device_get_tuner_target: device not set\n");
+		return -1;
+	}
+
 	char name[32];
 	sprintf(name, "/tuner%u/target", hd->tuner);
 	return hdhomerun_control_get(hd->cs, name, ptarget, NULL);
@@ -456,6 +582,11 @@
 
 int hdhomerun_device_get_tuner_plotsample(struct hdhomerun_device_t *hd, struct hdhomerun_plotsample_t **psamples, size_t *pcount)
 {
+	if (!hd->cs) {
+		hdhomerun_debug_printf(hd->dbg, "hdhomerun_device_get_tuner_plotsample: device not set\n");
+		return -1;
+	}
+
 	char name[32];
 	sprintf(name, "/tuner%u/plotsample", hd->tuner);
 
@@ -505,6 +636,11 @@
 
 int hdhomerun_device_get_tuner_lockkey_owner(struct hdhomerun_device_t *hd, char **powner)
 {
+	if (!hd->cs) {
+		hdhomerun_debug_printf(hd->dbg, "hdhomerun_device_get_tuner_lockkey_owner: device not set\n");
+		return -1;
+	}
+
 	char name[32];
 	sprintf(name, "/tuner%u/lockkey", hd->tuner);
 	return hdhomerun_control_get(hd->cs, name, powner, NULL);
@@ -512,16 +648,31 @@
 
 int hdhomerun_device_get_ir_target(struct hdhomerun_device_t *hd, char **ptarget)
 {
+	if (!hd->cs) {
+		hdhomerun_debug_printf(hd->dbg, "hdhomerun_device_get_ir_target: device not set\n");
+		return -1;
+	}
+
 	return hdhomerun_control_get(hd->cs, "/ir/target", ptarget, NULL);
 }
 
 int hdhomerun_device_get_lineup_location(struct hdhomerun_device_t *hd, char **plocation)
 {
+	if (!hd->cs) {
+		hdhomerun_debug_printf(hd->dbg, "hdhomerun_device_get_lineup_location: device not set\n");
+		return -1;
+	}
+
 	return hdhomerun_control_get(hd->cs, "/lineup/location", plocation, NULL);
 }
 
 int hdhomerun_device_get_version(struct hdhomerun_device_t *hd, char **pversion_str, uint32_t *pversion_num)
 {
+	if (!hd->cs) {
+		hdhomerun_debug_printf(hd->dbg, "hdhomerun_device_get_version: device not set\n");
+		return -1;
+	}
+
 	char *version_str;
 	int ret = hdhomerun_control_get(hd->cs, "/sys/version", &version_str, NULL);
 	if (ret <= 0) {
@@ -546,6 +697,11 @@
 
 int hdhomerun_device_set_tuner_channel(struct hdhomerun_device_t *hd, const char *channel)
 {
+	if (!hd->cs) {
+		hdhomerun_debug_printf(hd->dbg, "hdhomerun_device_set_tuner_channel: device not set\n");
+		return -1;
+	}
+
 	char name[32];
 	sprintf(name, "/tuner%u/channel", hd->tuner);
 	return hdhomerun_control_set_with_lockkey(hd->cs, name, channel, hd->lockkey, NULL, NULL);
@@ -553,6 +709,11 @@
 
 int hdhomerun_device_set_tuner_channelmap(struct hdhomerun_device_t *hd, const char *channelmap)
 {
+	if (!hd->cs) {
+		hdhomerun_debug_printf(hd->dbg, "hdhomerun_device_set_tuner_channelmap: device not set\n");
+		return -1;
+	}
+
 	char name[32];
 	sprintf(name, "/tuner%u/channelmap", hd->tuner);
 	return hdhomerun_control_set_with_lockkey(hd->cs, name, channelmap, hd->lockkey, NULL, NULL);
@@ -560,6 +721,11 @@
 
 int hdhomerun_device_set_tuner_filter(struct hdhomerun_device_t *hd, const char *filter)
 {
+	if (!hd->cs) {
+		hdhomerun_debug_printf(hd->dbg, "hdhomerun_device_set_tuner_filter: device not set\n");
+		return -1;
+	}
+
 	char name[32];
 	sprintf(name, "/tuner%u/filter", hd->tuner);
 	return hdhomerun_control_set_with_lockkey(hd->cs, name, filter, hd->lockkey, NULL, NULL);
@@ -635,6 +801,11 @@
 
 int hdhomerun_device_set_tuner_program(struct hdhomerun_device_t *hd, const char *program)
 {
+	if (!hd->cs) {
+		hdhomerun_debug_printf(hd->dbg, "hdhomerun_device_set_tuner_program: device not set\n");
+		return -1;
+	}
+
 	char name[32];
 	sprintf(name, "/tuner%u/program", hd->tuner);
 	return hdhomerun_control_set_with_lockkey(hd->cs, name, program, hd->lockkey, NULL, NULL);
@@ -642,16 +813,24 @@
 
 int hdhomerun_device_set_tuner_target(struct hdhomerun_device_t *hd, const char *target)
 {
+	if (!hd->cs) {
+		hdhomerun_debug_printf(hd->dbg, "hdhomerun_device_set_tuner_target: device not set\n");
+		return -1;
+	}
+
 	char name[32];
 	sprintf(name, "/tuner%u/target", hd->tuner);
 	return hdhomerun_control_set_with_lockkey(hd->cs, name, target, hd->lockkey, NULL, NULL);
 }
 
-int hdhomerun_device_set_tuner_target_to_local_protocol(struct hdhomerun_device_t *hd, const char *protocol)
+static int hdhomerun_device_set_tuner_target_to_local(struct hdhomerun_device_t *hd, const char *protocol)
 {
-	/* Create video socket. */
-	hdhomerun_device_get_video_sock(hd);
+	if (!hd->cs) {
+		hdhomerun_debug_printf(hd->dbg, "hdhomerun_device_set_tuner_target_to_local: device not set\n");
+		return -1;
+	}
 	if (!hd->vs) {
+		hdhomerun_debug_printf(hd->dbg, "hdhomerun_device_set_tuner_target_to_local: video not initialized\n");
 		return -1;
 	}
 
@@ -669,33 +848,66 @@
 	return hdhomerun_device_set_tuner_target(hd, target);
 }
 
-int hdhomerun_device_set_tuner_target_to_local(struct hdhomerun_device_t *hd)
+int hdhomerun_device_set_ir_target(struct hdhomerun_device_t *hd, const char *target)
 {
-	return hdhomerun_device_set_tuner_target_to_local_protocol(hd, HDHOMERUN_TARGET_PROTOCOL_UDP); 
-}
+	if (!hd->cs) {
+		hdhomerun_debug_printf(hd->dbg, "hdhomerun_device_set_ir_target: device not set\n");
+		return -1;
+	}
 
-int hdhomerun_device_set_ir_target(struct hdhomerun_device_t *hd, const char *target)
-{
 	return hdhomerun_control_set(hd->cs, "/ir/target", target, NULL, NULL);
 }
 
 int hdhomerun_device_set_lineup_location(struct hdhomerun_device_t *hd, const char *location)
 {
+	if (!hd->cs) {
+		hdhomerun_debug_printf(hd->dbg, "hdhomerun_device_set_lineup_location: device not set\n");
+		return -1;
+	}
+
 	return hdhomerun_control_set(hd->cs, "/lineup/location", location, NULL, NULL);
 }
 
+int hdhomerun_device_set_sys_dvbc_modulation(struct hdhomerun_device_t *hd, const char *modulation_list)
+{
+	if (!hd->cs) {
+		hdhomerun_debug_printf(hd->dbg, "hdhomerun_device_set_sys_dvbc_modulation: device not set\n");
+		return -1;
+	}
+
+	return hdhomerun_control_set(hd->cs, "/sys/dvbc_modulation", modulation_list, NULL, NULL);
+}
+
 int hdhomerun_device_get_var(struct hdhomerun_device_t *hd, const char *name, char **pvalue, char **perror)
 {
+	if (!hd->cs) {
+		hdhomerun_debug_printf(hd->dbg, "hdhomerun_device_get_var: device not set\n");
+		return -1;
+	}
+
 	return hdhomerun_control_get(hd->cs, name, pvalue, perror);
 }
 
 int hdhomerun_device_set_var(struct hdhomerun_device_t *hd, const char *name, const char *value, char **pvalue, char **perror)
 {
+	if (!hd->cs) {
+		hdhomerun_debug_printf(hd->dbg, "hdhomerun_device_set_var: device not set\n");
+		return -1;
+	}
+
 	return hdhomerun_control_set_with_lockkey(hd->cs, name, value, hd->lockkey, pvalue, perror);
 }
 
 int hdhomerun_device_tuner_lockkey_request(struct hdhomerun_device_t *hd, char **perror)
 {
+	if (hd->multicast_ip != 0) {
+		return 1;
+	}
+	if (!hd->cs) {
+		hdhomerun_debug_printf(hd->dbg, "hdhomerun_device_tuner_lockkey_request: device not set\n");
+		return -1;
+	}
+
 	uint32_t new_lockkey = (uint32_t)getcurrenttime();
 
 	char name[32];
@@ -716,6 +928,14 @@
 
 int hdhomerun_device_tuner_lockkey_release(struct hdhomerun_device_t *hd)
 {
+	if (hd->multicast_ip != 0) {
+		return 1;
+	}
+	if (!hd->cs) {
+		hdhomerun_debug_printf(hd->dbg, "hdhomerun_device_tuner_lockkey_release: device not set\n");
+		return -1;
+	}
+
 	if (hd->lockkey == 0) {
 		return 1;
 	}
@@ -730,6 +950,14 @@
 
 int hdhomerun_device_tuner_lockkey_force(struct hdhomerun_device_t *hd)
 {
+	if (hd->multicast_ip != 0) {
+		return 1;
+	}
+	if (!hd->cs) {
+		hdhomerun_debug_printf(hd->dbg, "hdhomerun_device_tuner_lockkey_force: device not set\n");
+		return -1;
+	}
+
 	char name[32];
 	sprintf(name, "/tuner%u/lockkey", hd->tuner);
 	int ret = hdhomerun_control_set(hd->cs, name, "force", NULL, NULL);
@@ -740,13 +968,17 @@
 
 void hdhomerun_device_tuner_lockkey_use_value(struct hdhomerun_device_t *hd, uint32_t lockkey)
 {
+	if (hd->multicast_ip != 0) {
+		return;
+	}
+
 	hd->lockkey = lockkey;
 }
 
 int hdhomerun_device_wait_for_lock(struct hdhomerun_device_t *hd, struct hdhomerun_tuner_status_t *status)
 {
 	/* Delay for SS reading to be valid (signal present). */
-	msleep(250);
+	msleep_minimum(250);
 
 	/* Wait for up to 2.5 seconds for lock. */
 	uint64_t timeout = getcurrenttime() + 2500;
@@ -768,51 +1000,73 @@
 			return 1;
 		}
 
-		msleep(250);
+		msleep_approx(250);
 	}
 }
 
 int hdhomerun_device_stream_start(struct hdhomerun_device_t *hd)
 {
+	hdhomerun_device_get_video_sock(hd);
+	if (!hd->vs) {
+		return -1;
+	}
+
 	/* Set target. */
-	int ret = hdhomerun_device_stream_refresh_target(hd);
-	if (ret <= 0) {
-		return ret;
+	if (hd->multicast_ip != 0) {
+		int ret = hdhomerun_video_join_multicast_group(hd->vs, hd->multicast_ip, 0);
+		if (ret <= 0) {
+			return ret;
+		}
+	} else {
+		int ret = hdhomerun_device_set_tuner_target_to_local(hd, HDHOMERUN_TARGET_PROTOCOL_RTP);
+		if (ret == 0) {
+			ret = hdhomerun_device_set_tuner_target_to_local(hd, HDHOMERUN_TARGET_PROTOCOL_UDP);
+		}
+		if (ret <= 0) {
+			return ret;
+		}
 	}
 
 	/* Flush video buffer. */
-	msleep(64);
+	msleep_minimum(64);
 	hdhomerun_video_flush(hd->vs);
 
 	/* Success. */
 	return 1;
 }
 
-int hdhomerun_device_stream_refresh_target(struct hdhomerun_device_t *hd)
-{
-	int ret = hdhomerun_device_set_tuner_target_to_local_protocol(hd, HDHOMERUN_TARGET_PROTOCOL_RTP);
-	if (ret == 0) {
-		ret = hdhomerun_device_set_tuner_target_to_local_protocol(hd, HDHOMERUN_TARGET_PROTOCOL_UDP);
-	}
-	return ret;
-}
-
 uint8_t *hdhomerun_device_stream_recv(struct hdhomerun_device_t *hd, size_t max_size, size_t *pactual_size)
 {
 	if (!hd->vs) {
+		hdhomerun_debug_printf(hd->dbg, "hdhomerun_device_stream_recv: video not initialized\n");
 		return NULL;
 	}
+
 	return hdhomerun_video_recv(hd->vs, max_size, pactual_size);
 }
 
 void hdhomerun_device_stream_flush(struct hdhomerun_device_t *hd)
 {
+	if (!hd->vs) {
+		hdhomerun_debug_printf(hd->dbg, "hdhomerun_device_stream_flush: video not initialized\n");
+		return;
+	}
+
 	hdhomerun_video_flush(hd->vs);
 }
 
 void hdhomerun_device_stream_stop(struct hdhomerun_device_t *hd)
 {
-	hdhomerun_device_set_tuner_target(hd, "none");
+	if (!hd->vs) {
+		hdhomerun_debug_printf(hd->dbg, "hdhomerun_device_stream_stop: video not initialized\n");
+		return;
+	}
+
+	if (hd->multicast_ip != 0) {
+		hdhomerun_video_leave_multicast_group(hd->vs);
+	} else {
+		hdhomerun_device_set_tuner_target(hd, "none");
+	}
 }
 
 int hdhomerun_device_channelscan_init(struct hdhomerun_device_t *hd, const char *channelmap)
@@ -823,6 +1077,7 @@
 
 	hd->scan = channelscan_create(hd, channelmap);
 	if (!hd->scan) {
+		hdhomerun_debug_printf(hd->dbg, "hdhomerun_device_channelscan_init: failed to create scan object\n");
 		return -1;
 	}
 
@@ -832,6 +1087,7 @@
 int hdhomerun_device_channelscan_advance(struct hdhomerun_device_t *hd, struct hdhomerun_channelscan_result_t *result)
 {
 	if (!hd->scan) {
+		hdhomerun_debug_printf(hd->dbg, "hdhomerun_device_channelscan_advance: scan not initialized\n");
 		return 0;
 	}
 
@@ -847,6 +1103,7 @@
 int hdhomerun_device_channelscan_detect(struct hdhomerun_device_t *hd, struct hdhomerun_channelscan_result_t *result)
 {
 	if (!hd->scan) {
+		hdhomerun_debug_printf(hd->dbg, "hdhomerun_device_channelscan_detect: scan not initialized\n");
 		return 0;
 	}
 
@@ -862,52 +1119,46 @@
 uint8_t hdhomerun_device_channelscan_get_progress(struct hdhomerun_device_t *hd)
 {
 	if (!hd->scan) {
+		hdhomerun_debug_printf(hd->dbg, "hdhomerun_device_channelscan_get_progress: scan not initialized\n");
 		return 0;
 	}
 
 	return channelscan_get_progress(hd->scan);
 }
 
-int hdhomerun_device_firmware_version_check(struct hdhomerun_device_t *hd, uint32_t features)
-{
-	uint32_t version;
-	if (hdhomerun_device_get_version(hd, NULL, &version) <= 0) {
-		return -1;
-	}
-
-	if (version < 20070219) {
-		return 0;
-	}
-
-	return 1;
-}
-
 const char *hdhomerun_device_get_model_str(struct hdhomerun_device_t *hd)
 {
 	if (*hd->model) {
 		return hd->model;
 	}
 
+	if (!hd->cs) {
+		hdhomerun_debug_printf(hd->dbg, "hdhomerun_device_get_model_str: device not set\n");
+		return NULL;
+	}
+
 	char *model_str;
 	int ret = hdhomerun_control_get(hd->cs, "/sys/model", &model_str, NULL);
 	if (ret < 0) {
 		return NULL;
 	}
 	if (ret == 0) {
-		strncpy(hd->model, "hdhomerun_atsc", sizeof(hd->model) - 1);
-		hd->model[sizeof(hd->model) - 1] = 0;
+		model_str = "hdhomerun_atsc";
 	}
-	else
-	{
-		strncpy(hd->model, model_str, sizeof(hd->model) - 1);
-		hd->model[sizeof(hd->model) - 1] = 0;
-	}
 
+	strncpy(hd->model, model_str, sizeof(hd->model) - 1);
+	hd->model[sizeof(hd->model) - 1] = 0;
+
 	return hd->model;
 }
 
 int hdhomerun_device_upgrade(struct hdhomerun_device_t *hd, FILE *upgrade_file)
 {
+	if (!hd->cs) {
+		hdhomerun_debug_printf(hd->dbg, "hdhomerun_device_upgrade: device not set\n");
+		return -1;
+	}
+
 	hdhomerun_control_set(hd->cs, "/tuner0/lockkey", "force", NULL, NULL);
 	hdhomerun_control_set(hd->cs, "/tuner0/channel", "none", NULL, NULL);
 
@@ -923,21 +1174,23 @@
 		return;
 	}
 
-	char name[32];
-	sprintf(name, "/tuner%u/debug", hd->tuner);
+	if (hd->cs) {
+		char name[32];
+		sprintf(name, "/tuner%u/debug", hd->tuner);
 
-	char *debug_str;
-	char *error_str;
-	int ret = hdhomerun_control_get(hd->cs, name, &debug_str, &error_str);
-	if (ret < 0) {
-		hdhomerun_debug_printf(hd->dbg, "video dev: communication error getting debug stats\n");
-		return;
-	}
+		char *debug_str;
+		char *error_str;
+		int ret = hdhomerun_control_get(hd->cs, name, &debug_str, &error_str);
+		if (ret < 0) {
+			hdhomerun_debug_printf(hd->dbg, "video dev: communication error getting debug stats\n");
+			return;
+		}
 
-	if (error_str) {
-		hdhomerun_debug_printf(hd->dbg, "video dev: %s\n", error_str);
-	} else {
-		hdhomerun_debug_printf(hd->dbg, "video dev: %s\n", debug_str);
+		if (error_str) {
+			hdhomerun_debug_printf(hd->dbg, "video dev: %s\n", error_str);
+		} else {
+			hdhomerun_debug_printf(hd->dbg, "video dev: %s\n", debug_str);
+		}
 	}
 
 	if (hd->vs) {
@@ -947,5 +1200,11 @@
 
 void hdhomerun_device_get_video_stats(struct hdhomerun_device_t *hd, struct hdhomerun_video_stats_t *stats)
 {
+	if (!hd->vs) {
+		hdhomerun_debug_printf(hd->dbg, "hdhomerun_device_stream_flush: video not initialized\n");
+		memset(stats, 0, sizeof(struct hdhomerun_video_stats_t));
+		return;
+	}
+
 	hdhomerun_video_get_stats(hd->vs, stats);
 }
Index: libs/libmythhdhomerun/hdhomerun_os_windows.h
===================================================================
--- libs/libmythhdhomerun/hdhomerun_os_windows.h	(revision 23413)
+++ libs/libmythhdhomerun/hdhomerun_os_windows.h	(working copy)
@@ -1,7 +1,7 @@
 /*
  * hdhomerun_os_windows.h
  *
- * Copyright © 2006-2008 Silicondust USA Inc. <www.silicondust.com>.
+ * Copyright © 2006-2010 Silicondust USA Inc. <www.silicondust.com>.
  *
  * This library is free software; you can redistribute it and/or 
  * modify it under the terms of the GNU Lesser General Public
@@ -31,12 +31,10 @@
  */
 
 #define _WINSOCKAPI_
-// MinGW lacks wspiapi, so remove dependency by setting minimum WINVER to WinXP
-#define WINVER 0x0501
 #include <windows.h>
 #include <winsock2.h>
 #include <ws2tcpip.h>
-//#include <wspiapi.h>
+#include <wspiapi.h>
 #include <stdlib.h>
 #include <stdio.h>
 #include <stdarg.h>
@@ -55,7 +53,6 @@
 #endif
 
 typedef int bool_t;
-/* Use MinGW includes instead
 typedef signed __int8 int8_t;
 typedef signed __int16 int16_t;
 typedef signed __int32 int32_t;
@@ -66,17 +63,8 @@
 typedef unsigned __int64 uint64_t;
 typedef HANDLE pthread_t;
 typedef HANDLE pthread_mutex_t;
-*/
-#include <stdint.h>
-#include <pthread.h>
 
-// Avoid #define conflicts by limiting scope to non-c++
-#ifndef __cplusplus
-#define socklen_t int
-#define close closesocket
-#define sock_getlasterror WSAGetLastError()
-#define sock_getlasterror_socktimeout (WSAGetLastError() == WSAETIMEDOUT)
-//#define va_copy(x, y) x = y
+#define va_copy(x, y) x = y
 #define atoll _atoi64
 #define strdup _strdup
 #define strcasecmp _stricmp
@@ -85,100 +73,29 @@
 #define ftello _ftelli64
 #define THREAD_FUNC_PREFIX DWORD WINAPI
 #define SIGPIPE SIGABRT
+
+#ifdef __cplusplus
+extern "C" {
 #endif
 
-static inline uint64_t getcurrenttime(void)
-{
-	struct timeb tb;
-	ftime(&tb);
-	return ((uint64_t)tb.time * 1000) + tb.millitm;
-}
+extern LIBTYPE uint64_t getcurrenttime(void);
+extern LIBTYPE void msleep_approx(uint64_t ms);
+extern LIBTYPE void msleep_minimum(uint64_t ms);
 
-static inline int msleep(unsigned int ms)
-{
-	uint64_t stop_time = getcurrenttime() + ms;
+extern LIBTYPE int pthread_create(pthread_t *tid, void *attr, LPTHREAD_START_ROUTINE start, void *arg);
+extern LIBTYPE int pthread_join(pthread_t tid, void **value_ptr);
+extern LIBTYPE void pthread_mutex_init(pthread_mutex_t *mutex, void *attr);
+extern LIBTYPE void pthread_mutex_lock(pthread_mutex_t *mutex);
+extern LIBTYPE void pthread_mutex_unlock(pthread_mutex_t *mutex);
 
-	while (1) {
-		uint64_t current_time = getcurrenttime();
-		if (current_time >= stop_time) {
-			return 0;
-		}
-
-		uint64_t delay_ms = stop_time - current_time;
-		Sleep((DWORD)delay_ms);
-	}
-}
-
-// Avoid a define conflict
-/*static inline int sleep(unsigned int sec)
-{
-	msleep(sec * 1000);
-	return 0;
-}
-*/
-
-static inline int setsocktimeout(int s, int level, int optname, uint64_t timeout)
-{
-	int t = (int)timeout;
-	return setsockopt(s, level, optname, (char *)&t, sizeof(t));
-}
-
-/* MythTV uses pthreads lib instead
-static inline int pthread_create(pthread_t *tid, void *attr, LPTHREAD_START_ROUTINE start, void *arg)
-{
-	*tid = CreateThread(NULL, 0, start, arg, 0, NULL);
-	if (!*tid) {
-		return (int)GetLastError();
-	}
-	return 0;
-}
-
-static inline int pthread_join(pthread_t tid, void **value_ptr)
-{
-	while (1) {
-		DWORD ExitCode = 0;
-		if (!GetExitCodeThread(tid, &ExitCode)) {
-			return (int)GetLastError();
-		}
-		if (ExitCode != STILL_ACTIVE) {
-			return 0;
-		}
-	}
-}
-
-static inline void pthread_mutex_init(pthread_mutex_t *mutex, void *attr)
-{
-	*mutex = CreateMutex(NULL, FALSE, NULL);
-}
-
-static inline void pthread_mutex_lock(pthread_mutex_t *mutex)
-{
-	WaitForSingleObject(*mutex, INFINITE);
-}
-
-static inline void pthread_mutex_unlock(pthread_mutex_t *mutex)
-{
-	ReleaseMutex(*mutex);
-}
-*/
-
 /*
  * The console output format should be set to UTF-8, however in XP and Vista this breaks batch file processing.
  * Attempting to restore on exit fails to restore if the program is terminated by the user.
  * Solution - set the output format each printf.
  */
-static inline void console_vprintf(const char *fmt, va_list ap)
-{
-	UINT cp = GetConsoleOutputCP();
-	SetConsoleOutputCP(CP_UTF8);
-	vprintf(fmt, ap);
-	SetConsoleOutputCP(cp);
-}
+extern LIBTYPE void console_vprintf(const char *fmt, va_list ap);
+extern LIBTYPE void console_printf(const char *fmt, ...);
 
-static inline void console_printf(const char *fmt, ...)
-{
-	va_list ap;
-	va_start(ap, fmt);
-	console_vprintf(fmt, ap);
-	va_end(ap);
+#ifdef __cplusplus
 }
+#endif
Index: libs/libmythhdhomerun/hdhomerun_device.h
===================================================================
--- libs/libmythhdhomerun/hdhomerun_device.h	(revision 23413)
+++ libs/libmythhdhomerun/hdhomerun_device.h	(working copy)
@@ -90,8 +90,8 @@
 extern LIBTYPE uint32_t hdhomerun_device_get_device_ip_requested(struct hdhomerun_device_t *hd);
 extern LIBTYPE unsigned int hdhomerun_device_get_tuner(struct hdhomerun_device_t *hd);
 
-extern LIBTYPE void hdhomerun_device_set_device(struct hdhomerun_device_t *hd, uint32_t device_id, uint32_t device_ip);
-extern LIBTYPE void hdhomerun_device_set_tuner(struct hdhomerun_device_t *hd, unsigned int tuner);
+extern LIBTYPE int hdhomerun_device_set_device(struct hdhomerun_device_t *hd, uint32_t device_id, uint32_t device_ip);
+extern LIBTYPE int hdhomerun_device_set_tuner(struct hdhomerun_device_t *hd, unsigned int tuner);
 extern LIBTYPE int hdhomerun_device_set_tuner_from_str(struct hdhomerun_device_t *hd, const char *tuner_str);
 
 /*
@@ -148,10 +148,9 @@
 extern LIBTYPE int hdhomerun_device_set_tuner_filter_by_array(struct hdhomerun_device_t *hd, unsigned char filter_array[0x2000]);
 extern LIBTYPE int hdhomerun_device_set_tuner_program(struct hdhomerun_device_t *hd, const char *program);
 extern LIBTYPE int hdhomerun_device_set_tuner_target(struct hdhomerun_device_t *hd, const char *target);
-extern LIBTYPE int hdhomerun_device_set_tuner_target_to_local_protocol(struct hdhomerun_device_t *hd, const char *protocol);
-extern LIBTYPE int hdhomerun_device_set_tuner_target_to_local(struct hdhomerun_device_t *hd);
 extern LIBTYPE int hdhomerun_device_set_ir_target(struct hdhomerun_device_t *hd, const char *target);
 extern LIBTYPE int hdhomerun_device_set_lineup_location(struct hdhomerun_device_t *hd, const char *location);
+extern LIBTYPE int hdhomerun_device_set_sys_dvbc_modulation(struct hdhomerun_device_t *hd, const char *modulation_list);
 
 /*
  * Get/set a named control variable on the device.
@@ -224,7 +223,6 @@
  * The hdhomerun_device_stream_stop function tells the device to stop streaming data.
  */
 extern LIBTYPE int hdhomerun_device_stream_start(struct hdhomerun_device_t *hd);
-extern LIBTYPE int hdhomerun_device_stream_refresh_target(struct hdhomerun_device_t *hd);
 extern LIBTYPE uint8_t *hdhomerun_device_stream_recv(struct hdhomerun_device_t *hd, size_t max_size, size_t *pactual_size);
 extern LIBTYPE void hdhomerun_device_stream_flush(struct hdhomerun_device_t *hd);
 extern LIBTYPE void hdhomerun_device_stream_stop(struct hdhomerun_device_t *hd);
@@ -238,17 +236,6 @@
 extern LIBTYPE uint8_t hdhomerun_device_channelscan_get_progress(struct hdhomerun_device_t *hd);
 
 /*
- * Check that the device is running the recommended firmware.
- *
- * uint32_t features: Reserved for future use. Set to zero.
- *
- * Returns 1 if the firmware meets the minimum requriements for all operations.
- * Returns 0 if th firmware does not meet the minimum requriements for all operations.
- * Returns -1 if an error occurs.
- */
-extern LIBTYPE int hdhomerun_device_firmware_version_check(struct hdhomerun_device_t *hd, uint32_t features);
-
-/*
  * Upload new firmware to the device.
  *
  * FILE *upgrade_file: File pointer to read from. The file must have been opened in binary mode for reading.
Index: libs/libmythhdhomerun/hdhomerun_device_selector.h
===================================================================
--- libs/libmythhdhomerun/hdhomerun_device_selector.h	(revision 23413)
+++ libs/libmythhdhomerun/hdhomerun_device_selector.h	(working copy)
@@ -43,11 +43,17 @@
 extern LIBTYPE void hdhomerun_device_selector_destroy(struct hdhomerun_device_selector_t *hds, bool_t destroy_devices);
 
 /*
+ * Get the number of devices in the list.
+ */
+extern LIBTYPE int hdhomerun_device_selector_get_device_count(struct hdhomerun_device_selector_t *hds);
+
+/*
  * Populate device selector with devices from given source.
+ * Returns the number of devices populated.
  */
-extern LIBTYPE void hdhomerun_device_selector_load_from_file(struct hdhomerun_device_selector_t *hds, char *filename);
+extern LIBTYPE int hdhomerun_device_selector_load_from_file(struct hdhomerun_device_selector_t *hds, char *filename);
 #if defined(__WINDOWS__)
-extern LIBTYPE void hdhomerun_device_selector_load_from_windows_registry(struct hdhomerun_device_selector_t *hds, wchar_t *wsource);
+extern LIBTYPE int hdhomerun_device_selector_load_from_windows_registry(struct hdhomerun_device_selector_t *hds, wchar_t *wsource);
 #endif
 
 /*
Index: libs/libmythhdhomerun/hdhomerun_control.c
===================================================================
--- libs/libmythhdhomerun/hdhomerun_control.c	(revision 23413)
+++ libs/libmythhdhomerun/hdhomerun_control.c	(working copy)
@@ -1,7 +1,7 @@
 /*
  * hdhomerun_control.c
  *
- * Copyright © 2006 Silicondust USA Inc. <www.silicondust.com>.
+ * Copyright © 2006-2010 Silicondust USA Inc. <www.silicondust.com>.
  *
  * This library is free software; you can redistribute it and/or 
  * modify it under the terms of the GNU Lesser General Public
@@ -32,8 +32,9 @@
 
 #include "hdhomerun.h"
 
-#define HDHOMERUN_CONTROL_SEND_TIMEOUT 5000
-#define HDHOMERUN_CONTROL_RECV_TIMEOUT 5000
+#define HDHOMERUN_CONTROL_CONNECT_TIMEOUT 2500
+#define HDHOMERUN_CONTROL_SEND_TIMEOUT 2500
+#define HDHOMERUN_CONTROL_RECV_TIMEOUT 2500
 #define HDHOMERUN_CONTROL_UPGRADE_TIMEOUT 20000
 
 struct hdhomerun_control_sock_t {
@@ -49,12 +50,12 @@
 
 static void hdhomerun_control_close_sock(struct hdhomerun_control_sock_t *cs)
 {
-	if (cs->sock == -1) {
+	if (cs->sock == HDHOMERUN_SOCK_INVALID) {
 		return;
 	}
 
-	close(cs->sock);
-	cs->sock = -1;
+	hdhomerun_sock_destroy(cs->sock);
+	cs->sock = HDHOMERUN_SOCK_INVALID;
 }
 
 void hdhomerun_control_set_device(struct hdhomerun_control_sock_t *cs, uint32_t device_id, uint32_t device_ip)
@@ -76,7 +77,7 @@
 	}
 
 	cs->dbg = dbg;
-	cs->sock = -1;
+	cs->sock = HDHOMERUN_SOCK_INVALID;
 	hdhomerun_control_set_device(cs, device_id, device_ip);
 
 	return cs;
@@ -90,7 +91,7 @@
 
 static bool_t hdhomerun_control_connect_sock(struct hdhomerun_control_sock_t *cs)
 {
-	if (cs->sock != -1) {
+	if (cs->sock != HDHOMERUN_SOCK_INVALID) {
 		return TRUE;
 	}
 
@@ -98,6 +99,10 @@
 		hdhomerun_debug_printf(cs->dbg, "hdhomerun_control_connect_sock: no device specified\n");
 		return FALSE;
 	}
+	if (hdhomerun_discover_is_ip_multicast(cs->desired_device_ip)) {
+		hdhomerun_debug_printf(cs->dbg, "hdhomerun_control_connect_sock: cannot use multicast ip address for device operations\n");
+		return FALSE;
+	}
 
 	/* Find device. */
 	struct hdhomerun_discover_device_t result;
@@ -109,24 +114,15 @@
 	cs->actual_device_id = result.device_id;
 
 	/* Create socket. */
-	cs->sock = (int)socket(AF_INET, SOCK_STREAM, 0);
-	if (cs->sock == -1) {
-		hdhomerun_debug_printf(cs->dbg, "hdhomerun_control_connect_sock: failed to create socket (%d)\n", sock_getlasterror);
+	cs->sock = hdhomerun_sock_create_tcp();
+	if (cs->sock == HDHOMERUN_SOCK_INVALID) {
+		hdhomerun_debug_printf(cs->dbg, "hdhomerun_control_connect_sock: failed to create socket (%d)\n", hdhomerun_sock_getlasterror());
 		return FALSE;
 	}
 
-	/* Set timeouts. */
-	setsocktimeout(cs->sock, SOL_SOCKET, SO_SNDTIMEO, HDHOMERUN_CONTROL_SEND_TIMEOUT);
-	setsocktimeout(cs->sock, SOL_SOCKET, SO_RCVTIMEO, HDHOMERUN_CONTROL_RECV_TIMEOUT);
-
 	/* Initiate connection. */
-	struct sockaddr_in sock_addr;
-	memset(&sock_addr, 0, sizeof(sock_addr));
-	sock_addr.sin_family = AF_INET;
-	sock_addr.sin_addr.s_addr = htonl(cs->actual_device_ip);
-	sock_addr.sin_port = htons(HDHOMERUN_CONTROL_TCP_PORT);
-	if (connect(cs->sock, (struct sockaddr *)&sock_addr, sizeof(sock_addr)) != 0) {
-		hdhomerun_debug_printf(cs->dbg, "hdhomerun_control_connect_sock: failed to connect (%d)\n", sock_getlasterror);
+	if (!hdhomerun_sock_connect(cs->sock, cs->actual_device_ip, HDHOMERUN_CONTROL_TCP_PORT, HDHOMERUN_CONTROL_CONNECT_TIMEOUT)) {
+		hdhomerun_debug_printf(cs->dbg, "hdhomerun_control_connect_sock: failed to connect (%d)\n", hdhomerun_sock_getlasterror());
 		hdhomerun_control_close_sock(cs);
 		return FALSE;
 	}
@@ -172,76 +168,58 @@
 		return 0;
 	}
 
-	struct sockaddr_in sock_addr;
-	socklen_t sockaddr_size = sizeof(sock_addr);
-	if (getsockname(cs->sock, (struct sockaddr*)&sock_addr, &sockaddr_size) != 0) {
-		hdhomerun_debug_printf(cs->dbg, "hdhomerun_control_get_local_addr: getsockname failed (%d)\n", sock_getlasterror);
+	uint32_t addr = hdhomerun_sock_getsockname_addr(cs->sock);
+	if (addr == 0) {
+		hdhomerun_debug_printf(cs->dbg, "hdhomerun_control_get_local_addr: getsockname failed (%d)\n", hdhomerun_sock_getlasterror());
 		return 0;
 	}
 
-	return ntohl(sock_addr.sin_addr.s_addr);
+	return addr;
 }
 
-static int hdhomerun_control_send_sock(struct hdhomerun_control_sock_t *cs, struct hdhomerun_pkt_t *tx_pkt)
+static bool_t hdhomerun_control_send_sock(struct hdhomerun_control_sock_t *cs, struct hdhomerun_pkt_t *tx_pkt)
 {
-	int length = (int)(tx_pkt->end - tx_pkt->start);
-	if (send(cs->sock, (char *)tx_pkt->start, (int)length, 0) != length) {
-		hdhomerun_debug_printf(cs->dbg, "hdhomerun_control_send_sock: send failed (%d)\n", sock_getlasterror);
+	if (!hdhomerun_sock_send(cs->sock, tx_pkt->start, tx_pkt->end - tx_pkt->start, HDHOMERUN_CONTROL_SEND_TIMEOUT)) {
+		hdhomerun_debug_printf(cs->dbg, "hdhomerun_control_send_sock: send failed (%d)\n", hdhomerun_sock_getlasterror());
 		hdhomerun_control_close_sock(cs);
-		return -1;
+		return FALSE;
 	}
 
-	return 1;
+	return TRUE;
 }
 
-static int hdhomerun_control_recv_sock(struct hdhomerun_control_sock_t *cs, struct hdhomerun_pkt_t *rx_pkt, uint16_t *ptype, uint64_t recv_timeout)
+static bool_t hdhomerun_control_recv_sock(struct hdhomerun_control_sock_t *cs, struct hdhomerun_pkt_t *rx_pkt, uint16_t *ptype, uint64_t recv_timeout)
 {
 	uint64_t stop_time = getcurrenttime() + recv_timeout;
 	hdhomerun_pkt_reset(rx_pkt);
 
-	while (getcurrenttime() < stop_time) {
-		struct timeval t;
-		t.tv_sec = 0;
-		t.tv_usec = 250000;
-	
-		fd_set readfds;
-		FD_ZERO(&readfds);
-		FD_SET(cs->sock, &readfds);
-	
-		if (select(cs->sock+1, &readfds, NULL, NULL, &t) < 0) {
-			hdhomerun_debug_printf(cs->dbg, "hdhomerun_control_recv_sock: select failed (%d)\n", sock_getlasterror);
+	while (1) {
+		uint64_t current_time = getcurrenttime();
+		if (current_time >= stop_time) {
+			hdhomerun_debug_printf(cs->dbg, "hdhomerun_control_recv_sock: timeout\n");
 			hdhomerun_control_close_sock(cs);
-			return -1;
+			return FALSE;
 		}
-	
-		if (!FD_ISSET(cs->sock, &readfds)) {
-			continue;
-		}
-	
-		int rx_length = recv(cs->sock, (char *)rx_pkt->end, (int)(rx_pkt->limit - rx_pkt->end), 0);
-		if (rx_length <= 0) {
-			hdhomerun_debug_printf(cs->dbg, "hdhomerun_control_recv_sock: recv failed (%d)\n", sock_getlasterror);
+
+		size_t length = rx_pkt->limit - rx_pkt->end;
+		if (!hdhomerun_sock_recv(cs->sock, rx_pkt->end, &length, stop_time - current_time)) {
+			hdhomerun_debug_printf(cs->dbg, "hdhomerun_control_recv_sock: recv failed (%d)\n", hdhomerun_sock_getlasterror());
 			hdhomerun_control_close_sock(cs);
-			return -1;
+			return FALSE;
 		}
-		rx_pkt->end += rx_length;
 
+		rx_pkt->end += length;
+
 		int ret = hdhomerun_pkt_open_frame(rx_pkt, ptype);
 		if (ret < 0) {
 			hdhomerun_debug_printf(cs->dbg, "hdhomerun_control_recv_sock: frame error\n");
 			hdhomerun_control_close_sock(cs);
-			return -1;
+			return FALSE;
 		}
-		if (ret == 0) {
-			continue;
+		if (ret > 0) {
+			return TRUE;
 		}
-
-		return 1;
 	}
-
-	hdhomerun_debug_printf(cs->dbg, "hdhomerun_control_recv_sock: timeout\n");
-	hdhomerun_control_close_sock(cs);
-	return -1;
 }
 
 static int hdhomerun_control_send_recv_internal(struct hdhomerun_control_sock_t *cs, struct hdhomerun_pkt_t *tx_pkt, struct hdhomerun_pkt_t *rx_pkt, uint16_t type, uint64_t recv_timeout)
@@ -250,14 +228,14 @@
 
 	int i;
 	for (i = 0; i < 2; i++) {
-		if (cs->sock == -1) {
+		if (cs->sock == HDHOMERUN_SOCK_INVALID) {
 			if (!hdhomerun_control_connect_sock(cs)) {
 				hdhomerun_debug_printf(cs->dbg, "hdhomerun_control_send_recv: connect failed\n");
 				return -1;
 			}
 		}
 
-		if (hdhomerun_control_send_sock(cs, tx_pkt) < 0) {
+		if (!hdhomerun_control_send_sock(cs, tx_pkt)) {
 			continue;
 		}
 		if (!rx_pkt) {
@@ -265,7 +243,7 @@
 		}
 
 		uint16_t rsp_type;
-		if (hdhomerun_control_recv_sock(cs, rx_pkt, &rsp_type, recv_timeout) < 0) {
+		if (!hdhomerun_control_recv_sock(cs, rx_pkt, &rsp_type, recv_timeout)) {
 			continue;
 		}
 		if (rsp_type != type + 1) {
@@ -301,7 +279,7 @@
 	}
 	hdhomerun_pkt_write_u8(tx_pkt, HDHOMERUN_TAG_GETSET_NAME);
 	hdhomerun_pkt_write_var_length(tx_pkt, name_len);
-	hdhomerun_pkt_write_mem(tx_pkt, (const void *)name, name_len);
+	hdhomerun_pkt_write_mem(tx_pkt, (void *)name, name_len);
 
 	if (value) {
 		int value_len = (int)strlen(value) + 1;
@@ -311,7 +289,7 @@
 		}
 		hdhomerun_pkt_write_u8(tx_pkt, HDHOMERUN_TAG_GETSET_VALUE);
 		hdhomerun_pkt_write_var_length(tx_pkt, value_len);
-		hdhomerun_pkt_write_mem(tx_pkt, (const void *)value, value_len);
+		hdhomerun_pkt_write_mem(tx_pkt, (void *)value, value_len);
 	}
 
 	if (lockkey != 0) {
