libhdhomerun release 20150826

This commit is contained in:
Nick Kelsey 2016-01-26 13:26:49 -08:00
parent c28fd61832
commit a2475b9991
31 changed files with 7828 additions and 1 deletions

59
Makefile Normal file
View file

@ -0,0 +1,59 @@
LIBSRCS += hdhomerun_channels.c
LIBSRCS += hdhomerun_channelscan.c
LIBSRCS += hdhomerun_control.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
CFLAGS += -Wall -O2 -Wmissing-declarations -Wmissing-prototypes -Wstrict-prototypes -Wpointer-arith
LDFLAGS += -lpthread
SHARED = -shared -Wl,-soname,libhdhomerun$(LIBEXT)
ifeq ($(OS),Windows_NT)
BINEXT := .exe
LIBEXT := .dll
LDFLAGS += -liphlpapi
else
OS := $(shell uname -s)
LIBEXT := .so
ifeq ($(OS),Linux)
LDFLAGS += -lrt
endif
ifeq ($(OS),SunOS)
LDFLAGS += -lsocket
endif
ifeq ($(OS),Darwin)
CFLAGS += -arch i386 -arch x86_64
LIBEXT := .dylib
SHARED := -dynamiclib -install_name libhdhomerun$(LIBEXT)
endif
endif
all : hdhomerun_config$(BINEXT) libhdhomerun$(LIBEXT)
hdhomerun_config$(BINEXT) : hdhomerun_config.c $(LIBSRCS)
$(CC) $(CFLAGS) $+ $(LDFLAGS) -o $@
$(STRIP) $@
libhdhomerun$(LIBEXT) : $(LIBSRCS)
$(CC) $(CFLAGS) -fPIC -DDLL_EXPORT $(SHARED) $+ $(LDFLAGS) -o $@
clean :
-rm -f hdhomerun_config$(BINEXT)
-rm -f libhdhomerun$(LIBEXT)
distclean : clean
%:
@echo "(ignoring request to make $@)"
.PHONY: all list clean distclean

View file

@ -1 +1,29 @@
# libhdhomerun
/*
* README
*
* Copyright © 2005-2009 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
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
Top level include file: hdhomerun.h
Top level API: hdhomerun_device. See hdhomerun_device.h for documentation.
The hdhomerun_device API should be used rather than the low level control and video APIs required with previous versions.
Additional libraries required:
- pthread
- iphlpapi (windows only)

32
hdhomerun.h Normal file
View file

@ -0,0 +1,32 @@
/*
* hdhomerun.h
*
* 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
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#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"
#include "hdhomerun_video.h"
#include "hdhomerun_channels.h"
#include "hdhomerun_channelscan.h"
#include "hdhomerun_device.h"
#include "hdhomerun_device_selector.h"

406
hdhomerun_channels.c Normal file
View file

@ -0,0 +1,406 @@
/*
* hdhomerun_channels.c
*
* Copyright © 2007-2008 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
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "hdhomerun.h"
struct hdhomerun_channel_entry_t {
struct hdhomerun_channel_entry_t *next;
struct hdhomerun_channel_entry_t *prev;
uint32_t frequency;
uint16_t channel_number;
char name[16];
};
struct hdhomerun_channel_list_t {
struct hdhomerun_channel_entry_t *head;
struct hdhomerun_channel_entry_t *tail;
};
struct hdhomerun_channelmap_range_t {
uint16_t channel_range_start;
uint16_t channel_range_end;
uint32_t frequency;
uint32_t spacing;
};
struct hdhomerun_channelmap_record_t {
const char *channelmap;
const struct hdhomerun_channelmap_range_t *range_list;
const char *channelmap_scan_group;
const char *countrycodes;
};
/* AU antenna channels. Channels {6, 7, 8, 9, 9A} are numbered {5, 6, 7, 8, 9} by the HDHomeRun. */
static const struct hdhomerun_channelmap_range_t hdhomerun_channelmap_range_au_bcast[] = {
{ 5, 12, 177500000, 7000000},
{ 21, 69, 480500000, 7000000},
{ 0, 0, 0, 0}
};
/* EU antenna channels. */
static const struct hdhomerun_channelmap_range_t hdhomerun_channelmap_range_eu_bcast[] = {
{ 5, 12, 177500000, 7000000},
{ 21, 69, 474000000, 8000000},
{ 0, 0, 0, 0}
};
/* EU cable channels. No common standard - use frequency in MHz for channel number. */
static const struct hdhomerun_channelmap_range_t hdhomerun_channelmap_range_eu_cable[] = {
{108, 862, 108000000, 1000000},
{ 0, 0, 0, 0}
};
/* KR cable channels. */
static const struct hdhomerun_channelmap_range_t hdhomerun_channelmap_range_kr_cable[] = {
{ 2, 4, 57000000, 6000000},
{ 5, 6, 79000000, 6000000},
{ 7, 13, 177000000, 6000000},
{ 14, 22, 123000000, 6000000},
{ 23, 153, 219000000, 6000000},
{ 0, 0, 0, 0}
};
/* JP antenna channels. */
static const struct hdhomerun_channelmap_range_t hdhomerun_channelmap_range_jp_bcast[] = {
{ 13, 62, 473000000, 6000000},
{ 0, 0, 0, 0}
};
/* US antenna channels. */
static const struct hdhomerun_channelmap_range_t hdhomerun_channelmap_range_us_bcast[] = {
{ 2, 4, 57000000, 6000000},
{ 5, 6, 79000000, 6000000},
{ 7, 13, 177000000, 6000000},
{ 14, 69, 473000000, 6000000},
{ 0, 0, 0, 0}
};
/* US cable channels. */
static const struct hdhomerun_channelmap_range_t hdhomerun_channelmap_range_us_cable[] = {
{ 2, 4, 57000000, 6000000},
{ 5, 6, 79000000, 6000000},
{ 7, 13, 177000000, 6000000},
{ 14, 22, 123000000, 6000000},
{ 23, 94, 219000000, 6000000},
{ 95, 99, 93000000, 6000000},
{100, 158, 651000000, 6000000},
{ 0, 0, 0, 0}
};
/* US cable channels (HRC). */
static const struct hdhomerun_channelmap_range_t hdhomerun_channelmap_range_us_hrc[] = {
{ 2, 4, 55752700, 6000300},
{ 5, 6, 79753900, 6000300},
{ 7, 13, 175758700, 6000300},
{ 14, 22, 121756000, 6000300},
{ 23, 94, 217760800, 6000300},
{ 95, 99, 91754500, 6000300},
{100, 158, 649782400, 6000300},
{ 0, 0, 0, 0}
};
/* US cable channels (IRC). */
static const struct hdhomerun_channelmap_range_t hdhomerun_channelmap_range_us_irc[] = {
{ 2, 4, 57012500, 6000000},
{ 5, 6, 81012500, 6000000},
{ 7, 13, 177012500, 6000000},
{ 14, 22, 123012500, 6000000},
{ 23, 41, 219012500, 6000000},
{ 42, 42, 333025000, 6000000},
{ 43, 94, 339012500, 6000000},
{ 95, 97, 93012500, 6000000},
{ 98, 99, 111025000, 6000000},
{100, 158, 651012500, 6000000},
{ 0, 0, 0, 0}
};
static const struct hdhomerun_channelmap_record_t hdhomerun_channelmap_table[] = {
{"au-bcast", hdhomerun_channelmap_range_au_bcast, "au-bcast", "AU"},
{"au-cable", hdhomerun_channelmap_range_eu_cable, "au-cable", "AU"},
{"eu-bcast", hdhomerun_channelmap_range_eu_bcast, "eu-bcast", NULL},
{"eu-cable", hdhomerun_channelmap_range_eu_cable, "eu-cable", NULL},
{"tw-bcast", hdhomerun_channelmap_range_us_bcast, "tw-bcast", "TW"},
{"tw-cable", hdhomerun_channelmap_range_us_cable, "tw-cable", "TW"},
{"kr-bcast", hdhomerun_channelmap_range_us_bcast, "kr-bcast", "KR"},
{"kr-cable", hdhomerun_channelmap_range_kr_cable, "kr-cable", "KR"},
{"us-bcast", hdhomerun_channelmap_range_us_bcast, "us-bcast", NULL},
{"us-cable", hdhomerun_channelmap_range_us_cable, "us-cable us-hrc us-irc", NULL},
{"us-hrc", hdhomerun_channelmap_range_us_hrc , "us-cable us-hrc us-irc", NULL},
{"us-irc", hdhomerun_channelmap_range_us_irc, "us-cable us-hrc us-irc", NULL},
{"jp-bcast", hdhomerun_channelmap_range_jp_bcast, "jp-bcast", "JP" },
{ NULL, NULL, NULL, NULL }
};
const char *hdhomerun_channelmap_get_channelmap_from_country_source(const char *countrycode, const char *source, const char *supported)
{
const char *default_result = NULL;
const struct hdhomerun_channelmap_record_t *record = hdhomerun_channelmap_table;
while (record->channelmap) {
/* Ignore records that do not match the requested source. */
if (!strstr(record->channelmap, source)) {
record++;
continue;
}
/* Ignore records that are not supported by the hardware. */
if (!strstr(supported, record->channelmap)) {
record++;
continue;
}
/* If this record is the default result then remember it and keep searching. */
if (!record->countrycodes) {
default_result = record->channelmap;
record++;
continue;
}
/* Ignore records that have a countrycode filter and do not match. */
if (!strstr(record->countrycodes, countrycode)) {
record++;
continue;
}
/* Record found with exact match for source and countrycode. */
return record->channelmap;
}
return default_result;
}
const char *hdhomerun_channelmap_get_channelmap_scan_group(const char *channelmap)
{
const struct hdhomerun_channelmap_record_t *record = hdhomerun_channelmap_table;
while (record->channelmap) {
if (strstr(channelmap, record->channelmap)) {
return record->channelmap_scan_group;
}
record++;
}
return NULL;
}
uint16_t hdhomerun_channel_entry_channel_number(struct hdhomerun_channel_entry_t *entry)
{
return entry->channel_number;
}
uint32_t hdhomerun_channel_entry_frequency(struct hdhomerun_channel_entry_t *entry)
{
return entry->frequency;
}
const char *hdhomerun_channel_entry_name(struct hdhomerun_channel_entry_t *entry)
{
return entry->name;
}
struct hdhomerun_channel_entry_t *hdhomerun_channel_list_first(struct hdhomerun_channel_list_t *channel_list)
{
return channel_list->head;
}
struct hdhomerun_channel_entry_t *hdhomerun_channel_list_last(struct hdhomerun_channel_list_t *channel_list)
{
return channel_list->tail;
}
struct hdhomerun_channel_entry_t *hdhomerun_channel_list_next(struct hdhomerun_channel_list_t *channel_list, struct hdhomerun_channel_entry_t *entry)
{
return entry->next;
}
struct hdhomerun_channel_entry_t *hdhomerun_channel_list_prev(struct hdhomerun_channel_list_t *channel_list, struct hdhomerun_channel_entry_t *entry)
{
return entry->prev;
}
uint32_t hdhomerun_channel_list_total_count(struct hdhomerun_channel_list_t *channel_list)
{
uint32_t count = 0;
struct hdhomerun_channel_entry_t *entry = hdhomerun_channel_list_first(channel_list);
while (entry) {
count++;
entry = hdhomerun_channel_list_next(channel_list, entry);
}
return count;
}
uint32_t hdhomerun_channel_list_frequency_count(struct hdhomerun_channel_list_t *channel_list)
{
uint32_t count = 0;
uint32_t last_frequency = 0;
struct hdhomerun_channel_entry_t *entry = hdhomerun_channel_list_first(channel_list);
while (entry) {
if (entry->frequency != last_frequency) {
last_frequency = entry->frequency;
count++;
}
entry = hdhomerun_channel_list_next(channel_list, entry);
}
return count;
}
uint32_t hdhomerun_channel_frequency_round(uint32_t frequency, uint32_t resolution)
{
frequency += resolution / 2;
return (frequency / resolution) * resolution;
}
uint32_t hdhomerun_channel_frequency_round_normal(uint32_t frequency)
{
return hdhomerun_channel_frequency_round(frequency, 125000);
}
uint32_t hdhomerun_channel_number_to_frequency(struct hdhomerun_channel_list_t *channel_list, uint16_t channel_number)
{
struct hdhomerun_channel_entry_t *entry = hdhomerun_channel_list_first(channel_list);
while (entry) {
if (entry->channel_number == channel_number) {
return entry->frequency;
}
entry = hdhomerun_channel_list_next(channel_list, entry);
}
return 0;
}
uint16_t hdhomerun_channel_frequency_to_number(struct hdhomerun_channel_list_t *channel_list, uint32_t frequency)
{
frequency = hdhomerun_channel_frequency_round_normal(frequency);
struct hdhomerun_channel_entry_t *entry = hdhomerun_channel_list_first(channel_list);
while (entry) {
if (entry->frequency == frequency) {
return entry->channel_number;
}
if (entry->frequency > frequency) {
return 0;
}
entry = hdhomerun_channel_list_next(channel_list, entry);
}
return 0;
}
static void hdhomerun_channel_list_build_insert(struct hdhomerun_channel_list_t *channel_list, struct hdhomerun_channel_entry_t *entry)
{
struct hdhomerun_channel_entry_t *prev = NULL;
struct hdhomerun_channel_entry_t *next = channel_list->head;
while (next) {
if (next->frequency > entry->frequency) {
break;
}
prev = next;
next = next->next;
}
entry->prev = prev;
entry->next = next;
if (prev) {
prev->next = entry;
} else {
channel_list->head = entry;
}
if (next) {
next->prev = entry;
} else {
channel_list->tail = entry;
}
}
static void hdhomerun_channel_list_build_range(struct hdhomerun_channel_list_t *channel_list, const char *channelmap, const struct hdhomerun_channelmap_range_t *range)
{
uint16_t channel_number;
for (channel_number = range->channel_range_start; channel_number <= range->channel_range_end; channel_number++) {
struct hdhomerun_channel_entry_t *entry = (struct hdhomerun_channel_entry_t *)calloc(1, sizeof(struct hdhomerun_channel_entry_t));
if (!entry) {
return;
}
entry->channel_number = channel_number;
entry->frequency = range->frequency + ((uint32_t)(channel_number - range->channel_range_start) * range->spacing);
entry->frequency = hdhomerun_channel_frequency_round_normal(entry->frequency);
hdhomerun_sprintf(entry->name, entry->name + sizeof(entry->name), "%s:%u", channelmap, entry->channel_number);
hdhomerun_channel_list_build_insert(channel_list, entry);
}
}
static void hdhomerun_channel_list_build_ranges(struct hdhomerun_channel_list_t *channel_list, const struct hdhomerun_channelmap_record_t *record)
{
const struct hdhomerun_channelmap_range_t *range = record->range_list;
while (range->frequency) {
hdhomerun_channel_list_build_range(channel_list, record->channelmap, range);
range++;
}
}
void hdhomerun_channel_list_destroy(struct hdhomerun_channel_list_t *channel_list)
{
while (channel_list->head) {
struct hdhomerun_channel_entry_t *entry = channel_list->head;
channel_list->head = entry->next;
free(entry);
}
free(channel_list);
}
struct hdhomerun_channel_list_t *hdhomerun_channel_list_create(const char *channelmap)
{
struct hdhomerun_channel_list_t *channel_list = (struct hdhomerun_channel_list_t *)calloc(1, sizeof(struct hdhomerun_channel_list_t));
if (!channel_list) {
return NULL;
}
const struct hdhomerun_channelmap_record_t *record = hdhomerun_channelmap_table;
while (record->channelmap) {
if (!strstr(channelmap, record->channelmap)) {
record++;
continue;
}
hdhomerun_channel_list_build_ranges(channel_list, record);
record++;
}
if (!channel_list->head) {
free(channel_list);
return NULL;
}
return channel_list;
}

52
hdhomerun_channels.h Normal file
View file

@ -0,0 +1,52 @@
/*
* hdhomerun_channels.h
*
* Copyright © 2007-2008 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
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifdef __cplusplus
extern "C" {
#endif
struct hdhomerun_channel_entry_t;
struct hdhomerun_channel_list_t;
extern LIBTYPE const char *hdhomerun_channelmap_get_channelmap_from_country_source(const char *countrycode, const char *source, const char *supported);
extern LIBTYPE const char *hdhomerun_channelmap_get_channelmap_scan_group(const char *channelmap);
extern LIBTYPE uint16_t hdhomerun_channel_entry_channel_number(struct hdhomerun_channel_entry_t *entry);
extern LIBTYPE uint32_t hdhomerun_channel_entry_frequency(struct hdhomerun_channel_entry_t *entry);
extern LIBTYPE const char *hdhomerun_channel_entry_name(struct hdhomerun_channel_entry_t *entry);
extern LIBTYPE struct hdhomerun_channel_list_t *hdhomerun_channel_list_create(const char *channelmap);
extern LIBTYPE void hdhomerun_channel_list_destroy(struct hdhomerun_channel_list_t *channel_list);
extern LIBTYPE struct hdhomerun_channel_entry_t *hdhomerun_channel_list_first(struct hdhomerun_channel_list_t *channel_list);
extern LIBTYPE struct hdhomerun_channel_entry_t *hdhomerun_channel_list_last(struct hdhomerun_channel_list_t *channel_list);
extern LIBTYPE struct hdhomerun_channel_entry_t *hdhomerun_channel_list_next(struct hdhomerun_channel_list_t *channel_list, struct hdhomerun_channel_entry_t *entry);
extern LIBTYPE struct hdhomerun_channel_entry_t *hdhomerun_channel_list_prev(struct hdhomerun_channel_list_t *channel_list, struct hdhomerun_channel_entry_t *entry);
extern LIBTYPE uint32_t hdhomerun_channel_list_total_count(struct hdhomerun_channel_list_t *channel_list);
extern LIBTYPE uint32_t hdhomerun_channel_list_frequency_count(struct hdhomerun_channel_list_t *channel_list);
extern LIBTYPE uint32_t hdhomerun_channel_frequency_round(uint32_t frequency, uint32_t resolution);
extern LIBTYPE uint32_t hdhomerun_channel_frequency_round_normal(uint32_t frequency);
extern LIBTYPE uint32_t hdhomerun_channel_number_to_frequency(struct hdhomerun_channel_list_t *channel_list, uint16_t channel_number);
extern LIBTYPE uint16_t hdhomerun_channel_frequency_to_number(struct hdhomerun_channel_list_t *channel_list, uint32_t frequency);
#ifdef __cplusplus
}
#endif

347
hdhomerun_channelscan.c Normal file
View file

@ -0,0 +1,347 @@
/*
* hdhomerun_channelscan.c
*
* 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
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "hdhomerun.h"
struct hdhomerun_channelscan_t {
struct hdhomerun_device_t *hd;
uint32_t scanned_channels;
struct hdhomerun_channel_list_t *channel_list;
struct hdhomerun_channel_entry_t *next_channel;
};
struct hdhomerun_channelscan_t *channelscan_create(struct hdhomerun_device_t *hd, const char *channelmap)
{
struct hdhomerun_channelscan_t *scan = (struct hdhomerun_channelscan_t *)calloc(1, sizeof(struct hdhomerun_channelscan_t));
if (!scan) {
return NULL;
}
scan->hd = hd;
scan->channel_list = hdhomerun_channel_list_create(channelmap);
if (!scan->channel_list) {
free(scan);
return NULL;
}
scan->next_channel = hdhomerun_channel_list_last(scan->channel_list);
return scan;
}
void channelscan_destroy(struct hdhomerun_channelscan_t *scan)
{
hdhomerun_channel_list_destroy(scan->channel_list);
free(scan);
}
static int channelscan_find_lock(struct hdhomerun_channelscan_t *scan, uint32_t frequency, struct hdhomerun_channelscan_result_t *result)
{
/* Set channel. */
char channel_str[64];
hdhomerun_sprintf(channel_str, channel_str + sizeof(channel_str), "auto:%u", (unsigned int)frequency);
int ret = hdhomerun_device_set_tuner_channel(scan->hd, channel_str);
if (ret <= 0) {
return ret;
}
/* Wait for lock. */
ret = hdhomerun_device_wait_for_lock(scan->hd, &result->status);
if (ret <= 0) {
return ret;
}
if (!result->status.lock_supported) {
return 1;
}
/* Wait for symbol quality = 100%. */
uint64_t timeout = getcurrenttime() + 5000;
while (1) {
ret = hdhomerun_device_get_tuner_status(scan->hd, NULL, &result->status);
if (ret <= 0) {
return ret;
}
if (result->status.symbol_error_quality == 100) {
return 1;
}
if (getcurrenttime() >= timeout) {
return 1;
}
msleep_approx(250);
}
}
static void channelscan_extract_name(struct hdhomerun_channelscan_program_t *program, const char *line)
{
/* Find start of name. */
const char *start = strchr(line, ' ');
if (!start) {
return;
}
start++;
start = strchr(start, ' ');
if (!start) {
return;
}
start++;
/* Find end of name. */
const char *end = strstr(start, " (");
if (!end) {
end = strchr(line, 0);
}
if (end <= start) {
return;
}
/* Extract name. */
size_t length = (size_t)(end - start);
if (length > sizeof(program->name) - 1) {
length = sizeof(program->name) - 1;
}
strncpy(program->name, start, length);
program->name[length] = 0;
}
static int channelscan_detect_programs(struct hdhomerun_channelscan_t *scan, struct hdhomerun_channelscan_result_t *result, bool_t *pchanged, bool_t *pincomplete)
{
*pchanged = FALSE;
*pincomplete = FALSE;
char *streaminfo;
int ret = hdhomerun_device_get_tuner_streaminfo(scan->hd, &streaminfo);
if (ret <= 0) {
return ret;
}
char *next_line = streaminfo;
int program_count = 0;
while (1) {
char *line = next_line;
next_line = strchr(line, '\n');
if (!next_line) {
break;
}
*next_line++ = 0;
unsigned int transport_stream_id;
if (sscanf(line, "tsid=0x%x", &transport_stream_id) == 1) {
result->transport_stream_id = transport_stream_id;
result->transport_stream_id_detected = TRUE;
continue;
}
unsigned int original_network_id;
if (sscanf(line, "onid=0x%x", &original_network_id) == 1) {
result->original_network_id = original_network_id;
result->original_network_id_detected = TRUE;
continue;
}
if (program_count >= HDHOMERUN_CHANNELSCAN_MAX_PROGRAM_COUNT) {
continue;
}
struct hdhomerun_channelscan_program_t program;
memset(&program, 0, sizeof(program));
hdhomerun_sprintf(program.program_str, program.program_str + sizeof(program.program_str), "%s", line);
unsigned int program_number;
unsigned int virtual_major, virtual_minor;
if (sscanf(line, "%u: %u.%u", &program_number, &virtual_major, &virtual_minor) != 3) {
if (sscanf(line, "%u: %u", &program_number, &virtual_major) != 2) {
continue;
}
virtual_minor = 0;
}
program.program_number = program_number;
program.virtual_major = virtual_major;
program.virtual_minor = virtual_minor;
channelscan_extract_name(&program, line);
if (strstr(line, "(control)")) {
program.type = HDHOMERUN_CHANNELSCAN_PROGRAM_CONTROL;
} else if (strstr(line, "(encrypted)")) {
program.type = HDHOMERUN_CHANNELSCAN_PROGRAM_ENCRYPTED;
} else if (strstr(line, "(no data)")) {
program.type = HDHOMERUN_CHANNELSCAN_PROGRAM_NODATA;
*pincomplete = TRUE;
} else {
program.type = HDHOMERUN_CHANNELSCAN_PROGRAM_NORMAL;
if ((program.virtual_major == 0) || (program.name[0] == 0)) {
*pincomplete = TRUE;
}
}
if (memcmp(&result->programs[program_count], &program, sizeof(program)) != 0) {
memcpy(&result->programs[program_count], &program, sizeof(program));
*pchanged = TRUE;
}
program_count++;
}
if (program_count == 0) {
*pincomplete = TRUE;
}
if (result->program_count != program_count) {
result->program_count = program_count;
*pchanged = TRUE;
}
return 1;
}
int channelscan_advance(struct hdhomerun_channelscan_t *scan, struct hdhomerun_channelscan_result_t *result)
{
memset(result, 0, sizeof(struct hdhomerun_channelscan_result_t));
struct hdhomerun_channel_entry_t *entry = scan->next_channel;
if (!entry) {
return 0;
}
/* Combine channels with same frequency. */
result->frequency = hdhomerun_channel_entry_frequency(entry);
char *ptr = result->channel_str;
char *end = result->channel_str + sizeof(result->channel_str);
hdhomerun_sprintf(ptr, end, hdhomerun_channel_entry_name(entry));
while (1) {
entry = hdhomerun_channel_list_prev(scan->channel_list, entry);
if (!entry) {
scan->next_channel = NULL;
break;
}
if (hdhomerun_channel_entry_frequency(entry) != result->frequency) {
scan->next_channel = entry;
break;
}
ptr = strchr(ptr, 0);
hdhomerun_sprintf(ptr, end, ", %s", hdhomerun_channel_entry_name(entry));
}
return 1;
}
int channelscan_detect(struct hdhomerun_channelscan_t *scan, struct hdhomerun_channelscan_result_t *result)
{
scan->scanned_channels++;
/* Find lock. */
int ret = channelscan_find_lock(scan, result->frequency, result);
if (ret <= 0) {
return ret;
}
if (!result->status.lock_supported) {
return 1;
}
/* Detect programs. */
result->program_count = 0;
uint64_t timeout;
if (strstr(hdhomerun_device_get_model_str(scan->hd), "atsc")) {
timeout = getcurrenttime() + 4000;
} else {
timeout = getcurrenttime() + 10000;
}
uint64_t complete_time = getcurrenttime() + 1000;
while (1) {
bool_t changed, incomplete;
ret = channelscan_detect_programs(scan, result, &changed, &incomplete);
if (ret <= 0) {
return ret;
}
if (changed) {
complete_time = getcurrenttime() + 1000;
}
if (!incomplete && (getcurrenttime() >= complete_time)) {
break;
}
if (getcurrenttime() >= timeout) {
break;
}
msleep_approx(250);
}
/* Lock => skip overlapping channels. */
uint32_t max_next_frequency = result->frequency - 5500000;
while (1) {
if (!scan->next_channel) {
break;
}
if (hdhomerun_channel_entry_frequency(scan->next_channel) <= max_next_frequency) {
break;
}
scan->next_channel = hdhomerun_channel_list_prev(scan->channel_list, scan->next_channel);
}
/* Success. */
return 1;
}
uint8_t channelscan_get_progress(struct hdhomerun_channelscan_t *scan)
{
struct hdhomerun_channel_entry_t *entry = scan->next_channel;
if (!entry) {
return 100;
}
uint32_t channels_remaining = 1;
uint32_t frequency = hdhomerun_channel_entry_frequency(entry);
while (1) {
entry = hdhomerun_channel_list_prev(scan->channel_list, entry);
if (!entry) {
break;
}
if (hdhomerun_channel_entry_frequency(entry) != frequency) {
channels_remaining++;
frequency = hdhomerun_channel_entry_frequency(entry);
}
}
return scan->scanned_channels * 100 / (scan->scanned_channels + channels_remaining);
}

41
hdhomerun_channelscan.h Normal file
View file

@ -0,0 +1,41 @@
/*
* hdhomerun_channelscan.h
*
* Copyright © 2007-2008 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
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifdef __cplusplus
extern "C" {
#endif
#define HDHOMERUN_CHANNELSCAN_PROGRAM_NORMAL 0
#define HDHOMERUN_CHANNELSCAN_PROGRAM_NODATA 1
#define HDHOMERUN_CHANNELSCAN_PROGRAM_CONTROL 2
#define HDHOMERUN_CHANNELSCAN_PROGRAM_ENCRYPTED 3
struct hdhomerun_channelscan_t;
extern LIBTYPE struct hdhomerun_channelscan_t *channelscan_create(struct hdhomerun_device_t *hd, const char *channelmap);
extern LIBTYPE void channelscan_destroy(struct hdhomerun_channelscan_t *scan);
extern LIBTYPE int channelscan_advance(struct hdhomerun_channelscan_t *scan, struct hdhomerun_channelscan_result_t *result);
extern LIBTYPE int channelscan_detect(struct hdhomerun_channelscan_t *scan, struct hdhomerun_channelscan_result_t *result);
extern LIBTYPE uint8_t channelscan_get_progress(struct hdhomerun_channelscan_t *scan);
#ifdef __cplusplus
}
#endif

693
hdhomerun_config.c Normal file
View file

@ -0,0 +1,693 @@
/*
* hdhomerun_config.c
*
* Copyright © 2006-2008 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
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "hdhomerun.h"
/*
* 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.
*/
#if defined(__WINDOWS__)
#define printf console_printf
#define vprintf console_vprintf
#endif
static const char *appname;
struct hdhomerun_device_t *hd;
static int help(void)
{
printf("Usage:\n");
printf("\t%s discover\n", appname);
printf("\t%s <id> get help\n", appname);
printf("\t%s <id> get <item>\n", appname);
printf("\t%s <id> set <item> <value>\n", appname);
printf("\t%s <id> scan <tuner> [<filename>]\n", appname);
printf("\t%s <id> save <tuner> <filename>\n", appname);
printf("\t%s <id> upgrade <filename>\n", appname);
return -1;
}
static void extract_appname(const char *argv0)
{
const char *ptr = strrchr(argv0, '/');
if (ptr) {
argv0 = ptr + 1;
}
ptr = strrchr(argv0, '\\');
if (ptr) {
argv0 = ptr + 1;
}
appname = argv0;
}
static bool_t contains(const char *arg, const char *cmpstr)
{
if (strcmp(arg, cmpstr) == 0) {
return TRUE;
}
if (*arg++ != '-') {
return FALSE;
}
if (*arg++ != '-') {
return FALSE;
}
if (strcmp(arg, cmpstr) == 0) {
return TRUE;
}
return FALSE;
}
static uint32_t parse_ip_addr(const char *str)
{
unsigned int a[4];
if (sscanf(str, "%u.%u.%u.%u", &a[0], &a[1], &a[2], &a[3]) != 4) {
return 0;
}
return (uint32_t)((a[0] << 24) | (a[1] << 16) | (a[2] << 8) | (a[3] << 0));
}
static int discover_print(char *target_ip_str)
{
uint32_t target_ip = 0;
if (target_ip_str) {
target_ip = parse_ip_addr(target_ip_str);
if (target_ip == 0) {
fprintf(stderr, "invalid ip address: %s\n", target_ip_str);
return -1;
}
}
struct hdhomerun_discover_device_t result_list[64];
int count = hdhomerun_discover_find_devices_custom_v2(target_ip, HDHOMERUN_DEVICE_TYPE_TUNER, HDHOMERUN_DEVICE_ID_WILDCARD, result_list, 64);
if (count < 0) {
fprintf(stderr, "error sending discover request\n");
return -1;
}
if (count == 0) {
printf("no devices found\n");
return 0;
}
int index;
for (index = 0; index < count; index++) {
struct hdhomerun_discover_device_t *result = &result_list[index];
printf("hdhomerun device %08X found at %u.%u.%u.%u\n",
(unsigned int)result->device_id,
(unsigned int)(result->ip_addr >> 24) & 0x0FF, (unsigned int)(result->ip_addr >> 16) & 0x0FF,
(unsigned int)(result->ip_addr >> 8) & 0x0FF, (unsigned int)(result->ip_addr >> 0) & 0x0FF
);
}
return count;
}
static int cmd_get(const char *item)
{
char *ret_value;
char *ret_error;
if (hdhomerun_device_get_var(hd, item, &ret_value, &ret_error) < 0) {
fprintf(stderr, "communication error sending request to hdhomerun device\n");
return -1;
}
if (ret_error) {
printf("%s\n", ret_error);
return 0;
}
printf("%s\n", ret_value);
return 1;
}
static int cmd_set_internal(const char *item, const char *value)
{
char *ret_error;
if (hdhomerun_device_set_var(hd, item, value, NULL, &ret_error) < 0) {
fprintf(stderr, "communication error sending request to hdhomerun device\n");
return -1;
}
if (ret_error) {
printf("%s\n", ret_error);
return 0;
}
return 1;
}
static int cmd_set(const char *item, const char *value)
{
if (strcmp(value, "-") == 0) {
char *buffer = NULL;
size_t pos = 0;
while (1) {
buffer = (char *)realloc(buffer, pos + 1024);
if (!buffer) {
fprintf(stderr, "out of memory\n");
return -1;
}
size_t size = fread(buffer + pos, 1, 1024, stdin);
pos += size;
if (size < 1024) {
break;
}
}
buffer[pos] = 0;
int ret = cmd_set_internal(item, buffer);
free(buffer);
return ret;
}
return cmd_set_internal(item, value);
}
static volatile sig_atomic_t sigabort_flag = FALSE;
static volatile sig_atomic_t siginfo_flag = FALSE;
static void sigabort_handler(int arg)
{
sigabort_flag = TRUE;
}
static void siginfo_handler(int arg)
{
siginfo_flag = TRUE;
}
static void register_signal_handlers(sig_t sigpipe_handler, sig_t sigint_handler, sig_t siginfo_handler)
{
#if defined(SIGPIPE)
signal(SIGPIPE, sigpipe_handler);
#endif
#if defined(SIGINT)
signal(SIGINT, sigint_handler);
#endif
#if defined(SIGINFO)
signal(SIGINFO, siginfo_handler);
#endif
}
static void cmd_scan_printf(FILE *fp, const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
if (fp) {
va_list apc;
va_copy(apc, ap);
vfprintf(fp, fmt, apc);
fflush(fp);
va_end(apc);
}
vprintf(fmt, ap);
fflush(stdout);
va_end(ap);
}
static int cmd_scan(const char *tuner_str, const char *filename)
{
if (hdhomerun_device_set_tuner_from_str(hd, tuner_str) <= 0) {
fprintf(stderr, "invalid tuner number\n");
return -1;
}
char *ret_error;
if (hdhomerun_device_tuner_lockkey_request(hd, &ret_error) <= 0) {
fprintf(stderr, "failed to lock tuner\n");
if (ret_error) {
fprintf(stderr, "%s\n", ret_error);
}
return -1;
}
hdhomerun_device_set_tuner_target(hd, "none");
char *channelmap;
if (hdhomerun_device_get_tuner_channelmap(hd, &channelmap) <= 0) {
fprintf(stderr, "failed to query channelmap from device\n");
return -1;
}
const char *channelmap_scan_group = hdhomerun_channelmap_get_channelmap_scan_group(channelmap);
if (!channelmap_scan_group) {
fprintf(stderr, "unknown channelmap '%s'\n", channelmap);
return -1;
}
if (hdhomerun_device_channelscan_init(hd, channelmap_scan_group) <= 0) {
fprintf(stderr, "failed to initialize channel scan\n");
return -1;
}
FILE *fp = NULL;
if (filename) {
fp = fopen(filename, "w");
if (!fp) {
fprintf(stderr, "unable to create file: %s\n", filename);
return -1;
}
}
register_signal_handlers(sigabort_handler, sigabort_handler, siginfo_handler);
int ret = 0;
while (!sigabort_flag) {
struct hdhomerun_channelscan_result_t result;
ret = hdhomerun_device_channelscan_advance(hd, &result);
if (ret <= 0) {
break;
}
cmd_scan_printf(fp, "SCANNING: %u (%s)\n",
(unsigned int)result.frequency, result.channel_str
);
ret = hdhomerun_device_channelscan_detect(hd, &result);
if (ret < 0) {
break;
}
if (ret == 0) {
continue;
}
cmd_scan_printf(fp, "LOCK: %s (ss=%u snq=%u seq=%u)\n",
result.status.lock_str, result.status.signal_strength,
result.status.signal_to_noise_quality, result.status.symbol_error_quality
);
if (result.transport_stream_id_detected) {
cmd_scan_printf(fp, "TSID: 0x%04X\n", result.transport_stream_id);
}
if (result.original_network_id_detected) {
cmd_scan_printf(fp, "ONID: 0x%04X\n", result.original_network_id);
}
int i;
for (i = 0; i < result.program_count; i++) {
struct hdhomerun_channelscan_program_t *program = &result.programs[i];
cmd_scan_printf(fp, "PROGRAM %s\n", program->program_str);
}
}
hdhomerun_device_tuner_lockkey_release(hd);
if (fp) {
fclose(fp);
}
if (ret < 0) {
fprintf(stderr, "communication error sending request to hdhomerun device\n");
}
return ret;
}
static void cmd_save_print_stats(void)
{
struct hdhomerun_video_stats_t stats;
hdhomerun_device_get_video_stats(hd, &stats);
fprintf(stderr, "%u packets received, %u overflow errors, %u network errors, %u transport errors, %u sequence errors\n",
(unsigned int)stats.packet_count,
(unsigned int)stats.overflow_error_count,
(unsigned int)stats.network_error_count,
(unsigned int)stats.transport_error_count,
(unsigned int)stats.sequence_error_count
);
}
static int cmd_save(const char *tuner_str, const char *filename)
{
if (hdhomerun_device_set_tuner_from_str(hd, tuner_str) <= 0) {
fprintf(stderr, "invalid tuner number\n");
return -1;
}
FILE *fp;
if (strcmp(filename, "null") == 0) {
fp = NULL;
} else if (strcmp(filename, "-") == 0) {
fp = stdout;
} else {
fp = fopen(filename, "wb");
if (!fp) {
fprintf(stderr, "unable to create file %s\n", filename);
return -1;
}
}
int ret = hdhomerun_device_stream_start(hd);
if (ret <= 0) {
fprintf(stderr, "unable to start stream\n");
if (fp && fp != stdout) {
fclose(fp);
}
return ret;
}
register_signal_handlers(sigabort_handler, sigabort_handler, siginfo_handler);
struct hdhomerun_video_stats_t stats_old, stats_cur;
hdhomerun_device_get_video_stats(hd, &stats_old);
uint64_t next_progress = getcurrenttime() + 1000;
while (!sigabort_flag) {
uint64_t loop_start_time = getcurrenttime();
if (siginfo_flag) {
fprintf(stderr, "\n");
cmd_save_print_stats();
siginfo_flag = FALSE;
}
size_t actual_size;
uint8_t *ptr = hdhomerun_device_stream_recv(hd, VIDEO_DATA_BUFFER_SIZE_1S, &actual_size);
if (!ptr) {
msleep_approx(64);
continue;
}
if (fp) {
if (fwrite(ptr, 1, actual_size, fp) != actual_size) {
fprintf(stderr, "error writing output\n");
return -1;
}
}
if (loop_start_time >= next_progress) {
next_progress += 1000;
if (loop_start_time >= next_progress) {
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) {
fprintf(stderr, "o");
} else if (stats_cur.network_error_count > stats_old.network_error_count) {
fprintf(stderr, "n");
} else if (stats_cur.transport_error_count > stats_old.transport_error_count) {
fprintf(stderr, "t");
} else if (stats_cur.sequence_error_count > stats_old.sequence_error_count) {
fprintf(stderr, "s");
} else {
fprintf(stderr, ".");
}
stats_old = stats_cur;
fflush(stderr);
}
int32_t delay = 64 - (int32_t)(getcurrenttime() - loop_start_time);
if (delay <= 0) {
continue;
}
msleep_approx(delay);
}
if (fp) {
fclose(fp);
}
hdhomerun_device_stream_stop(hd);
fprintf(stderr, "\n");
fprintf(stderr, "-- Video statistics --\n");
cmd_save_print_stats();
return 0;
}
static int cmd_upgrade(const char *filename)
{
FILE *fp = fopen(filename, "rb");
if (!fp) {
fprintf(stderr, "unable to open file %s\n", filename);
return -1;
}
printf("uploading firmware...\n");
if (hdhomerun_device_upgrade(hd, fp) <= 0) {
fprintf(stderr, "error sending upgrade file to hdhomerun device\n");
fclose(fp);
return -1;
}
fclose(fp);
msleep_minimum(2000);
printf("upgrading firmware...\n");
msleep_minimum(8000);
printf("rebooting...\n");
int count = 0;
char *version_str;
while (1) {
if (hdhomerun_device_get_version(hd, &version_str, NULL) >= 0) {
break;
}
count++;
if (count > 30) {
fprintf(stderr, "error finding device after firmware upgrade\n");
return -1;
}
msleep_minimum(1000);
}
printf("upgrade complete - now running firmware %s\n", version_str);
return 0;
}
static int cmd_execute(void)
{
char *ret_value;
char *ret_error;
if (hdhomerun_device_get_var(hd, "/sys/boot", &ret_value, &ret_error) < 0) {
fprintf(stderr, "communication error sending request to hdhomerun device\n");
return -1;
}
if (ret_error) {
printf("%s\n", ret_error);
return 0;
}
char *end = ret_value + strlen(ret_value);
char *pos = ret_value;
while (1) {
if (pos >= end) {
break;
}
char *eol_r = strchr(pos, '\r');
if (!eol_r) {
eol_r = end;
}
char *eol_n = strchr(pos, '\n');
if (!eol_n) {
eol_n = end;
}
char *eol = eol_r;
if (eol_n < eol) {
eol = eol_n;
}
char *sep = strchr(pos, ' ');
if (!sep || sep > eol) {
pos = eol + 1;
continue;
}
*sep = 0;
*eol = 0;
char *item = pos;
char *value = sep + 1;
printf("set %s \"%s\"\n", item, value);
cmd_set_internal(item, value);
pos = eol + 1;
}
return 1;
}
static int main_cmd(int argc, char *argv[])
{
if (argc < 1) {
return help();
}
char *cmd = *argv++; argc--;
if (contains(cmd, "key")) {
if (argc < 2) {
return help();
}
uint32_t lockkey = strtoul(argv[0], NULL, 0);
hdhomerun_device_tuner_lockkey_use_value(hd, lockkey);
cmd = argv[1];
argv+=2; argc-=2;
}
if (contains(cmd, "get")) {
if (argc < 1) {
return help();
}
return cmd_get(argv[0]);
}
if (contains(cmd, "set")) {
if (argc < 2) {
return help();
}
return cmd_set(argv[0], argv[1]);
}
if (contains(cmd, "scan")) {
if (argc < 1) {
return help();
}
if (argc < 2) {
return cmd_scan(argv[0], NULL);
} else {
return cmd_scan(argv[0], argv[1]);
}
}
if (contains(cmd, "save")) {
if (argc < 2) {
return help();
}
return cmd_save(argv[0], argv[1]);
}
if (contains(cmd, "upgrade")) {
if (argc < 1) {
return help();
}
return cmd_upgrade(argv[0]);
}
if (contains(cmd, "execute")) {
return cmd_execute();
}
return help();
}
static int main_internal(int argc, char *argv[])
{
#if defined(__WINDOWS__)
/* Initialize network socket support. */
WORD wVersionRequested = MAKEWORD(2, 0);
WSADATA wsaData;
WSAStartup(wVersionRequested, &wsaData);
#endif
extract_appname(argv[0]);
argv++;
argc--;
if (argc == 0) {
return help();
}
char *id_str = *argv++; argc--;
if (contains(id_str, "help")) {
return help();
}
if (contains(id_str, "discover")) {
if (argc < 1) {
return discover_print(NULL);
} else {
return discover_print(argv[0]);
}
}
/* Device object. */
hd = hdhomerun_device_create_from_str(id_str, NULL);
if (!hd) {
fprintf(stderr, "invalid device id: %s\n", id_str);
return -1;
}
/* Device ID check. */
uint32_t device_id_requested = hdhomerun_device_get_device_id_requested(hd);
if (!hdhomerun_discover_validate_device_id(device_id_requested)) {
fprintf(stderr, "invalid device id: %08X\n", (unsigned int)device_id_requested);
}
/* Connect to device and check model. */
const char *model = hdhomerun_device_get_model_str(hd);
if (!model) {
fprintf(stderr, "unable to connect to device\n");
hdhomerun_device_destroy(hd);
return -1;
}
/* Command. */
int ret = main_cmd(argc, argv);
/* Cleanup. */
hdhomerun_device_destroy(hd);
/* Complete. */
return ret;
}
int main(int argc, char *argv[])
{
int ret = main_internal(argc, argv);
if (ret <= 0) {
return 1;
}
return 0;
}

431
hdhomerun_control.c Normal file
View file

@ -0,0 +1,431 @@
/*
* hdhomerun_control.c
*
* 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
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "hdhomerun.h"
#define HDHOMERUN_CONTROL_CONNECT_TIMEOUT 2500
#define HDHOMERUN_CONTROL_SEND_TIMEOUT 2500
#define HDHOMERUN_CONTROL_RECV_TIMEOUT 2500
#define HDHOMERUN_CONTROL_UPGRADE_TIMEOUT 30000
struct hdhomerun_control_sock_t {
uint32_t desired_device_id;
uint32_t desired_device_ip;
uint32_t actual_device_id;
uint32_t actual_device_ip;
hdhomerun_sock_t sock;
struct hdhomerun_debug_t *dbg;
struct hdhomerun_pkt_t tx_pkt;
struct hdhomerun_pkt_t rx_pkt;
};
static void hdhomerun_control_close_sock(struct hdhomerun_control_sock_t *cs)
{
if (cs->sock == HDHOMERUN_SOCK_INVALID) {
return;
}
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)
{
hdhomerun_control_close_sock(cs);
cs->desired_device_id = device_id;
cs->desired_device_ip = device_ip;
cs->actual_device_id = 0;
cs->actual_device_ip = 0;
}
struct hdhomerun_control_sock_t *hdhomerun_control_create(uint32_t device_id, uint32_t device_ip, struct hdhomerun_debug_t *dbg)
{
struct hdhomerun_control_sock_t *cs = (struct hdhomerun_control_sock_t *)calloc(1, sizeof(struct hdhomerun_control_sock_t));
if (!cs) {
hdhomerun_debug_printf(dbg, "hdhomerun_control_create: failed to allocate control object\n");
return NULL;
}
cs->dbg = dbg;
cs->sock = HDHOMERUN_SOCK_INVALID;
hdhomerun_control_set_device(cs, device_id, device_ip);
return cs;
}
void hdhomerun_control_destroy(struct hdhomerun_control_sock_t *cs)
{
hdhomerun_control_close_sock(cs);
free(cs);
}
static bool_t hdhomerun_control_connect_sock(struct hdhomerun_control_sock_t *cs)
{
if (cs->sock != HDHOMERUN_SOCK_INVALID) {
return TRUE;
}
if ((cs->desired_device_id == 0) && (cs->desired_device_ip == 0)) {
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;
if (hdhomerun_discover_find_devices_custom_v2(cs->desired_device_ip, HDHOMERUN_DEVICE_TYPE_WILDCARD, cs->desired_device_id, &result, 1) <= 0) {
hdhomerun_debug_printf(cs->dbg, "hdhomerun_control_connect_sock: device not found\n");
return FALSE;
}
cs->actual_device_ip = result.ip_addr;
cs->actual_device_id = result.device_id;
/* Create socket. */
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;
}
/* Initiate connection. */
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;
}
/* Success. */
return TRUE;
}
uint32_t hdhomerun_control_get_device_id(struct hdhomerun_control_sock_t *cs)
{
if (!hdhomerun_control_connect_sock(cs)) {
hdhomerun_debug_printf(cs->dbg, "hdhomerun_control_get_device_id: connect failed\n");
return 0;
}
return cs->actual_device_id;
}
uint32_t hdhomerun_control_get_device_ip(struct hdhomerun_control_sock_t *cs)
{
if (!hdhomerun_control_connect_sock(cs)) {
hdhomerun_debug_printf(cs->dbg, "hdhomerun_control_get_device_ip: connect failed\n");
return 0;
}
return cs->actual_device_ip;
}
uint32_t hdhomerun_control_get_device_id_requested(struct hdhomerun_control_sock_t *cs)
{
return cs->desired_device_id;
}
uint32_t hdhomerun_control_get_device_ip_requested(struct hdhomerun_control_sock_t *cs)
{
return cs->desired_device_ip;
}
uint32_t hdhomerun_control_get_local_addr(struct hdhomerun_control_sock_t *cs)
{
if (!hdhomerun_control_connect_sock(cs)) {
hdhomerun_debug_printf(cs->dbg, "hdhomerun_control_get_local_addr: connect failed\n");
return 0;
}
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 addr;
}
static bool_t hdhomerun_control_send_sock(struct hdhomerun_control_sock_t *cs, struct hdhomerun_pkt_t *tx_pkt)
{
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 FALSE;
}
return TRUE;
}
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 (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 FALSE;
}
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 FALSE;
}
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 FALSE;
}
if (ret > 0) {
return TRUE;
}
}
}
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)
{
hdhomerun_pkt_seal_frame(tx_pkt, type);
int i;
for (i = 0; i < 2; i++) {
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)) {
continue;
}
if (!rx_pkt) {
return 1;
}
uint16_t rsp_type;
if (!hdhomerun_control_recv_sock(cs, rx_pkt, &rsp_type, recv_timeout)) {
continue;
}
if (rsp_type != type + 1) {
hdhomerun_debug_printf(cs->dbg, "hdhomerun_control_send_recv: unexpected frame type\n");
hdhomerun_control_close_sock(cs);
continue;
}
return 1;
}
hdhomerun_debug_printf(cs->dbg, "hdhomerun_control_send_recv: failed\n");
return -1;
}
int hdhomerun_control_send_recv(struct hdhomerun_control_sock_t *cs, struct hdhomerun_pkt_t *tx_pkt, struct hdhomerun_pkt_t *rx_pkt, uint16_t type)
{
return hdhomerun_control_send_recv_internal(cs, tx_pkt, rx_pkt, type, HDHOMERUN_CONTROL_RECV_TIMEOUT);
}
static int hdhomerun_control_get_set(struct hdhomerun_control_sock_t *cs, const char *name, const char *value, uint32_t lockkey, char **pvalue, char **perror)
{
struct hdhomerun_pkt_t *tx_pkt = &cs->tx_pkt;
struct hdhomerun_pkt_t *rx_pkt = &cs->rx_pkt;
/* Request. */
hdhomerun_pkt_reset(tx_pkt);
int name_len = (int)strlen(name) + 1;
if (tx_pkt->end + 3 + name_len > tx_pkt->limit) {
hdhomerun_debug_printf(cs->dbg, "hdhomerun_control_get_set: request too long\n");
return -1;
}
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);
if (value) {
int value_len = (int)strlen(value) + 1;
if (tx_pkt->end + 3 + value_len > tx_pkt->limit) {
hdhomerun_debug_printf(cs->dbg, "hdhomerun_control_get_set: request too long\n");
return -1;
}
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);
}
if (lockkey != 0) {
if (tx_pkt->end + 6 > tx_pkt->limit) {
hdhomerun_debug_printf(cs->dbg, "hdhomerun_control_get_set: request too long\n");
return -1;
}
hdhomerun_pkt_write_u8(tx_pkt, HDHOMERUN_TAG_GETSET_LOCKKEY);
hdhomerun_pkt_write_var_length(tx_pkt, 4);
hdhomerun_pkt_write_u32(tx_pkt, lockkey);
}
/* Send/Recv. */
if (hdhomerun_control_send_recv_internal(cs, tx_pkt, rx_pkt, HDHOMERUN_TYPE_GETSET_REQ, HDHOMERUN_CONTROL_RECV_TIMEOUT) < 0) {
hdhomerun_debug_printf(cs->dbg, "hdhomerun_control_get_set: send/recv error\n");
return -1;
}
/* Response. */
while (1) {
uint8_t tag;
size_t len;
uint8_t *next = hdhomerun_pkt_read_tlv(rx_pkt, &tag, &len);
if (!next) {
break;
}
switch (tag) {
case HDHOMERUN_TAG_GETSET_VALUE:
if (pvalue) {
*pvalue = (char *)rx_pkt->pos;
rx_pkt->pos[len] = 0;
}
if (perror) {
*perror = NULL;
}
return 1;
case HDHOMERUN_TAG_ERROR_MESSAGE:
rx_pkt->pos[len] = 0;
hdhomerun_debug_printf(cs->dbg, "hdhomerun_control_get_set: %s\n", rx_pkt->pos);
if (pvalue) {
*pvalue = NULL;
}
if (perror) {
*perror = (char *)rx_pkt->pos;
}
return 0;
}
rx_pkt->pos = next;
}
hdhomerun_debug_printf(cs->dbg, "hdhomerun_control_get_set: missing response tags\n");
return -1;
}
int hdhomerun_control_get(struct hdhomerun_control_sock_t *cs, const char *name, char **pvalue, char **perror)
{
return hdhomerun_control_get_set(cs, name, NULL, 0, pvalue, perror);
}
int hdhomerun_control_set(struct hdhomerun_control_sock_t *cs, const char *name, const char *value, char **pvalue, char **perror)
{
return hdhomerun_control_get_set(cs, name, value, 0, pvalue, perror);
}
int hdhomerun_control_set_with_lockkey(struct hdhomerun_control_sock_t *cs, const char *name, const char *value, uint32_t lockkey, char **pvalue, char **perror)
{
return hdhomerun_control_get_set(cs, name, value, lockkey, pvalue, perror);
}
int hdhomerun_control_upgrade(struct hdhomerun_control_sock_t *cs, FILE *upgrade_file)
{
struct hdhomerun_pkt_t *tx_pkt = &cs->tx_pkt;
struct hdhomerun_pkt_t *rx_pkt = &cs->rx_pkt;
bool_t upload_delay = FALSE;
uint32_t sequence = 0;
/* Special case detection. */
char *version_str;
int ret = hdhomerun_control_get(cs, "/sys/version", &version_str, NULL);
if (ret > 0) {
upload_delay = strcmp(version_str, "20120704beta1") == 0;
}
/* Upload. */
while (1) {
uint8_t data[1024];
size_t length = fread(data, 1, 1024, upgrade_file);
if (length == 0) {
break;
}
hdhomerun_pkt_reset(tx_pkt);
hdhomerun_pkt_write_u32(tx_pkt, sequence);
hdhomerun_pkt_write_mem(tx_pkt, data, length);
if (hdhomerun_control_send_recv_internal(cs, tx_pkt, NULL, HDHOMERUN_TYPE_UPGRADE_REQ, 0) < 0) {
hdhomerun_debug_printf(cs->dbg, "hdhomerun_control_upgrade: send/recv failed\n");
return -1;
}
sequence += (uint32_t)length;
if (upload_delay) {
msleep_approx(25);
}
}
if (sequence == 0) {
/* No data in file. Error, but no need to close connection. */
hdhomerun_debug_printf(cs->dbg, "hdhomerun_control_upgrade: zero length file\n");
return 0;
}
/* Execute upgrade. */
hdhomerun_pkt_reset(tx_pkt);
hdhomerun_pkt_write_u32(tx_pkt, 0xFFFFFFFF);
if (hdhomerun_control_send_recv_internal(cs, tx_pkt, rx_pkt, HDHOMERUN_TYPE_UPGRADE_REQ, HDHOMERUN_CONTROL_UPGRADE_TIMEOUT) < 0) {
hdhomerun_debug_printf(cs->dbg, "hdhomerun_control_upgrade: send/recv failed\n");
return -1;
}
/* Check response. */
while (1) {
uint8_t tag;
size_t len;
uint8_t *next = hdhomerun_pkt_read_tlv(rx_pkt, &tag, &len);
if (!next) {
break;
}
switch (tag) {
case HDHOMERUN_TAG_ERROR_MESSAGE:
rx_pkt->pos[len] = 0;
hdhomerun_debug_printf(cs->dbg, "hdhomerun_control_upgrade: %s\n", (char *)rx_pkt->pos);
return 0;
default:
break;
}
rx_pkt->pos = next;
}
return 1;
}

103
hdhomerun_control.h Normal file
View file

@ -0,0 +1,103 @@
/*
* hdhomerun_control.h
*
* Copyright © 2006 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
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifdef __cplusplus
extern "C" {
#endif
struct hdhomerun_control_sock_t;
/*
* Create a control socket.
*
* This function will not attempt to connect to the device.
* The connection will be established when first used.
*
* uint32_t device_id = 32-bit device id of device. Set to HDHOMERUN_DEVICE_ID_WILDCARD to match any device ID.
* uint32_t device_ip = IP address of device. Set to 0 to auto-detect.
* struct hdhomerun_debug_t *dbg: Pointer to debug logging object. May be NULL.
*
* Returns a pointer to the newly created control socket.
*
* When no longer needed, the socket should be destroyed by calling hdhomerun_control_destroy.
*/
extern LIBTYPE struct hdhomerun_control_sock_t *hdhomerun_control_create(uint32_t device_id, uint32_t device_ip, struct hdhomerun_debug_t *dbg);
extern LIBTYPE void hdhomerun_control_destroy(struct hdhomerun_control_sock_t *cs);
/*
* Get the actual device id or ip of the device.
*
* Returns 0 if the device id cannot be determined.
*/
extern LIBTYPE uint32_t hdhomerun_control_get_device_id(struct hdhomerun_control_sock_t *cs);
extern LIBTYPE uint32_t hdhomerun_control_get_device_ip(struct hdhomerun_control_sock_t *cs);
extern LIBTYPE uint32_t hdhomerun_control_get_device_id_requested(struct hdhomerun_control_sock_t *cs);
extern LIBTYPE uint32_t hdhomerun_control_get_device_ip_requested(struct hdhomerun_control_sock_t *cs);
extern LIBTYPE void hdhomerun_control_set_device(struct hdhomerun_control_sock_t *cs, uint32_t device_id, uint32_t device_ip);
/*
* Get the local machine IP address used when communicating with the device.
*
* This function is useful for determining the IP address to use with set target commands.
*
* Returns 32-bit IP address with native endianness, or 0 on error.
*/
extern LIBTYPE uint32_t hdhomerun_control_get_local_addr(struct hdhomerun_control_sock_t *cs);
/*
* Low-level communication.
*/
extern LIBTYPE int hdhomerun_control_send_recv(struct hdhomerun_control_sock_t *cs, struct hdhomerun_pkt_t *tx_pkt, struct hdhomerun_pkt_t *rx_pkt, uint16_t type);
/*
* Get/set a control variable on the device.
*
* const char *name: The name of var to get/set (c-string). The supported vars is device/firmware dependant.
* const char *value: The value to set (c-string). The format is device/firmware dependant.
* char **pvalue: If provided, the caller-supplied char pointer will be populated with a pointer to the value
* string returned by the device, or NULL if the device returned an error string. The string will remain
* valid until the next call to a control sock function.
* char **perror: If provided, the caller-supplied char pointer will be populated with a pointer to the error
* string returned by the device, or NULL if the device returned an value string. The string will remain
* valid until the next call to a control sock function.
*
* Returns 1 if the operation was successful (pvalue set, perror NULL).
* Returns 0 if the operation was rejected (pvalue NULL, perror set).
* Returns -1 if a communication error occurs.
*/
extern LIBTYPE int hdhomerun_control_get(struct hdhomerun_control_sock_t *cs, const char *name, char **pvalue, char **perror);
extern LIBTYPE int hdhomerun_control_set(struct hdhomerun_control_sock_t *cs, const char *name, const char *value, char **pvalue, char **perror);
extern LIBTYPE int hdhomerun_control_set_with_lockkey(struct hdhomerun_control_sock_t *cs, const char *name, const char *value, uint32_t lockkey, char **pvalue, char **perror);
/*
* 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.
*
* Returns 1 if the upload succeeded.
* Returns 0 if the upload was rejected.
* Returns -1 if an error occurs.
*/
extern LIBTYPE int hdhomerun_control_upgrade(struct hdhomerun_control_sock_t *cs, FILE *upgrade_file);
#ifdef __cplusplus
}
#endif

449
hdhomerun_debug.c Normal file
View file

@ -0,0 +1,449 @@
/*
* hdhomerun_debug.c
*
* 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
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
/*
* The debug logging includes optional support for connecting to the
* Silicondust support server. This option should not be used without
* being explicitly enabled by the user. Debug information should be
* limited to information useful to diagnosing a problem.
* - Silicondust.
*/
#include "hdhomerun.h"
#if !defined(HDHOMERUN_DEBUG_HOST)
#define HDHOMERUN_DEBUG_HOST "debug.silicondust.com"
#endif
#if !defined(HDHOMERUN_DEBUG_PORT)
#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;
struct hdhomerun_debug_message_t *prev;
char buffer[2048];
};
struct hdhomerun_debug_t
{
pthread_t thread;
volatile bool_t enabled;
volatile bool_t terminate;
char *prefix;
pthread_mutex_t print_lock;
pthread_mutex_t queue_lock;
pthread_mutex_t send_lock;
struct hdhomerun_debug_message_t *queue_head;
struct hdhomerun_debug_message_t *queue_tail;
uint32_t queue_depth;
uint64_t connect_delay;
char *file_name;
FILE *file_fp;
hdhomerun_sock_t sock;
};
static THREAD_FUNC_PREFIX hdhomerun_debug_thread_execute(void *arg);
struct hdhomerun_debug_t *hdhomerun_debug_create(void)
{
struct hdhomerun_debug_t *dbg = (struct hdhomerun_debug_t *)calloc(1, sizeof(struct hdhomerun_debug_t));
if (!dbg) {
return NULL;
}
dbg->sock = HDHOMERUN_SOCK_INVALID;
pthread_mutex_init(&dbg->print_lock, NULL);
pthread_mutex_init(&dbg->queue_lock, NULL);
pthread_mutex_init(&dbg->send_lock, NULL);
if (pthread_create(&dbg->thread, NULL, &hdhomerun_debug_thread_execute, dbg) != 0) {
free(dbg);
return NULL;
}
return dbg;
}
void hdhomerun_debug_destroy(struct hdhomerun_debug_t *dbg)
{
if (!dbg) {
return;
}
dbg->terminate = TRUE;
pthread_join(dbg->thread, NULL);
if (dbg->prefix) {
free(dbg->prefix);
}
if (dbg->file_name) {
free(dbg->file_name);
}
if (dbg->file_fp) {
fclose(dbg->file_fp);
}
if (dbg->sock != HDHOMERUN_SOCK_INVALID) {
hdhomerun_sock_destroy(dbg->sock);
}
free(dbg);
}
/* Send lock held by caller */
static void hdhomerun_debug_close_internal(struct hdhomerun_debug_t *dbg)
{
if (dbg->file_fp) {
fclose(dbg->file_fp);
dbg->file_fp = NULL;
}
if (dbg->sock != HDHOMERUN_SOCK_INVALID) {
hdhomerun_sock_destroy(dbg->sock);
dbg->sock = HDHOMERUN_SOCK_INVALID;
}
}
void hdhomerun_debug_close(struct hdhomerun_debug_t *dbg, uint64_t timeout)
{
if (!dbg) {
return;
}
if (timeout > 0) {
hdhomerun_debug_flush(dbg, timeout);
}
pthread_mutex_lock(&dbg->send_lock);
hdhomerun_debug_close_internal(dbg);
dbg->connect_delay = 0;
pthread_mutex_unlock(&dbg->send_lock);
}
void hdhomerun_debug_set_filename(struct hdhomerun_debug_t *dbg, const char *filename)
{
if (!dbg) {
return;
}
pthread_mutex_lock(&dbg->send_lock);
if (!filename && !dbg->file_name) {
pthread_mutex_unlock(&dbg->send_lock);
return;
}
if (filename && dbg->file_name) {
if (strcmp(filename, dbg->file_name) == 0) {
pthread_mutex_unlock(&dbg->send_lock);
return;
}
}
hdhomerun_debug_close_internal(dbg);
dbg->connect_delay = 0;
if (dbg->file_name) {
free(dbg->file_name);
dbg->file_name = NULL;
}
if (filename) {
dbg->file_name = strdup(filename);
}
pthread_mutex_unlock(&dbg->send_lock);
}
void hdhomerun_debug_set_prefix(struct hdhomerun_debug_t *dbg, const char *prefix)
{
if (!dbg) {
return;
}
pthread_mutex_lock(&dbg->print_lock);
if (dbg->prefix) {
free(dbg->prefix);
dbg->prefix = NULL;
}
if (prefix) {
dbg->prefix = strdup(prefix);
}
pthread_mutex_unlock(&dbg->print_lock);
}
void hdhomerun_debug_enable(struct hdhomerun_debug_t *dbg)
{
if (!dbg) {
return;
}
dbg->enabled = TRUE;
}
void hdhomerun_debug_disable(struct hdhomerun_debug_t *dbg)
{
if (!dbg) {
return;
}
dbg->enabled = FALSE;
}
bool_t hdhomerun_debug_enabled(struct hdhomerun_debug_t *dbg)
{
if (!dbg) {
return FALSE;
}
return dbg->enabled;
}
void hdhomerun_debug_flush(struct hdhomerun_debug_t *dbg, uint64_t timeout)
{
if (!dbg) {
return;
}
timeout = getcurrenttime() + timeout;
while (getcurrenttime() < timeout) {
pthread_mutex_lock(&dbg->queue_lock);
struct hdhomerun_debug_message_t *message = dbg->queue_tail;
pthread_mutex_unlock(&dbg->queue_lock);
if (!message) {
return;
}
msleep_approx(10);
}
}
void hdhomerun_debug_printf(struct hdhomerun_debug_t *dbg, const char *fmt, ...)
{
va_list args;
va_start(args, fmt);
hdhomerun_debug_vprintf(dbg, fmt, args);
va_end(args);
}
void hdhomerun_debug_vprintf(struct hdhomerun_debug_t *dbg, const char *fmt, va_list args)
{
if (!dbg) {
return;
}
if (!dbg->enabled) {
return;
}
struct hdhomerun_debug_message_t *message = (struct hdhomerun_debug_message_t *)malloc(sizeof(struct hdhomerun_debug_message_t));
if (!message) {
return;
}
char *ptr = message->buffer;
char *end = message->buffer + sizeof(message->buffer) - 2;
*end = 0;
/*
* Timestamp.
*/
time_t current_time = time(NULL);
ptr += strftime(ptr, end - ptr, "%Y%m%d-%H:%M:%S ", localtime(&current_time));
if (ptr > end) {
ptr = end;
}
/*
* Debug prefix.
*/
pthread_mutex_lock(&dbg->print_lock);
if (dbg->prefix) {
hdhomerun_sprintf(ptr, end, "%s ", dbg->prefix);
ptr = strchr(ptr, 0);
}
pthread_mutex_unlock(&dbg->print_lock);
/*
* Message text.
*/
hdhomerun_vsprintf(ptr, end, fmt, args);
ptr = strchr(ptr, 0);
/*
* Force newline.
*/
if (ptr[-1] != '\n') {
hdhomerun_sprintf(ptr, end, "\n");
}
/*
* Enqueue.
*/
pthread_mutex_lock(&dbg->queue_lock);
message->prev = NULL;
message->next = dbg->queue_head;
dbg->queue_head = message;
if (message->next) {
message->next->prev = message;
} else {
dbg->queue_tail = message;
}
dbg->queue_depth++;
pthread_mutex_unlock(&dbg->queue_lock);
}
/* Send lock held by caller */
static bool_t hdhomerun_debug_output_message_file(struct hdhomerun_debug_t *dbg, struct hdhomerun_debug_message_t *message)
{
if (!dbg->file_fp) {
uint64_t current_time = getcurrenttime();
if (current_time < dbg->connect_delay) {
return FALSE;
}
dbg->connect_delay = current_time + 30*1000;
dbg->file_fp = fopen(dbg->file_name, "a");
if (!dbg->file_fp) {
return FALSE;
}
}
fprintf(dbg->file_fp, "%s", message->buffer);
fflush(dbg->file_fp);
return TRUE;
}
/* Send lock held by caller */
static bool_t hdhomerun_debug_output_message_sock(struct hdhomerun_debug_t *dbg, struct hdhomerun_debug_message_t *message)
{
if (dbg->sock == HDHOMERUN_SOCK_INVALID) {
uint64_t current_time = getcurrenttime();
if (current_time < dbg->connect_delay) {
return FALSE;
}
dbg->connect_delay = current_time + HDHOMERUN_DEBUG_CONNECT_RETRY_TIME;
dbg->sock = hdhomerun_sock_create_tcp();
if (dbg->sock == HDHOMERUN_SOCK_INVALID) {
return FALSE;
}
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 (!hdhomerun_sock_connect(dbg->sock, remote_addr, HDHOMERUN_DEBUG_PORT, HDHOMERUN_DEBUG_CONNECT_TIMEOUT)) {
hdhomerun_debug_close_internal(dbg);
return FALSE;
}
}
size_t length = strlen(message->buffer);
if (!hdhomerun_sock_send(dbg->sock, message->buffer, length, HDHOMERUN_DEBUG_SEND_TIMEOUT)) {
hdhomerun_debug_close_internal(dbg);
return FALSE;
}
return TRUE;
}
static bool_t hdhomerun_debug_output_message(struct hdhomerun_debug_t *dbg, struct hdhomerun_debug_message_t *message)
{
pthread_mutex_lock(&dbg->send_lock);
bool_t ret;
if (dbg->file_name) {
ret = hdhomerun_debug_output_message_file(dbg, message);
} else {
ret = hdhomerun_debug_output_message_sock(dbg, message);
}
pthread_mutex_unlock(&dbg->send_lock);
return ret;
}
static void hdhomerun_debug_pop_and_free_message(struct hdhomerun_debug_t *dbg)
{
pthread_mutex_lock(&dbg->queue_lock);
struct hdhomerun_debug_message_t *message = dbg->queue_tail;
dbg->queue_tail = message->prev;
if (message->prev) {
message->prev->next = NULL;
} else {
dbg->queue_head = NULL;
}
dbg->queue_depth--;
pthread_mutex_unlock(&dbg->queue_lock);
free(message);
}
static THREAD_FUNC_PREFIX hdhomerun_debug_thread_execute(void *arg)
{
struct hdhomerun_debug_t *dbg = (struct hdhomerun_debug_t *)arg;
while (!dbg->terminate) {
pthread_mutex_lock(&dbg->queue_lock);
struct hdhomerun_debug_message_t *message = dbg->queue_tail;
uint32_t queue_depth = dbg->queue_depth;
pthread_mutex_unlock(&dbg->queue_lock);
if (!message) {
msleep_approx(250);
continue;
}
if (queue_depth > 1024) {
hdhomerun_debug_pop_and_free_message(dbg);
continue;
}
if (!hdhomerun_debug_output_message(dbg, message)) {
msleep_approx(250);
continue;
}
hdhomerun_debug_pop_and_free_message(dbg);
}
return 0;
}

52
hdhomerun_debug.h Normal file
View file

@ -0,0 +1,52 @@
/*
* hdhomerun_debug.h
*
* Copyright © 2006 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
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
/*
* The debug logging includes optional support for connecting to the
* Silicondust support server. This option should not be used without
* being explicitly enabled by the user. Debug information should be
* limited to information useful to diagnosing a problem.
* - Silicondust.
*/
#ifdef __cplusplus
extern "C" {
#endif
struct hdhomerun_debug_t;
extern LIBTYPE struct hdhomerun_debug_t *hdhomerun_debug_create(void);
extern LIBTYPE void hdhomerun_debug_destroy(struct hdhomerun_debug_t *dbg);
extern LIBTYPE void hdhomerun_debug_set_prefix(struct hdhomerun_debug_t *dbg, const char *prefix);
extern LIBTYPE void hdhomerun_debug_set_filename(struct hdhomerun_debug_t *dbg, const char *filename);
extern LIBTYPE void hdhomerun_debug_enable(struct hdhomerun_debug_t *dbg);
extern LIBTYPE void hdhomerun_debug_disable(struct hdhomerun_debug_t *dbg);
extern LIBTYPE bool_t hdhomerun_debug_enabled(struct hdhomerun_debug_t *dbg);
extern LIBTYPE void hdhomerun_debug_flush(struct hdhomerun_debug_t *dbg, uint64_t timeout);
extern LIBTYPE void hdhomerun_debug_close(struct hdhomerun_debug_t *dbg, uint64_t timeout);
extern LIBTYPE void hdhomerun_debug_printf(struct hdhomerun_debug_t *dbg, const char *fmt, ...);
extern LIBTYPE void hdhomerun_debug_vprintf(struct hdhomerun_debug_t *dbg, const char *fmt, va_list args);
#ifdef __cplusplus
}
#endif

1359
hdhomerun_device.c Normal file

File diff suppressed because it is too large Load diff

257
hdhomerun_device.h Normal file
View file

@ -0,0 +1,257 @@
/*
* hdhomerun_device.h
*
* Copyright © 2006-2008 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
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifdef __cplusplus
extern "C" {
#endif
#define HDHOMERUN_DEVICE_MAX_TUNE_TO_LOCK_TIME 1500
#define HDHOMERUN_DEVICE_MAX_LOCK_TO_DATA_TIME 2000
#define HDHOMERUN_DEVICE_MAX_TUNE_TO_DATA_TIME (HDHOMERUN_DEVICE_MAX_TUNE_TO_LOCK_TIME + HDHOMERUN_DEVICE_MAX_LOCK_TO_DATA_TIME)
#define HDHOMERUN_TARGET_PROTOCOL_UDP "udp"
#define HDHOMERUN_TARGET_PROTOCOL_RTP "rtp"
/*
* Create a device object.
*
* Typically a device object will be created for each tuner.
* It is valid to have multiple device objects communicating with a single HDHomeRun.
*
* For example, a threaded application that streams video from 4 tuners (2 HDHomeRun devices) and has
* GUI feedback to the user of the selected tuner might use 5 device objects: 4 for streaming video
* (one per thread) and one for the GUI display that can switch between tuners.
*
* This function will not attempt to connect to the device. The connection will be established when first used.
*
* uint32_t device_id = 32-bit device id of device. Set to HDHOMERUN_DEVICE_ID_WILDCARD to match any device ID.
* uint32_t device_ip = IP address of device. Set to 0 to auto-detect.
* unsigned int tuner = tuner index (0 or 1). Can be changed later by calling hdhomerun_device_set_tuner.
* struct hdhomerun_debug_t *dbg: Pointer to debug logging object. May be NULL.
*
* Returns a pointer to the newly created device object.
*
* When no longer needed, the socket should be destroyed by calling hdhomerun_device_destroy.
*
* The hdhomerun_device_create_from_str function creates a device object from the given device_str.
* The device_str parameter can be any of the following forms:
* <device id>
* <device id>-<tuner index>
* <ip address>
* If the tuner index is not included in the device_str then it is set to zero. Use hdhomerun_device_set_tuner
* or hdhomerun_device_set_tuner_from_str to set the tuner.
*
* The hdhomerun_device_set_tuner_from_str function sets the tuner from the given tuner_str.
* The tuner_str parameter can be any of the following forms:
* <tuner index>
* /tuner<tuner index>
*/
extern LIBTYPE struct hdhomerun_device_t *hdhomerun_device_create(uint32_t device_id, uint32_t device_ip, unsigned int tuner, struct hdhomerun_debug_t *dbg);
extern LIBTYPE struct hdhomerun_device_t *hdhomerun_device_create_multicast(uint32_t multicast_ip, uint16_t multicast_port, struct hdhomerun_debug_t *dbg);
extern LIBTYPE struct hdhomerun_device_t *hdhomerun_device_create_from_str(const char *device_str, struct hdhomerun_debug_t *dbg);
extern LIBTYPE void hdhomerun_device_destroy(struct hdhomerun_device_t *hd);
/*
* Get the device id, ip, or tuner of the device instance.
*/
extern LIBTYPE const char *hdhomerun_device_get_name(struct hdhomerun_device_t *hd);
extern LIBTYPE uint32_t hdhomerun_device_get_device_id(struct hdhomerun_device_t *hd);
extern LIBTYPE uint32_t hdhomerun_device_get_device_ip(struct hdhomerun_device_t *hd);
extern LIBTYPE uint32_t hdhomerun_device_get_device_id_requested(struct hdhomerun_device_t *hd);
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 int hdhomerun_device_set_device(struct hdhomerun_device_t *hd, uint32_t device_id, uint32_t device_ip);
extern LIBTYPE int hdhomerun_device_set_multicast(struct hdhomerun_device_t *hd, uint32_t multicast_ip, uint16_t multicast_port);
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);
/*
* Get the local machine IP address used when communicating with the device.
*
* This function is useful for determining the IP address to use with set target commands.
*
* Returns 32-bit IP address with native endianness, or 0 on error.
*/
extern LIBTYPE uint32_t hdhomerun_device_get_local_machine_addr(struct hdhomerun_device_t *hd);
/*
* Get operations.
*
* struct hdhomerun_tuner_status_t *status = Pointer to caller supplied status struct to be populated with result.
* const char **p<name> = Caller supplied char * to be updated to point to the result string. The string will remain
* valid until another call to a device function.
*
* Returns 1 if the operation was successful.
* Returns 0 if the operation was rejected.
* Returns -1 if a communication error occurred.
*/
extern LIBTYPE int hdhomerun_device_get_tuner_status(struct hdhomerun_device_t *hd, char **pstatus_str, struct hdhomerun_tuner_status_t *status);
extern LIBTYPE int hdhomerun_device_get_tuner_vstatus(struct hdhomerun_device_t *hd, char **pvstatus_str, struct hdhomerun_tuner_vstatus_t *vstatus);
extern LIBTYPE int hdhomerun_device_get_tuner_streaminfo(struct hdhomerun_device_t *hd, char **pstreaminfo);
extern LIBTYPE int hdhomerun_device_get_tuner_channel(struct hdhomerun_device_t *hd, char **pchannel);
extern LIBTYPE int hdhomerun_device_get_tuner_vchannel(struct hdhomerun_device_t *hd, char **pvchannel);
extern LIBTYPE int hdhomerun_device_get_tuner_channelmap(struct hdhomerun_device_t *hd, char **pchannelmap);
extern LIBTYPE int hdhomerun_device_get_tuner_filter(struct hdhomerun_device_t *hd, char **pfilter);
extern LIBTYPE int hdhomerun_device_get_tuner_program(struct hdhomerun_device_t *hd, char **pprogram);
extern LIBTYPE int hdhomerun_device_get_tuner_target(struct hdhomerun_device_t *hd, char **ptarget);
extern LIBTYPE int hdhomerun_device_get_tuner_plotsample(struct hdhomerun_device_t *hd, struct hdhomerun_plotsample_t **psamples, size_t *pcount);
extern LIBTYPE int hdhomerun_device_get_tuner_lockkey_owner(struct hdhomerun_device_t *hd, char **powner);
extern LIBTYPE int hdhomerun_device_get_oob_status(struct hdhomerun_device_t *hd, char **pstatus_str, struct hdhomerun_tuner_status_t *status);
extern LIBTYPE int hdhomerun_device_get_oob_plotsample(struct hdhomerun_device_t *hd, struct hdhomerun_plotsample_t **psamples, size_t *pcount);
extern LIBTYPE int hdhomerun_device_get_ir_target(struct hdhomerun_device_t *hd, char **ptarget);
extern LIBTYPE int hdhomerun_device_get_version(struct hdhomerun_device_t *hd, char **pversion_str, uint32_t *pversion_num);
extern LIBTYPE int hdhomerun_device_get_supported(struct hdhomerun_device_t *hd, char *prefix, char **pstr);
extern LIBTYPE uint32_t hdhomerun_device_get_tuner_status_ss_color(struct hdhomerun_tuner_status_t *status);
extern LIBTYPE uint32_t hdhomerun_device_get_tuner_status_snq_color(struct hdhomerun_tuner_status_t *status);
extern LIBTYPE uint32_t hdhomerun_device_get_tuner_status_seq_color(struct hdhomerun_tuner_status_t *status);
extern LIBTYPE const char *hdhomerun_device_get_hw_model_str(struct hdhomerun_device_t *hd);
extern LIBTYPE const char *hdhomerun_device_get_model_str(struct hdhomerun_device_t *hd);
/*
* Set operations.
*
* const char *<name> = String to send to device.
*
* Returns 1 if the operation was successful.
* Returns 0 if the operation was rejected.
* Returns -1 if a communication error occurred.
*/
extern LIBTYPE int hdhomerun_device_set_tuner_channel(struct hdhomerun_device_t *hd, const char *channel);
extern LIBTYPE int hdhomerun_device_set_tuner_vchannel(struct hdhomerun_device_t *hd, const char *vchannel);
extern LIBTYPE int hdhomerun_device_set_tuner_channelmap(struct hdhomerun_device_t *hd, const char *channelmap);
extern LIBTYPE int hdhomerun_device_set_tuner_filter(struct hdhomerun_device_t *hd, const char *filter);
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_ir_target(struct hdhomerun_device_t *hd, const char *target);
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.
*
* const char *name: The name of var to get/set (c-string). The supported vars is device/firmware dependant.
* const char *value: The value to set (c-string). The format is device/firmware dependant.
* char **pvalue: If provided, the caller-supplied char pointer will be populated with a pointer to the value
* string returned by the device, or NULL if the device returned an error string. The string will remain
* valid until the next call to a control sock function.
* char **perror: If provided, the caller-supplied char pointer will be populated with a pointer to the error
* string returned by the device, or NULL if the device returned an value string. The string will remain
* valid until the next call to a control sock function.
*
* Returns 1 if the operation was successful (pvalue set, perror NULL).
* Returns 0 if the operation was rejected (pvalue NULL, perror set).
* Returns -1 if a communication error occurs.
*/
extern LIBTYPE int hdhomerun_device_get_var(struct hdhomerun_device_t *hd, const char *name, char **pvalue, char **perror);
extern LIBTYPE int hdhomerun_device_set_var(struct hdhomerun_device_t *hd, const char *name, const char *value, char **pvalue, char **perror);
/*
* Tuner locking.
*
* The hdhomerun_device_tuner_lockkey_request function is used to obtain a lock
* or to verify that the hdhomerun_device object still holds the lock.
* Returns 1 if the lock request was successful and the lock was obtained.
* Returns 0 if the lock request was rejected.
* Returns -1 if a communication error occurs.
*
* The hdhomerun_device_tuner_lockkey_release function is used to release a
* previously held lock. If locking is used then this function must be called
* before destroying the hdhomerun_device object.
*/
extern LIBTYPE int hdhomerun_device_tuner_lockkey_request(struct hdhomerun_device_t *hd, char **perror);
extern LIBTYPE int hdhomerun_device_tuner_lockkey_release(struct hdhomerun_device_t *hd);
extern LIBTYPE int hdhomerun_device_tuner_lockkey_force(struct hdhomerun_device_t *hd);
/*
* Intended only for non persistent connections; eg, hdhomerun_config.
*/
extern LIBTYPE void hdhomerun_device_tuner_lockkey_use_value(struct hdhomerun_device_t *hd, uint32_t lockkey);
/*
* Wait for tuner lock after channel change.
*
* The hdhomerun_device_wait_for_lock function is used to detect/wait for a lock vs no lock indication
* after a channel change.
*
* It will return quickly if a lock is aquired.
* It will return quickly if there is no signal detected.
* Worst case it will time out after 1.5 seconds - the case where there is signal but no lock.
*/
extern LIBTYPE int hdhomerun_device_wait_for_lock(struct hdhomerun_device_t *hd, struct hdhomerun_tuner_status_t *status);
/*
* Stream a filtered program or the unfiltered stream.
*
* The hdhomerun_device_stream_start function initializes the process and tells the device to start streamin data.
*
* uint16_t program_number = The program number to filer, or 0 for unfiltered.
*
* Returns 1 if the oprtation started successfully.
* Returns 0 if the operation was rejected.
* Returns -1 if a communication error occurs.
*
* The hdhomerun_device_stream_recv function should be called periodically to receive the stream data.
* The buffer can losslessly store 1 second of data, however a more typical call rate would be every 15ms.
*
* 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 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);
/*
* Channel scan API.
*/
extern LIBTYPE int hdhomerun_device_channelscan_init(struct hdhomerun_device_t *hd, const char *channelmap);
extern LIBTYPE int hdhomerun_device_channelscan_advance(struct hdhomerun_device_t *hd, struct hdhomerun_channelscan_result_t *result);
extern LIBTYPE int hdhomerun_device_channelscan_detect(struct hdhomerun_device_t *hd, struct hdhomerun_channelscan_result_t *result);
extern LIBTYPE uint8_t hdhomerun_device_channelscan_get_progress(struct hdhomerun_device_t *hd);
/*
* 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.
*
* Returns 1 if the upload succeeded.
* Returns 0 if the upload was rejected.
* Returns -1 if an error occurs.
*/
extern LIBTYPE int hdhomerun_device_upgrade(struct hdhomerun_device_t *hd, FILE *upgrade_file);
/*
* Low level accessor functions.
*/
extern LIBTYPE struct hdhomerun_control_sock_t *hdhomerun_device_get_control_sock(struct hdhomerun_device_t *hd);
extern LIBTYPE struct hdhomerun_video_sock_t *hdhomerun_device_get_video_sock(struct hdhomerun_device_t *hd);
/*
* Debug print internal stats.
*/
extern LIBTYPE void hdhomerun_device_debug_print_video_stats(struct hdhomerun_device_t *hd);
extern LIBTYPE void hdhomerun_device_get_video_stats(struct hdhomerun_device_t *hd, struct hdhomerun_video_stats_t *stats);
#ifdef __cplusplus
}
#endif

447
hdhomerun_device_selector.c Normal file
View file

@ -0,0 +1,447 @@
/*
* hdhomerun_device_selector.c
*
* 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
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "hdhomerun.h"
struct hdhomerun_device_selector_t {
struct hdhomerun_device_t **hd_list;
size_t hd_count;
struct hdhomerun_debug_t *dbg;
};
struct hdhomerun_device_selector_t *hdhomerun_device_selector_create(struct hdhomerun_debug_t *dbg)
{
struct hdhomerun_device_selector_t *hds = (struct hdhomerun_device_selector_t *)calloc(1, sizeof(struct hdhomerun_device_selector_t));
if (!hds) {
hdhomerun_debug_printf(dbg, "hdhomerun_device_selector_create: failed to allocate selector object\n");
return NULL;
}
hds->dbg = dbg;
return hds;
}
void hdhomerun_device_selector_destroy(struct hdhomerun_device_selector_t *hds, bool_t destroy_devices)
{
if (destroy_devices) {
size_t index;
for (index = 0; index < hds->hd_count; index++) {
struct hdhomerun_device_t *entry = hds->hd_list[index];
hdhomerun_device_destroy(entry);
}
}
if (hds->hd_list) {
free(hds->hd_list);
}
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;
for (index = 0; index < hds->hd_count; index++) {
struct hdhomerun_device_t *entry = hds->hd_list[index];
if (entry == hd) {
return;
}
}
hds->hd_list = (struct hdhomerun_device_t **)realloc(hds->hd_list, (hds->hd_count + 1) * sizeof(struct hdhomerun_device_selector_t *));
if (!hds->hd_list) {
hdhomerun_debug_printf(hds->dbg, "hdhomerun_device_selector_add_device: failed to allocate device list\n");
return;
}
hds->hd_list[hds->hd_count++] = hd;
}
void hdhomerun_device_selector_remove_device(struct hdhomerun_device_selector_t *hds, struct hdhomerun_device_t *hd)
{
size_t index = 0;
while (1) {
if (index >= hds->hd_count) {
return;
}
struct hdhomerun_device_t *entry = hds->hd_list[index];
if (entry == hd) {
break;
}
index++;
}
while (index + 1 < hds->hd_count) {
hds->hd_list[index] = hds->hd_list[index + 1];
index++;
}
hds->hd_list[index] = NULL;
hds->hd_count--;
}
struct hdhomerun_device_t *hdhomerun_device_selector_find_device(struct hdhomerun_device_selector_t *hds, uint32_t device_id, unsigned int tuner_index)
{
size_t index;
for (index = 0; index < hds->hd_count; index++) {
struct hdhomerun_device_t *entry = hds->hd_list[index];
if (hdhomerun_device_get_device_id(entry) != device_id) {
continue;
}
if (hdhomerun_device_get_tuner(entry) != tuner_index) {
continue;
}
return entry;
}
return NULL;
}
static int hdhomerun_device_selector_load_from_str_discover(struct hdhomerun_device_selector_t *hds, uint32_t target_ip, uint32_t device_id)
{
struct hdhomerun_discover_device_t result_list[64];
int discover_count = hdhomerun_discover_find_devices_custom_v2(target_ip, HDHOMERUN_DEVICE_TYPE_TUNER, device_id, result_list, 64);
int count = 0;
int result_index;
struct hdhomerun_discover_device_t *result = result_list;
for (result_index = 0; result_index < discover_count; result_index++) {
unsigned int tuner_index;
for (tuner_index = 0; tuner_index < result->tuner_count; tuner_index++) {
struct hdhomerun_device_t *hd = hdhomerun_device_create(result->device_id, result->ip_addr, tuner_index, hds->dbg);
if (!hd) {
continue;
}
hdhomerun_device_selector_add_device(hds, hd);
count++;
}
result++;
}
return count;
}
int hdhomerun_device_selector_load_from_str(struct hdhomerun_device_selector_t *hds, char *device_str)
{
/*
* IP address based device_str.
*/
unsigned int a[4];
if (sscanf(device_str, "%u.%u.%u.%u", &a[0], &a[1], &a[2], &a[3]) == 4) {
uint32_t ip_addr = (uint32_t)((a[0] << 24) | (a[1] << 16) | (a[2] << 8) | (a[3] << 0));
/*
* Multicast IP address.
*/
unsigned int port;
if (sscanf(device_str, "%u.%u.%u.%u:%u", &a[0], &a[1], &a[2], &a[3], &port) == 5) {
struct hdhomerun_device_t *hd = hdhomerun_device_create_multicast(ip_addr, port, hds->dbg);
if (!hd) {
return 0;
}
hdhomerun_device_selector_add_device(hds, hd);
return 1;
}
/*
* IP address + tuner number.
*/
unsigned int tuner;
if (sscanf(device_str, "%u.%u.%u.%u-%u", &a[0], &a[1], &a[2], &a[3], &tuner) == 5) {
struct hdhomerun_device_t *hd = hdhomerun_device_create(HDHOMERUN_DEVICE_ID_WILDCARD, ip_addr, tuner, hds->dbg);
if (!hd) {
return 0;
}
hdhomerun_device_selector_add_device(hds, hd);
return 1;
}
/*
* IP address only - discover and add tuners.
*/
return hdhomerun_device_selector_load_from_str_discover(hds, ip_addr, HDHOMERUN_DEVICE_ID_WILDCARD);
}
/*
* Device ID based device_str.
*/
char *end;
uint32_t device_id = (uint32_t)strtoul(device_str, &end, 16);
if ((end == device_str + 8) && hdhomerun_discover_validate_device_id(device_id)) {
/*
* IP address + tuner number.
*/
if (*end == '-') {
unsigned int tuner = (unsigned int)strtoul(end + 1, NULL, 10);
struct hdhomerun_device_t *hd = hdhomerun_device_create(device_id, 0, tuner, hds->dbg);
if (!hd) {
return 0;
}
hdhomerun_device_selector_add_device(hds, hd);
return 1;
}
/*
* Device ID only - discover and add tuners.
*/
return hdhomerun_device_selector_load_from_str_discover(hds, 0, device_id);
}
/*
* DNS based device_str.
*/
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(device_str, "65001", &hints, &sock_info) != 0) {
return 0;
}
struct sockaddr_in *sock_addr = (struct sockaddr_in *)sock_info->ai_addr;
uint32_t ip_addr = (uint32_t)ntohl(sock_addr->sin_addr.s_addr);
freeaddrinfo(sock_info);
if (ip_addr == 0) {
return 0;
}
return hdhomerun_device_selector_load_from_str_discover(hds, ip_addr, HDHOMERUN_DEVICE_ID_WILDCARD);
}
int hdhomerun_device_selector_load_from_file(struct hdhomerun_device_selector_t *hds, char *filename)
{
FILE *fp = fopen(filename, "r");
if (!fp) {
return 0;
}
int count = 0;
while(1) {
char device_str[32];
if (!fgets(device_str, sizeof(device_str), fp)) {
break;
}
count += hdhomerun_device_selector_load_from_str(hds, device_str);
}
fclose(fp);
return count;
}
#if defined(__WINDOWS__)
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", (long)ret);
return 0;
}
int count = 0;
DWORD index = 0;
while (1) {
/* Next tuner device. */
wchar_t wdevice_str[32];
DWORD size = sizeof(wdevice_str);
ret = RegEnumKeyEx(tuners_key, index++, wdevice_str, &size, NULL, NULL, NULL, NULL);
if (ret != ERROR_SUCCESS) {
break;
}
/* Check device configuation. */
HKEY device_key;
ret = RegOpenKeyEx(tuners_key, wdevice_str, 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_str, (long)ret);
continue;
}
wchar_t wsource_test[32];
size = sizeof(wsource_test);
if (RegQueryValueEx(device_key, L"Source", NULL, NULL, (LPBYTE)&wsource_test, &size) != ERROR_SUCCESS) {
wsprintf(wsource_test, L"Unknown");
}
RegCloseKey(device_key);
if (_wcsicmp(wsource_test, wsource) != 0) {
continue;
}
/* Create and add device. */
char device_str[32];
hdhomerun_sprintf(device_str, device_str + sizeof(device_str), "%S", wdevice_str);
count += hdhomerun_device_selector_load_from_str(hds, device_str);
}
RegCloseKey(tuners_key);
return count;
}
#endif
static bool_t hdhomerun_device_selector_choose_test(struct hdhomerun_device_selector_t *hds, struct hdhomerun_device_t *test_hd)
{
const char *name = hdhomerun_device_get_name(test_hd);
/*
* Attempt to aquire lock.
*/
char *error;
int ret = hdhomerun_device_tuner_lockkey_request(test_hd, &error);
if (ret > 0) {
hdhomerun_debug_printf(hds->dbg, "hdhomerun_device_selector_choose_test: device %s chosen\n", name);
return TRUE;
}
if (ret < 0) {
hdhomerun_debug_printf(hds->dbg, "hdhomerun_device_selector_choose_test: device %s communication error\n", name);
return FALSE;
}
/*
* In use - check target.
*/
char *target;
ret = hdhomerun_device_get_tuner_target(test_hd, &target);
if (ret < 0) {
hdhomerun_debug_printf(hds->dbg, "hdhomerun_device_selector_choose_test: device %s communication error\n", name);
return FALSE;
}
if (ret == 0) {
hdhomerun_debug_printf(hds->dbg, "hdhomerun_device_selector_choose_test: device %s in use, failed to read target\n", name);
return FALSE;
}
if (strcmp(target, "none") == 0) {
hdhomerun_debug_printf(hds->dbg, "hdhomerun_device_selector_choose_test: device %s in use, no target set\n", name);
return FALSE;
}
if ((strncmp(target, "udp://", 6) != 0) && (strncmp(target, "rtp://", 6) != 0)) {
hdhomerun_debug_printf(hds->dbg, "hdhomerun_device_selector_choose_test: device %s in use by %s\n", name, target);
return FALSE;
}
unsigned int a[4];
unsigned int target_port;
if (sscanf(target + 6, "%u.%u.%u.%u:%u", &a[0], &a[1], &a[2], &a[3], &target_port) != 5) {
hdhomerun_debug_printf(hds->dbg, "hdhomerun_device_selector_choose_test: device %s in use, unexpected target set (%s)\n", name, target);
return FALSE;
}
uint32_t target_ip = (uint32_t)((a[0] << 24) | (a[1] << 16) | (a[2] << 8) | (a[3] << 0));
uint32_t local_ip = hdhomerun_device_get_local_machine_addr(test_hd);
if (target_ip != local_ip) {
hdhomerun_debug_printf(hds->dbg, "hdhomerun_device_selector_choose_test: device %s in use by %s\n", name, target);
return FALSE;
}
/*
* Test local port.
*/
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;
}
bool_t inuse = (hdhomerun_sock_bind(test_sock, INADDR_ANY, (uint16_t)target_port, FALSE) == FALSE);
hdhomerun_sock_destroy(test_sock);
if (inuse) {
hdhomerun_debug_printf(hds->dbg, "hdhomerun_device_selector_choose_test: device %s in use by local machine\n", name);
return FALSE;
}
/*
* Dead local target, force clear lock.
*/
ret = hdhomerun_device_tuner_lockkey_force(test_hd);
if (ret < 0) {
hdhomerun_debug_printf(hds->dbg, "hdhomerun_device_selector_choose_test: device %s communication error\n", name);
return FALSE;
}
if (ret == 0) {
hdhomerun_debug_printf(hds->dbg, "hdhomerun_device_selector_choose_test: device %s in use by local machine, dead target, failed to force release lockkey\n", name);
return FALSE;
}
hdhomerun_debug_printf(hds->dbg, "hdhomerun_device_selector_choose_test: device %s in use by local machine, dead target, lockkey force successful\n", name);
/*
* Attempt to aquire lock.
*/
ret = hdhomerun_device_tuner_lockkey_request(test_hd, &error);
if (ret > 0) {
hdhomerun_debug_printf(hds->dbg, "hdhomerun_device_selector_choose_test: device %s chosen\n", name);
return TRUE;
}
if (ret < 0) {
hdhomerun_debug_printf(hds->dbg, "hdhomerun_device_selector_choose_test: device %s communication error\n", name);
return FALSE;
}
hdhomerun_debug_printf(hds->dbg, "hdhomerun_device_selector_choose_test: device %s still in use after lockkey force (%s)\n", name, error);
return FALSE;
}
struct hdhomerun_device_t *hdhomerun_device_selector_choose_and_lock(struct hdhomerun_device_selector_t *hds, struct hdhomerun_device_t *prefered)
{
/* Test prefered device first. */
if (prefered) {
if (hdhomerun_device_selector_choose_test(hds, prefered)) {
return prefered;
}
}
/* Test other tuners. */
size_t index;
for (index = 0; index < hds->hd_count; index++) {
struct hdhomerun_device_t *entry = hds->hd_list[index];
if (entry == prefered) {
continue;
}
if (hdhomerun_device_selector_choose_test(hds, entry)) {
return entry;
}
}
hdhomerun_debug_printf(hds->dbg, "hdhomerun_device_selector_choose_and_lock: no devices available\n");
return NULL;
}

View file

@ -0,0 +1,86 @@
/*
* hdhomerun_device_selector.h
*
* Copyright © 2009 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
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifdef __cplusplus
extern "C" {
#endif
/*
* Create a device selector object for use with dynamic tuner allocation support.
* All tuners registered with a specific device selector instance must have the same signal source.
* The dbg parameter may be null.
*/
extern LIBTYPE struct hdhomerun_device_selector_t *hdhomerun_device_selector_create(struct hdhomerun_debug_t *dbg);
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 int hdhomerun_device_selector_load_from_str(struct hdhomerun_device_selector_t *hds, char *device_str);
extern LIBTYPE int hdhomerun_device_selector_load_from_file(struct hdhomerun_device_selector_t *hds, char *filename);
#if defined(__WINDOWS__)
extern LIBTYPE int hdhomerun_device_selector_load_from_windows_registry(struct hdhomerun_device_selector_t *hds, wchar_t *wsource);
#endif
/*
* Add/remove a device from the selector list.
*/
extern LIBTYPE void hdhomerun_device_selector_add_device(struct hdhomerun_device_selector_t *hds, struct hdhomerun_device_t *hd);
extern LIBTYPE void hdhomerun_device_selector_remove_device(struct hdhomerun_device_selector_t *hds, struct hdhomerun_device_t *hd);
/*
* Find a device in the selector list.
*/
extern LIBTYPE struct hdhomerun_device_t *hdhomerun_device_selector_find_device(struct hdhomerun_device_selector_t *hds, uint32_t device_id, unsigned int tuner_index);
/*
* Select and lock an available device.
* If not null, preference will be given to the prefered device specified.
* The device resource lock must be released by the application when no longer needed by
* calling hdhomerun_device_tuner_lockkey_release().
*
* Recommended channel change logic:
*
* Start (inactive -> active):
* - Call hdhomerun_device_selector_choose_and_lock() to choose and lock an available tuner.
*
* Stop (active -> inactive):
* - Call hdhomerun_device_tuner_lockkey_release() to release the resource lock and allow the tuner
* to be allocated by other computers.
*
* Channel change (active -> active):
* - If the new channel has a different signal source then call hdhomerun_device_tuner_lockkey_release()
* to release the lock on the tuner playing the previous channel, then call
* hdhomerun_device_selector_choose_and_lock() to choose and lock an available tuner.
* - If the new channel has the same signal source then call hdhomerun_device_tuner_lockkey_request()
* to refresh the lock. If this function succeeds then the same device can be used. If this fucntion fails
* then call hdhomerun_device_selector_choose_and_lock() to choose and lock an available tuner.
*/
extern LIBTYPE struct hdhomerun_device_t *hdhomerun_device_selector_choose_and_lock(struct hdhomerun_device_selector_t *hds, struct hdhomerun_device_t *prefered);
#ifdef __cplusplus
}
#endif

496
hdhomerun_discover.c Normal file
View file

@ -0,0 +1,496 @@
/*
* hdhomerun_discover.c
*
* Copyright © 2006-2015 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
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "hdhomerun.h"
#define HDHOMERUN_DISOCVER_MAX_SOCK_COUNT 16
struct hdhomerun_discover_sock_t {
hdhomerun_sock_t sock;
bool_t detected;
uint32_t local_ip;
uint32_t subnet_mask;
};
struct hdhomerun_discover_t {
struct hdhomerun_discover_sock_t socks[HDHOMERUN_DISOCVER_MAX_SOCK_COUNT];
unsigned int sock_count;
struct hdhomerun_pkt_t tx_pkt;
struct hdhomerun_pkt_t rx_pkt;
struct hdhomerun_debug_t *dbg;
};
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. */
hdhomerun_sock_t sock = hdhomerun_sock_create_udp();
if (sock == HDHOMERUN_SOCK_INVALID) {
return FALSE;
}
/* Bind socket. */
if (!hdhomerun_sock_bind(sock, local_ip, 0, FALSE)) {
hdhomerun_debug_printf(ds->dbg, "discover: failed to bind to %u.%u.%u.%u:0\n", (unsigned int)(local_ip >> 24) & 0xFF, (unsigned int)(local_ip >> 16) & 0xFF, (unsigned int)(local_ip >> 8) & 0xFF, (unsigned int)(local_ip >> 0) & 0xFF);
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(struct hdhomerun_debug_t *dbg)
{
struct hdhomerun_discover_t *ds = (struct hdhomerun_discover_t *)calloc(1, sizeof(struct hdhomerun_discover_t));
if (!ds) {
return NULL;
}
ds->dbg = dbg;
/* 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);
}
static void hdhomerun_discover_sock_detect(struct hdhomerun_discover_t *ds)
{
unsigned int i;
for (i = 1; i < ds->sock_count; i++) {
struct hdhomerun_discover_sock_t *dss = &ds->socks[i];
dss->detected = FALSE;
}
struct hdhomerun_local_ip_info_t ip_info_list[HDHOMERUN_DISOCVER_MAX_SOCK_COUNT];
int count = hdhomerun_local_ip_info(ip_info_list, HDHOMERUN_DISOCVER_MAX_SOCK_COUNT);
if (count < 0) {
hdhomerun_debug_printf(ds->dbg, "discover: hdhomerun_local_ip_info returned error\n");
count = 0;
}
if (count > HDHOMERUN_DISOCVER_MAX_SOCK_COUNT) {
hdhomerun_debug_printf(ds->dbg, "discover: too many local IP addresses\n");
count = HDHOMERUN_DISOCVER_MAX_SOCK_COUNT;
}
int index;
for (index = 0; index < count; index++) {
struct hdhomerun_local_ip_info_t *ip_info = &ip_info_list[index];
hdhomerun_discover_sock_add(ds, ip_info->ip_addr, ip_info->subnet_mask);
}
struct hdhomerun_discover_sock_t *src = &ds->socks[1];
struct hdhomerun_discover_sock_t *dst = &ds->socks[1];
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++;
}
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)
{
struct hdhomerun_pkt_t *tx_pkt = &ds->tx_pkt;
hdhomerun_pkt_reset(tx_pkt);
hdhomerun_pkt_write_u8(tx_pkt, HDHOMERUN_TAG_DEVICE_TYPE);
hdhomerun_pkt_write_var_length(tx_pkt, 4);
hdhomerun_pkt_write_u32(tx_pkt, device_type);
hdhomerun_pkt_write_u8(tx_pkt, HDHOMERUN_TAG_DEVICE_ID);
hdhomerun_pkt_write_var_length(tx_pkt, 4);
hdhomerun_pkt_write_u32(tx_pkt, device_id);
hdhomerun_pkt_seal_frame(tx_pkt, HDHOMERUN_TYPE_DISCOVER_REQ);
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)
{
bool_t result = FALSE;
/*
* Send subnet broadcast using each local ip socket.
* This will work with multiple separate 169.254.x.x interfaces.
*/
unsigned int i;
for (i = 1; i < ds->sock_count; i++) {
struct hdhomerun_discover_sock_t *dss = &ds->socks[i];
uint32_t target_ip = dss->local_ip | ~dss->subnet_mask;
result |= hdhomerun_discover_send_internal(ds, dss, target_ip, device_type, device_id);
}
/*
* If no local ip sockets then fall back to sending a global broadcast letting the OS choose the interface.
*/
if (!result) {
struct hdhomerun_discover_sock_t *dss = &ds->socks[0];
result = hdhomerun_discover_send_internal(ds, dss, 0xFFFFFFFF, device_type, device_id);
}
return result;
}
static bool_t hdhomerun_discover_send_target_ip(struct hdhomerun_discover_t *ds, uint32_t target_ip, uint32_t device_type, uint32_t device_id)
{
bool_t result = FALSE;
/*
* Send targeted packet from any local ip that is in the same subnet.
* This will work with multiple separate 169.254.x.x interfaces.
*/
unsigned int i;
for (i = 1; i < ds->sock_count; i++) {
struct hdhomerun_discover_sock_t *dss = &ds->socks[i];
if ((target_ip & dss->subnet_mask) != (dss->local_ip & dss->subnet_mask)) {
continue;
}
result |= hdhomerun_discover_send_internal(ds, dss, target_ip, device_type, device_id);
}
/*
* If target IP does not match a local subnet then fall back to letting the OS choose the gateway interface.
*/
if (!result) {
struct hdhomerun_discover_sock_t *dss = &ds->socks[0];
result = hdhomerun_discover_send_internal(ds, dss, target_ip, device_type, device_id);
}
return result;
}
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) {
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);
}
}
static bool_t hdhomerun_discover_is_legacy(uint32_t device_id)
{
switch (device_id >> 20) {
case 0x100: /* TECH-US/TECH3-US */
return (device_id < 0x10040000);
case 0x120: /* TECH3-EU */
return (device_id < 0x12030000);
case 0x101: /* HDHR-US */
case 0x102: /* HDHR-T1-US */
case 0x103: /* HDHR3-US */
case 0x111: /* HDHR3-DT */
case 0x121: /* HDHR-EU */
case 0x122: /* HDHR3-EU */
return TRUE;
default:
return FALSE;
}
}
static bool_t hdhomerun_discover_recv_internal(struct hdhomerun_discover_t *ds, struct hdhomerun_discover_sock_t *dss, struct hdhomerun_discover_device_t *result)
{
static char hdhomerun_discover_recv_base64_encode_table[64 + 1] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
struct hdhomerun_pkt_t *rx_pkt = &ds->rx_pkt;
hdhomerun_pkt_reset(rx_pkt);
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 += length;
uint16_t type;
if (hdhomerun_pkt_open_frame(rx_pkt, &type) <= 0) {
return FALSE;
}
if (type != HDHOMERUN_TYPE_DISCOVER_RPY) {
return FALSE;
}
memset(result, 0, sizeof(struct hdhomerun_discover_device_t));
result->ip_addr = remote_addr;
hdhomerun_sprintf(result->base_url, result->base_url + sizeof(result->base_url), "http://%u.%u.%u.%u:80",
(remote_addr >> 24) & 0xFF, (remote_addr >> 16) & 0xFF, (remote_addr >> 8) & 0xFF, (remote_addr >> 0) & 0xFF
);
while (1) {
uint8_t tag;
size_t len;
uint8_t *next = hdhomerun_pkt_read_tlv(rx_pkt, &tag, &len);
if (!next) {
break;
}
int i;
switch (tag) {
case HDHOMERUN_TAG_DEVICE_TYPE:
if (len != 4) {
break;
}
result->device_type = hdhomerun_pkt_read_u32(rx_pkt);
break;
case HDHOMERUN_TAG_DEVICE_ID:
if (len != 4) {
break;
}
result->device_id = hdhomerun_pkt_read_u32(rx_pkt);
result->is_legacy = hdhomerun_discover_is_legacy(result->device_id);
break;
case HDHOMERUN_TAG_TUNER_COUNT:
if (len != 1) {
break;
}
result->tuner_count = hdhomerun_pkt_read_u8(rx_pkt);
break;
case HDHOMERUN_TAG_DEVICE_AUTH_STR:
if (len >= sizeof(result->device_auth)) {
break;
}
hdhomerun_pkt_read_mem(rx_pkt, result->device_auth, len);
result->device_auth[len] = 0;
break;
case HDHOMERUN_TAG_DEVICE_AUTH_BIN:
if (len != 18) {
break;
}
for (i = 0; i < 24; i += 4) {
uint32_t raw24;
raw24 = (uint32_t)hdhomerun_pkt_read_u8(rx_pkt) << 16;
raw24 |= (uint32_t)hdhomerun_pkt_read_u8(rx_pkt) << 8;
raw24 |= (uint32_t)hdhomerun_pkt_read_u8(rx_pkt) << 0;
result->device_auth[i + 0] = hdhomerun_discover_recv_base64_encode_table[(raw24 >> 18) & 0x3F];
result->device_auth[i + 1] = hdhomerun_discover_recv_base64_encode_table[(raw24 >> 12) & 0x3F];
result->device_auth[i + 2] = hdhomerun_discover_recv_base64_encode_table[(raw24 >> 6) & 0x3F];
result->device_auth[i + 3] = hdhomerun_discover_recv_base64_encode_table[(raw24 >> 0) & 0x3F];
}
result->device_auth[24] = 0;
break;
case HDHOMERUN_TAG_BASE_URL:
if (len >= sizeof(result->base_url)) {
break;
}
hdhomerun_pkt_read_mem(rx_pkt, result->base_url, len);
result->base_url[len] = 0;
break;
default:
break;
}
rx_pkt->pos = next;
}
/* Fixup for old firmware. */
if (result->tuner_count == 0) {
switch (result->device_id >> 20) {
case 0x102:
result->tuner_count = 1;
break;
case 0x100:
case 0x101:
case 0x121:
result->tuner_count = 2;
break;
default:
break;
}
}
return TRUE;
}
static bool_t hdhomerun_discover_recv(struct hdhomerun_discover_t *ds, struct hdhomerun_discover_device_t *result)
{
unsigned int i;
for (i = 0; i < ds->sock_count; i++) {
struct hdhomerun_discover_sock_t *dss = &ds->socks[i];
if (hdhomerun_discover_recv_internal(ds, dss, result)) {
return TRUE;
}
}
return FALSE;
}
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 *entry = &result_list[index];
if (memcmp(lookup, entry, sizeof(struct hdhomerun_discover_device_t)) == 0) {
return entry;
}
}
return NULL;
}
int hdhomerun_discover_find_devices_v2(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++) {
if (!hdhomerun_discover_send(ds, target_ip, device_type, device_id)) {
return -1;
}
uint64_t timeout = getcurrenttime() + 200;
while (1) {
struct hdhomerun_discover_device_t *result = &result_list[count];
memset(result, 0, sizeof(struct hdhomerun_discover_device_t));
if (!hdhomerun_discover_recv(ds, result)) {
if (getcurrenttime() >= timeout) {
break;
}
msleep_approx(10);
continue;
}
/* Filter. */
if (device_type != HDHOMERUN_DEVICE_TYPE_WILDCARD) {
if (device_type != result->device_type) {
continue;
}
}
if (device_id != HDHOMERUN_DEVICE_ID_WILDCARD) {
if (device_id != result->device_id) {
continue;
}
}
/* Ensure not already in list. */
if (hdhomerun_discover_find_in_list(result_list, count, result)) {
continue;
}
/* Add to list. */
count++;
if (count >= max_count) {
return count;
}
}
}
return count;
}
int hdhomerun_discover_find_devices_custom_v2(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(NULL);
if (!ds) {
return -1;
}
int ret = hdhomerun_discover_find_devices_v2(ds, target_ip, device_type, device_id, result_list, max_count);
hdhomerun_discover_destroy(ds);
return ret;
}
bool_t hdhomerun_discover_validate_device_id(uint32_t device_id)
{
static uint8_t lookup_table[16] = {0xA, 0x5, 0xF, 0x6, 0x7, 0xC, 0x1, 0xB, 0x9, 0x2, 0x8, 0xD, 0x4, 0x3, 0xE, 0x0};
uint8_t checksum = 0;
checksum ^= lookup_table[(device_id >> 28) & 0x0F];
checksum ^= (device_id >> 24) & 0x0F;
checksum ^= lookup_table[(device_id >> 20) & 0x0F];
checksum ^= (device_id >> 16) & 0x0F;
checksum ^= lookup_table[(device_id >> 12) & 0x0F];
checksum ^= (device_id >> 8) & 0x0F;
checksum ^= lookup_table[(device_id >> 4) & 0x0F];
checksum ^= (device_id >> 0) & 0x0F;
return (checksum == 0);
}
bool_t hdhomerun_discover_is_ip_multicast(uint32_t ip_addr)
{
return (ip_addr >= 0xE0000000) && (ip_addr < 0xF0000000);
}

78
hdhomerun_discover.h Normal file
View file

@ -0,0 +1,78 @@
/*
* hdhomerun_discover.h
*
* Copyright © 2006-2015 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
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifdef __cplusplus
extern "C" {
#endif
struct hdhomerun_discover_device_t {
uint32_t ip_addr;
uint32_t device_type;
uint32_t device_id;
uint8_t tuner_count;
bool_t is_legacy;
char device_auth[25];
char base_url[29];
};
/*
* Find devices.
*
* 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 typically 400ms if max_count is not reached.
*
* Set target_ip to zero to auto-detect the 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.
*/
extern LIBTYPE int hdhomerun_discover_find_devices_custom_v2(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(struct hdhomerun_debug_t *dbg);
extern LIBTYPE void hdhomerun_discover_destroy(struct hdhomerun_discover_t *ds);
extern LIBTYPE int hdhomerun_discover_find_devices_v2(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
* single-digit errors and two digit transposition errors.
*
* Returns TRUE if valid.
* Returns FALSE if not valid.
*/
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

37
hdhomerun_os.h Normal file
View file

@ -0,0 +1,37 @@
/*
* hdhomerun_os.h
*
* Copyright © 2006-2008 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
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#if defined(_WIN32) || defined(_WIN64)
#define __WINDOWS__
#endif
#if defined(__WINDOWS__)
#include "hdhomerun_os_windows.h"
#else
#include "hdhomerun_os_posix.h"
#endif
#if !defined(TRUE)
#define TRUE 1
#endif
#if !defined(FALSE)
#define FALSE 0
#endif

128
hdhomerun_os_posix.c Normal file
View file

@ -0,0 +1,128 @@
/*
* hdhomerun_os_posix.c
*
* 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
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "hdhomerun_os.h"
#if defined(__APPLE__)
#include <mach/clock.h>
#include <mach/mach.h>
#endif
static pthread_once_t random_get32_once = PTHREAD_ONCE_INIT;
static FILE *random_get32_fp = NULL;
static void random_get32_init(void)
{
random_get32_fp = fopen("/dev/urandom", "rb");
}
uint32_t random_get32(void)
{
pthread_once(&random_get32_once, random_get32_init);
if (!random_get32_fp) {
return (uint32_t)getcurrenttime();
}
uint32_t Result;
if (fread(&Result, 4, 1, random_get32_fp) != 1) {
return (uint32_t)getcurrenttime();
}
return Result;
}
uint64_t getcurrenttime(void)
{
#if defined(CLOCK_MONOTONIC)
struct timespec t;
clock_gettime(CLOCK_MONOTONIC, &t);
return ((uint64_t)t.tv_sec * 1000) + (t.tv_nsec / 1000000);
#elif defined(__APPLE__)
clock_serv_t clock_serv;
host_get_clock_service(mach_host_self(), SYSTEM_CLOCK, &clock_serv);
struct mach_timespec t;
clock_get_time(clock_serv, &t);
mach_port_deallocate(mach_task_self(), clock_serv);
return ((uint64_t)t.tv_sec * 1000) + (t.tv_nsec / 1000000);
#else
#error no clock source for getcurrenttime()
#endif
}
void msleep_approx(uint64_t ms)
{
unsigned int delay_s = ms / 1000;
if (delay_s > 0) {
sleep(delay_s);
ms -= delay_s * 1000;
}
unsigned int delay_us = ms * 1000;
if (delay_us > 0) {
usleep(delay_us);
}
}
void msleep_minimum(uint64_t ms)
{
uint64_t stop_time = getcurrenttime() + ms;
while (1) {
uint64_t current_time = getcurrenttime();
if (current_time >= stop_time) {
return;
}
msleep_approx(stop_time - current_time);
}
}
bool_t hdhomerun_vsprintf(char *buffer, char *end, const char *fmt, va_list ap)
{
if (buffer >= end) {
return FALSE;
}
int length = vsnprintf(buffer, end - buffer - 1, fmt, ap);
if (length < 0) {
*buffer = 0;
return FALSE;
}
if (buffer + length + 1 > end) {
*(end - 1) = 0;
return FALSE;
}
return TRUE;
}
bool_t hdhomerun_sprintf(char *buffer, char *end, const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
bool_t result = hdhomerun_vsprintf(buffer, end, fmt, ap);
va_end(ap);
return result;
}

63
hdhomerun_os_posix.h Normal file
View file

@ -0,0 +1,63 @@
/*
* hdhomerun_os_posix.h
*
* 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
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#define _FILE_OFFSET_BITS 64
#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <sys/timeb.h>
#include <sys/wait.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <poll.h>
#include <netdb.h>
#include <pthread.h>
typedef int bool_t;
typedef void (*sig_t)(int);
#define LIBTYPE
#define console_vprintf vprintf
#define console_printf printf
#define THREAD_FUNC_PREFIX void *
#ifdef __cplusplus
extern "C" {
#endif
extern LIBTYPE uint32_t random_get32(void);
extern LIBTYPE uint64_t getcurrenttime(void);
extern LIBTYPE void msleep_approx(uint64_t ms);
extern LIBTYPE void msleep_minimum(uint64_t ms);
extern LIBTYPE bool_t hdhomerun_vsprintf(char *buffer, char *end, const char *fmt, va_list ap);
extern LIBTYPE bool_t hdhomerun_sprintf(char *buffer, char *end, const char *fmt, ...);
#ifdef __cplusplus
}
#endif

152
hdhomerun_os_windows.c Normal file
View file

@ -0,0 +1,152 @@
/*
* hdhomerun_os_windows.c
*
* 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
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "hdhomerun_os.h"
static DWORD random_get32_context_tls = TlsAlloc();
uint32_t random_get32(void)
{
HCRYPTPROV *phProv = (HCRYPTPROV *)TlsGetValue(random_get32_context_tls);
if (!phProv) {
phProv = (HCRYPTPROV *)calloc(1, sizeof(HCRYPTPROV));
CryptAcquireContext(phProv, 0, 0, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT);
TlsSetValue(random_get32_context_tls, phProv);
}
uint32_t Result;
if (!CryptGenRandom(*phProv, sizeof(Result), (BYTE *)&Result)) {
return (uint32_t)getcurrenttime();
}
return Result;
}
uint64_t getcurrenttime(void)
{
return GetTickCount64();
}
void msleep_approx(uint64_t ms)
{
Sleep((DWORD)ms);
}
void msleep_minimum(uint64_t ms)
{
uint64_t stop_time = getcurrenttime() + ms;
while (1) {
uint64_t current_time = getcurrenttime();
if (current_time >= stop_time) {
return;
}
msleep_approx(stop_time - current_time);
}
}
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;
}
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;
}
}
}
void pthread_mutex_init(pthread_mutex_t *mutex, void *attr)
{
*mutex = CreateMutex(NULL, FALSE, NULL);
}
void pthread_mutex_lock(pthread_mutex_t *mutex)
{
WaitForSingleObject(*mutex, INFINITE);
}
void pthread_mutex_unlock(pthread_mutex_t *mutex)
{
ReleaseMutex(*mutex);
}
bool_t hdhomerun_vsprintf(char *buffer, char *end, const char *fmt, va_list ap)
{
if (buffer >= end) {
return FALSE;
}
int length = _vsnprintf(buffer, end - buffer - 1, fmt, ap);
if (length < 0) {
*buffer = 0;
return FALSE;
}
if (buffer + length + 1 > end) {
*(end - 1) = 0;
return FALSE;
}
return TRUE;
}
bool_t hdhomerun_sprintf(char *buffer, char *end, const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
bool_t result = hdhomerun_vsprintf(buffer, end, fmt, ap);
va_end(ap);
return result;
}
/*
* 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.
*/
void console_vprintf(const char *fmt, va_list ap)
{
UINT cp = GetConsoleOutputCP();
SetConsoleOutputCP(CP_UTF8);
vprintf(fmt, ap);
SetConsoleOutputCP(cp);
}
void console_printf(const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
console_vprintf(fmt, ap);
va_end(ap);
}

95
hdhomerun_os_windows.h Normal file
View file

@ -0,0 +1,95 @@
/*
* hdhomerun_os_windows.h
*
* 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
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#define _WINSOCKAPI_
#include <windows.h>
#include <winsock2.h>
#include <ws2tcpip.h>
#include <wspiapi.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include <signal.h>
#include <time.h>
#include <sys/types.h>
#include <sys/timeb.h>
#if defined(DLL_IMPORT)
#define LIBTYPE __declspec( dllexport )
#elif defined(DLL_EXPORT)
#define LIBTYPE __declspec( dllimport )
#else
#define LIBTYPE
#endif
typedef int bool_t;
typedef signed __int8 int8_t;
typedef signed __int16 int16_t;
typedef signed __int32 int32_t;
typedef signed __int64 int64_t;
typedef unsigned __int8 uint8_t;
typedef unsigned __int16 uint16_t;
typedef unsigned __int32 uint32_t;
typedef unsigned __int64 uint64_t;
typedef void (*sig_t)(int);
typedef HANDLE pthread_t;
typedef HANDLE pthread_mutex_t;
#if !defined(va_copy)
#define va_copy(x, y) x = y
#endif
#define atoll _atoi64
#define strdup _strdup
#define strcasecmp _stricmp
#define fseeko _fseeki64
#define ftello _ftelli64
#define THREAD_FUNC_PREFIX DWORD WINAPI
#ifdef __cplusplus
extern "C" {
#endif
extern LIBTYPE uint32_t random_get32(void);
extern LIBTYPE uint64_t getcurrenttime(void);
extern LIBTYPE void msleep_approx(uint64_t ms);
extern LIBTYPE void msleep_minimum(uint64_t 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);
extern LIBTYPE bool_t hdhomerun_vsprintf(char *buffer, char *end, const char *fmt, va_list ap);
extern LIBTYPE bool_t hdhomerun_sprintf(char *buffer, char *end, const char *fmt, ...);
/*
* 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.
*/
extern LIBTYPE void console_vprintf(const char *fmt, va_list ap);
extern LIBTYPE void console_printf(const char *fmt, ...);
#ifdef __cplusplus
}
#endif

239
hdhomerun_pkt.c Normal file
View file

@ -0,0 +1,239 @@
/*
* hdhomerun_pkt.c
*
* Copyright © 2005-2006 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
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "hdhomerun.h"
struct hdhomerun_pkt_t *hdhomerun_pkt_create(void)
{
struct hdhomerun_pkt_t *pkt = (struct hdhomerun_pkt_t *)calloc(1, sizeof(struct hdhomerun_pkt_t));
if (!pkt) {
return NULL;
}
hdhomerun_pkt_reset(pkt);
return pkt;
}
void hdhomerun_pkt_destroy(struct hdhomerun_pkt_t *pkt)
{
free(pkt);
}
void hdhomerun_pkt_reset(struct hdhomerun_pkt_t *pkt)
{
pkt->limit = pkt->buffer + sizeof(pkt->buffer) - 4;
pkt->start = pkt->buffer + 1024;
pkt->end = pkt->start;
pkt->pos = pkt->start;
}
static uint32_t hdhomerun_pkt_calc_crc(uint8_t *start, uint8_t *end)
{
uint8_t *pos = start;
uint32_t crc = 0xFFFFFFFF;
while (pos < end) {
uint8_t x = (uint8_t)(crc) ^ *pos++;
crc >>= 8;
if (x & 0x01) crc ^= 0x77073096;
if (x & 0x02) crc ^= 0xEE0E612C;
if (x & 0x04) crc ^= 0x076DC419;
if (x & 0x08) crc ^= 0x0EDB8832;
if (x & 0x10) crc ^= 0x1DB71064;
if (x & 0x20) crc ^= 0x3B6E20C8;
if (x & 0x40) crc ^= 0x76DC4190;
if (x & 0x80) crc ^= 0xEDB88320;
}
return crc ^ 0xFFFFFFFF;
}
uint8_t hdhomerun_pkt_read_u8(struct hdhomerun_pkt_t *pkt)
{
uint8_t v = *pkt->pos++;
return v;
}
uint16_t hdhomerun_pkt_read_u16(struct hdhomerun_pkt_t *pkt)
{
uint16_t v;
v = (uint16_t)*pkt->pos++ << 8;
v |= (uint16_t)*pkt->pos++ << 0;
return v;
}
uint32_t hdhomerun_pkt_read_u32(struct hdhomerun_pkt_t *pkt)
{
uint32_t v;
v = (uint32_t)*pkt->pos++ << 24;
v |= (uint32_t)*pkt->pos++ << 16;
v |= (uint32_t)*pkt->pos++ << 8;
v |= (uint32_t)*pkt->pos++ << 0;
return v;
}
size_t hdhomerun_pkt_read_var_length(struct hdhomerun_pkt_t *pkt)
{
size_t length;
if (pkt->pos + 1 > pkt->end) {
return (size_t)-1;
}
length = (size_t)*pkt->pos++;
if (length & 0x0080) {
if (pkt->pos + 1 > pkt->end) {
return (size_t)-1;
}
length &= 0x007F;
length |= (size_t)*pkt->pos++ << 7;
}
return length;
}
uint8_t *hdhomerun_pkt_read_tlv(struct hdhomerun_pkt_t *pkt, uint8_t *ptag, size_t *plength)
{
if (pkt->pos + 2 > pkt->end) {
return NULL;
}
*ptag = hdhomerun_pkt_read_u8(pkt);
*plength = hdhomerun_pkt_read_var_length(pkt);
if (pkt->pos + *plength > pkt->end) {
return NULL;
}
return pkt->pos + *plength;
}
void hdhomerun_pkt_read_mem(struct hdhomerun_pkt_t *pkt, void *mem, size_t length)
{
memcpy(mem, pkt->pos, length);
pkt->pos += length;
}
void hdhomerun_pkt_write_u8(struct hdhomerun_pkt_t *pkt, uint8_t v)
{
*pkt->pos++ = v;
if (pkt->pos > pkt->end) {
pkt->end = pkt->pos;
}
}
void hdhomerun_pkt_write_u16(struct hdhomerun_pkt_t *pkt, uint16_t v)
{
*pkt->pos++ = (uint8_t)(v >> 8);
*pkt->pos++ = (uint8_t)(v >> 0);
if (pkt->pos > pkt->end) {
pkt->end = pkt->pos;
}
}
void hdhomerun_pkt_write_u32(struct hdhomerun_pkt_t *pkt, uint32_t v)
{
*pkt->pos++ = (uint8_t)(v >> 24);
*pkt->pos++ = (uint8_t)(v >> 16);
*pkt->pos++ = (uint8_t)(v >> 8);
*pkt->pos++ = (uint8_t)(v >> 0);
if (pkt->pos > pkt->end) {
pkt->end = pkt->pos;
}
}
void hdhomerun_pkt_write_var_length(struct hdhomerun_pkt_t *pkt, size_t v)
{
if (v <= 127) {
*pkt->pos++ = (uint8_t)v;
} else {
*pkt->pos++ = (uint8_t)(v | 0x80);
*pkt->pos++ = (uint8_t)(v >> 7);
}
if (pkt->pos > pkt->end) {
pkt->end = pkt->pos;
}
}
void hdhomerun_pkt_write_mem(struct hdhomerun_pkt_t *pkt, const void *mem, size_t length)
{
memcpy(pkt->pos, mem, length);
pkt->pos += length;
if (pkt->pos > pkt->end) {
pkt->end = pkt->pos;
}
}
int hdhomerun_pkt_open_frame(struct hdhomerun_pkt_t *pkt, uint16_t *ptype)
{
pkt->pos = pkt->start;
if (pkt->pos + 4 > pkt->end) {
return 0;
}
*ptype = hdhomerun_pkt_read_u16(pkt);
size_t length = hdhomerun_pkt_read_u16(pkt);
pkt->pos += length;
if (pkt->pos + 4 > pkt->end) {
pkt->pos = pkt->start;
return 0;
}
uint32_t calc_crc = hdhomerun_pkt_calc_crc(pkt->start, pkt->pos);
uint32_t packet_crc;
packet_crc = (uint32_t)*pkt->pos++ << 0;
packet_crc |= (uint32_t)*pkt->pos++ << 8;
packet_crc |= (uint32_t)*pkt->pos++ << 16;
packet_crc |= (uint32_t)*pkt->pos++ << 24;
if (calc_crc != packet_crc) {
return -1;
}
pkt->start += 4;
pkt->end = pkt->start + length;
pkt->pos = pkt->start;
return 1;
}
void hdhomerun_pkt_seal_frame(struct hdhomerun_pkt_t *pkt, uint16_t frame_type)
{
size_t length = pkt->end - pkt->start;
pkt->start -= 4;
pkt->pos = pkt->start;
hdhomerun_pkt_write_u16(pkt, frame_type);
hdhomerun_pkt_write_u16(pkt, (uint16_t)length);
uint32_t crc = hdhomerun_pkt_calc_crc(pkt->start, pkt->end);
*pkt->end++ = (uint8_t)(crc >> 0);
*pkt->end++ = (uint8_t)(crc >> 8);
*pkt->end++ = (uint8_t)(crc >> 16);
*pkt->end++ = (uint8_t)(crc >> 24);
pkt->pos = pkt->start;
}

170
hdhomerun_pkt.h Normal file
View file

@ -0,0 +1,170 @@
/*
* hdhomerun_pkt.h
*
* Copyright © 2005-2006 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
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifdef __cplusplus
extern "C" {
#endif
/*
* The discover protocol (UDP port 65001) and control protocol (TCP port 65001)
* both use the same packet based format:
* uint16_t Packet type
* uint16_t Payload length (bytes)
* uint8_t[] Payload data (0-n bytes).
* uint32_t CRC (Ethernet style 32-bit CRC)
*
* All variables are big-endian except for the crc which is little-endian.
*
* Valid values for the packet type are listed below as defines prefixed
* with "HDHOMERUN_TYPE_"
*
* Discovery:
*
* The payload for a discovery request or reply is a simple sequence of
* tag-length-value data:
* uint8_t Tag
* varlen Length
* uint8_t[] Value (0-n bytes)
*
* The length field can be one or two bytes long.
* For a length <= 127 bytes the length is expressed as a single byte. The
* most-significant-bit is clear indicating a single-byte length.
* For a length >= 128 bytes the length is expressed as a sequence of two bytes as follows:
* The first byte is contains the least-significant 7-bits of the length. The
* most-significant bit is then set (add 0x80) to indicate that it is a two byte length.
* The second byte contains the length shifted down 7 bits.
*
* A discovery request packet has a packet type of HDHOMERUN_TYPE_DISCOVER_REQ and should
* contain two tags: HDHOMERUN_TAG_DEVICE_TYPE and HDHOMERUN_TAG_DEVICE_ID.
* The HDHOMERUN_TAG_DEVICE_TYPE value should be set to HDHOMERUN_DEVICE_TYPE_TUNER.
* The HDHOMERUN_TAG_DEVICE_ID value should be set to HDHOMERUN_DEVICE_ID_WILDCARD to match
* all devices, or to the 32-bit device id number to match a single device.
*
* The discovery response packet has a packet type of HDHOMERUN_TYPE_DISCOVER_RPY and has the
* same format as the discovery request packet with the two tags: HDHOMERUN_TAG_DEVICE_TYPE and
* HDHOMERUN_TAG_DEVICE_ID. In the future additional tags may also be returned - unknown tags
* should be skipped and not treated as an error.
*
* Control get/set:
*
* The payload for a control get/set request is a simple sequence of tag-length-value data
* following the same format as for discover packets.
*
* A get request packet has a packet type of HDHOMERUN_TYPE_GETSET_REQ and should contain
* the tag: HDHOMERUN_TAG_GETSET_NAME. The HDHOMERUN_TAG_GETSET_NAME value should be a sequence
* of bytes forming a null-terminated string, including the NULL. The TLV length must include
* the NULL character so the length field should be set to strlen(str) + 1.
*
* A set request packet has a packet type of HDHOMERUN_TYPE_GETSET_REQ (same as a get request)
* and should contain two tags: HDHOMERUN_TAG_GETSET_NAME and HDHOMERUN_TAG_GETSET_VALUE.
* The HDHOMERUN_TAG_GETSET_NAME value should be a sequence of bytes forming a null-terminated
* string, including the NULL.
* The HDHOMERUN_TAG_GETSET_VALUE value should be a sequence of bytes forming a null-terminated
* string, including the NULL.
*
* The get and set reply packets have the packet type HDHOMERUN_TYPE_GETSET_RPY and have the same
* format as the set request packet with the two tags: HDHOMERUN_TAG_GETSET_NAME and
* HDHOMERUN_TAG_GETSET_VALUE. A set request is also implicit get request so the updated value is
* returned.
*
* If the device encounters an error handling the get or set request then the get/set reply packet
* will contain the tag HDHOMERUN_TAG_ERROR_MESSAGE. The format of the value is a sequence of
* bytes forming a null-terminated string, including the NULL.
*
* In the future additional tags may also be returned - unknown tags should be skipped and not
* treated as an error.
*
* Security note: The application should not rely on the NULL character being present. The
* application should write a NULL character based on the TLV length to protect the application
* from a potential attack.
*
* Firmware Upgrade:
*
* A firmware upgrade packet has a packet type of HDHOMERUN_TYPE_UPGRADE_REQ and has a fixed format:
* uint32_t Position in bytes from start of file.
* uint8_t[256] Firmware data (256 bytes)
*
* The data must be uploaded in 256 byte chunks and must be uploaded in order.
* The position number is in bytes so will increment by 256 each time.
*
* When all data is uploaded it should be signaled complete by sending another packet of type
* HDHOMERUN_TYPE_UPGRADE_REQ with payload of a single uint32_t with the value 0xFFFFFFFF.
*/
#define HDHOMERUN_DISCOVER_UDP_PORT 65001
#define HDHOMERUN_CONTROL_TCP_PORT 65001
#define HDHOMERUN_MAX_PACKET_SIZE 1460
#define HDHOMERUN_MAX_PAYLOAD_SIZE 1452
#define HDHOMERUN_TYPE_DISCOVER_REQ 0x0002
#define HDHOMERUN_TYPE_DISCOVER_RPY 0x0003
#define HDHOMERUN_TYPE_GETSET_REQ 0x0004
#define HDHOMERUN_TYPE_GETSET_RPY 0x0005
#define HDHOMERUN_TYPE_UPGRADE_REQ 0x0006
#define HDHOMERUN_TYPE_UPGRADE_RPY 0x0007
#define HDHOMERUN_TAG_DEVICE_TYPE 0x01
#define HDHOMERUN_TAG_DEVICE_ID 0x02
#define HDHOMERUN_TAG_GETSET_NAME 0x03
#define HDHOMERUN_TAG_GETSET_VALUE 0x04
#define HDHOMERUN_TAG_GETSET_LOCKKEY 0x15
#define HDHOMERUN_TAG_ERROR_MESSAGE 0x05
#define HDHOMERUN_TAG_TUNER_COUNT 0x10
#define HDHOMERUN_TAG_DEVICE_AUTH_BIN 0x29
#define HDHOMERUN_TAG_BASE_URL 0x2A
#define HDHOMERUN_TAG_DEVICE_AUTH_STR 0x2B
#define HDHOMERUN_DEVICE_TYPE_WILDCARD 0xFFFFFFFF
#define HDHOMERUN_DEVICE_TYPE_TUNER 0x00000001
#define HDHOMERUN_DEVICE_ID_WILDCARD 0xFFFFFFFF
#define HDHOMERUN_MIN_PEEK_LENGTH 4
struct hdhomerun_pkt_t {
uint8_t *pos;
uint8_t *start;
uint8_t *end;
uint8_t *limit;
uint8_t buffer[3074];
};
extern LIBTYPE struct hdhomerun_pkt_t *hdhomerun_pkt_create(void);
extern LIBTYPE void hdhomerun_pkt_destroy(struct hdhomerun_pkt_t *pkt);
extern LIBTYPE void hdhomerun_pkt_reset(struct hdhomerun_pkt_t *pkt);
extern LIBTYPE uint8_t hdhomerun_pkt_read_u8(struct hdhomerun_pkt_t *pkt);
extern LIBTYPE uint16_t hdhomerun_pkt_read_u16(struct hdhomerun_pkt_t *pkt);
extern LIBTYPE uint32_t hdhomerun_pkt_read_u32(struct hdhomerun_pkt_t *pkt);
extern LIBTYPE size_t hdhomerun_pkt_read_var_length(struct hdhomerun_pkt_t *pkt);
extern LIBTYPE uint8_t *hdhomerun_pkt_read_tlv(struct hdhomerun_pkt_t *pkt, uint8_t *ptag, size_t *plength);
extern LIBTYPE void hdhomerun_pkt_read_mem(struct hdhomerun_pkt_t *pkt, void *mem, size_t length);
extern LIBTYPE void hdhomerun_pkt_write_u8(struct hdhomerun_pkt_t *pkt, uint8_t v);
extern LIBTYPE void hdhomerun_pkt_write_u16(struct hdhomerun_pkt_t *pkt, uint16_t v);
extern LIBTYPE void hdhomerun_pkt_write_u32(struct hdhomerun_pkt_t *pkt, uint32_t v);
extern LIBTYPE void hdhomerun_pkt_write_var_length(struct hdhomerun_pkt_t *pkt, size_t v);
extern LIBTYPE void hdhomerun_pkt_write_mem(struct hdhomerun_pkt_t *pkt, const void *mem, size_t length);
extern LIBTYPE bool_t hdhomerun_pkt_open_frame(struct hdhomerun_pkt_t *pkt, uint16_t *ptype);
extern LIBTYPE void hdhomerun_pkt_seal_frame(struct hdhomerun_pkt_t *pkt, uint16_t frame_type);
#ifdef __cplusplus
}
#endif

59
hdhomerun_sock.h Normal file
View file

@ -0,0 +1,59 @@
/*
* hdhomerun_sock.h
*
* Copyright © 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
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifdef __cplusplus
extern "C" {
#endif
struct hdhomerun_local_ip_info_t {
uint32_t ip_addr;
uint32_t subnet_mask;
};
extern LIBTYPE int hdhomerun_local_ip_info(struct hdhomerun_local_ip_info_t ip_info_list[], int max_count);
#define HDHOMERUN_SOCK_INVALID -1
typedef int hdhomerun_sock_t;
extern LIBTYPE hdhomerun_sock_t hdhomerun_sock_create_udp(void);
extern LIBTYPE hdhomerun_sock_t hdhomerun_sock_create_tcp(void);
extern LIBTYPE void hdhomerun_sock_destroy(hdhomerun_sock_t sock);
extern LIBTYPE int hdhomerun_sock_getlasterror(void);
extern LIBTYPE uint32_t hdhomerun_sock_getsockname_addr(hdhomerun_sock_t sock);
extern LIBTYPE uint16_t hdhomerun_sock_getsockname_port(hdhomerun_sock_t sock);
extern LIBTYPE uint32_t hdhomerun_sock_getpeername_addr(hdhomerun_sock_t sock);
extern LIBTYPE uint32_t hdhomerun_sock_getaddrinfo_addr(hdhomerun_sock_t sock, const char *name);
extern LIBTYPE bool_t hdhomerun_sock_join_multicast_group(hdhomerun_sock_t sock, uint32_t multicast_ip, uint32_t local_ip);
extern LIBTYPE bool_t hdhomerun_sock_leave_multicast_group(hdhomerun_sock_t sock, uint32_t multicast_ip, uint32_t local_ip);
extern LIBTYPE bool_t hdhomerun_sock_bind(hdhomerun_sock_t sock, uint32_t local_addr, uint16_t local_port, bool_t allow_reuse);
extern LIBTYPE bool_t hdhomerun_sock_connect(hdhomerun_sock_t sock, uint32_t remote_addr, uint16_t remote_port, uint64_t timeout);
extern LIBTYPE bool_t hdhomerun_sock_send(hdhomerun_sock_t sock, const void *data, size_t length, uint64_t timeout);
extern LIBTYPE bool_t hdhomerun_sock_sendto(hdhomerun_sock_t sock, uint32_t remote_addr, uint16_t remote_port, const void *data, size_t length, uint64_t timeout);
extern LIBTYPE bool_t hdhomerun_sock_recv(hdhomerun_sock_t sock, void *data, size_t *length, uint64_t timeout);
extern LIBTYPE bool_t hdhomerun_sock_recvfrom(hdhomerun_sock_t sock, uint32_t *remote_addr, uint16_t *remote_port, void *data, size_t *length, uint64_t timeout);
#ifdef __cplusplus
}
#endif

451
hdhomerun_sock_posix.c Normal file
View file

@ -0,0 +1,451 @@
/*
* hdhomerun_sock_posix.c
*
* Copyright © 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
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
/*
* Implementation notes:
*
* API specifies timeout for each operation (or zero for non-blocking).
*
* It is not possible to rely on the OS socket timeout as this will fail to
* detect the command-response situation where data is sent successfully and
* the other end chooses not to send a response (other than the TCP ack).
*
* The select() cannot be used with high socket numbers (typically max 1024)
* so the code works as follows:
* - Use non-blocking sockets to allow operation without select.
* - Use select where safe (low socket numbers).
* - Poll with short sleep when select cannot be used safely.
*/
#include "hdhomerun.h"
#include <net/if.h>
#include <sys/ioctl.h>
#ifndef SIOCGIFCONF
#include <sys/sockio.h>
#endif
#ifndef _SIZEOF_ADDR_IFREQ
#define _SIZEOF_ADDR_IFREQ(x) sizeof(x)
#endif
#ifndef MSG_NOSIGNAL
#define MSG_NOSIGNAL 0
#endif
int hdhomerun_local_ip_info(struct hdhomerun_local_ip_info_t ip_info_list[], int max_count)
{
int sock = socket(AF_INET, SOCK_DGRAM, 0);
if (sock == HDHOMERUN_SOCK_INVALID) {
return -1;
}
struct ifconf ifc;
size_t ifreq_buffer_size = 1024;
while (1) {
ifc.ifc_len = ifreq_buffer_size;
ifc.ifc_buf = (char *)malloc(ifreq_buffer_size);
if (!ifc.ifc_buf) {
close(sock);
return -1;
}
memset(ifc.ifc_buf, 0, ifreq_buffer_size);
if (ioctl(sock, SIOCGIFCONF, &ifc) != 0) {
free(ifc.ifc_buf);
close(sock);
return -1;
}
if (ifc.ifc_len < ifreq_buffer_size) {
break;
}
free(ifc.ifc_buf);
ifreq_buffer_size += 1024;
}
char *ptr = ifc.ifc_buf;
char *end = ifc.ifc_buf + ifc.ifc_len;
int count = 0;
while (ptr < end) {
struct ifreq *ifr = (struct ifreq *)ptr;
ptr += _SIZEOF_ADDR_IFREQ(*ifr);
/* Flags. */
if (ioctl(sock, SIOCGIFFLAGS, ifr) != 0) {
continue;
}
if ((ifr->ifr_flags & IFF_UP) == 0) {
continue;
}
if ((ifr->ifr_flags & IFF_RUNNING) == 0) {
continue;
}
/* Local IP address. */
if (ioctl(sock, SIOCGIFADDR, ifr) != 0) {
continue;
}
struct sockaddr_in *ip_addr_in = (struct sockaddr_in *)&(ifr->ifr_addr);
uint32_t ip_addr = ntohl(ip_addr_in->sin_addr.s_addr);
if (ip_addr == 0) {
continue;
}
/* Subnet mask. */
if (ioctl(sock, SIOCGIFNETMASK, ifr) != 0) {
continue;
}
struct sockaddr_in *subnet_mask_in = (struct sockaddr_in *)&(ifr->ifr_addr);
uint32_t subnet_mask = ntohl(subnet_mask_in->sin_addr.s_addr);
/* Report. */
if (count < max_count) {
struct hdhomerun_local_ip_info_t *ip_info = &ip_info_list[count];
ip_info->ip_addr = ip_addr;
ip_info->subnet_mask = subnet_mask;
}
count++;
}
free(ifc.ifc_buf);
close(sock);
return count;
}
hdhomerun_sock_t hdhomerun_sock_create_udp(void)
{
/* Create socket. */
hdhomerun_sock_t sock = (hdhomerun_sock_t)socket(AF_INET, SOCK_DGRAM, 0);
if (sock == -1) {
return HDHOMERUN_SOCK_INVALID;
}
/* Set non-blocking */
if (fcntl(sock, F_SETFL, O_NONBLOCK) != 0) {
close(sock);
return HDHOMERUN_SOCK_INVALID;
}
/* Allow broadcast. */
int sock_opt = 1;
setsockopt(sock, SOL_SOCKET, SO_BROADCAST, (char *)&sock_opt, sizeof(sock_opt));
/* Success. */
return sock;
}
hdhomerun_sock_t hdhomerun_sock_create_tcp(void)
{
/* Create socket. */
hdhomerun_sock_t sock = (hdhomerun_sock_t)socket(AF_INET, SOCK_STREAM, 0);
if (sock == -1) {
return HDHOMERUN_SOCK_INVALID;
}
/* Set non-blocking */
if (fcntl(sock, F_SETFL, O_NONBLOCK) != 0) {
close(sock);
return HDHOMERUN_SOCK_INVALID;
}
/* Success. */
return sock;
}
void hdhomerun_sock_destroy(hdhomerun_sock_t sock)
{
close(sock);
}
int hdhomerun_sock_getlasterror(void)
{
return errno;
}
uint32_t hdhomerun_sock_getsockname_addr(hdhomerun_sock_t sock)
{
struct sockaddr_in sock_addr;
socklen_t sockaddr_size = sizeof(sock_addr);
if (getsockname(sock, (struct sockaddr *)&sock_addr, &sockaddr_size) != 0) {
return 0;
}
return ntohl(sock_addr.sin_addr.s_addr);
}
uint16_t hdhomerun_sock_getsockname_port(hdhomerun_sock_t sock)
{
struct sockaddr_in sock_addr;
socklen_t sockaddr_size = sizeof(sock_addr);
if (getsockname(sock, (struct sockaddr *)&sock_addr, &sockaddr_size) != 0) {
return 0;
}
return ntohs(sock_addr.sin_port);
}
uint32_t hdhomerun_sock_getpeername_addr(hdhomerun_sock_t sock)
{
struct sockaddr_in sock_addr;
socklen_t sockaddr_size = sizeof(sock_addr);
if (getpeername(sock, (struct sockaddr *)&sock_addr, &sockaddr_size) != 0) {
return 0;
}
return ntohl(sock_addr.sin_addr.s_addr);
}
uint32_t hdhomerun_sock_getaddrinfo_addr(hdhomerun_sock_t sock, const char *name)
{
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(name, NULL, &hints, &sock_info) != 0) {
return 0;
}
struct sockaddr_in *sock_addr = (struct sockaddr_in *)sock_info->ai_addr;
uint32_t addr = ntohl(sock_addr->sin_addr.s_addr);
freeaddrinfo(sock_info);
return addr;
}
bool_t hdhomerun_sock_join_multicast_group(hdhomerun_sock_t sock, uint32_t multicast_ip, uint32_t local_ip)
{
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(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, (const char *)&imr, sizeof(imr)) != 0) {
return FALSE;
}
return TRUE;
}
bool_t hdhomerun_sock_leave_multicast_group(hdhomerun_sock_t sock, uint32_t multicast_ip, uint32_t local_ip)
{
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(sock, IPPROTO_IP, IP_DROP_MEMBERSHIP, (const char *)&imr, sizeof(imr)) != 0) {
return FALSE;
}
return TRUE;
}
bool_t hdhomerun_sock_bind(hdhomerun_sock_t sock, uint32_t local_addr, uint16_t local_port, bool_t allow_reuse)
{
int sock_opt = allow_reuse;
setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *)&sock_opt, sizeof(sock_opt));
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_addr);
sock_addr.sin_port = htons(local_port);
if (bind(sock, (struct sockaddr *)&sock_addr, sizeof(sock_addr)) != 0) {
return FALSE;
}
return TRUE;
}
static bool_t hdhomerun_sock_wait_for_event(hdhomerun_sock_t sock, short event_type, uint64_t stop_time)
{
uint64_t current_time = getcurrenttime();
if (current_time >= stop_time) {
return FALSE;
}
struct pollfd poll_event;
poll_event.fd = sock;
poll_event.events = event_type;
poll_event.revents = 0;
uint64_t timeout = stop_time - current_time;
if (poll(&poll_event, 1, (int)timeout) <= 0) {
return FALSE;
}
if ((poll_event.revents & event_type) == 0) {
return FALSE;
}
return TRUE;
}
bool_t hdhomerun_sock_connect(hdhomerun_sock_t sock, uint32_t remote_addr, uint16_t remote_port, uint64_t timeout)
{
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(remote_addr);
sock_addr.sin_port = htons(remote_port);
if (connect(sock, (struct sockaddr *)&sock_addr, sizeof(sock_addr)) != 0) {
if ((errno != EAGAIN) && (errno != EWOULDBLOCK) && (errno != EINPROGRESS)) {
return FALSE;
}
}
uint64_t stop_time = getcurrenttime() + timeout;
return hdhomerun_sock_wait_for_event(sock, POLLOUT, stop_time);
}
bool_t hdhomerun_sock_send(hdhomerun_sock_t sock, const void *data, size_t length, uint64_t timeout)
{
uint64_t stop_time = getcurrenttime() + timeout;
const uint8_t *ptr = (const uint8_t *)data;
while (1) {
int ret = send(sock, ptr, length, MSG_NOSIGNAL);
if (ret <= 0) {
if ((errno != EAGAIN) && (errno != EWOULDBLOCK) && (errno != EINPROGRESS)) {
return FALSE;
}
if (!hdhomerun_sock_wait_for_event(sock, POLLOUT, stop_time)) {
return FALSE;
}
continue;
}
if (ret < (int)length) {
ptr += ret;
length -= ret;
continue;
}
return TRUE;
}
}
bool_t hdhomerun_sock_sendto(hdhomerun_sock_t sock, uint32_t remote_addr, uint16_t remote_port, const void *data, size_t length, uint64_t timeout)
{
uint64_t stop_time = getcurrenttime() + timeout;
const uint8_t *ptr = (const uint8_t *)data;
while (1) {
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(remote_addr);
sock_addr.sin_port = htons(remote_port);
int ret = sendto(sock, ptr, length, 0, (struct sockaddr *)&sock_addr, sizeof(sock_addr));
if (ret <= 0) {
if ((errno != EAGAIN) && (errno != EWOULDBLOCK) && (errno != EINPROGRESS)) {
return FALSE;
}
if (!hdhomerun_sock_wait_for_event(sock, POLLOUT, stop_time)) {
return FALSE;
}
continue;
}
if (ret < (int)length) {
ptr += ret;
length -= ret;
continue;
}
return TRUE;
}
}
bool_t hdhomerun_sock_recv(hdhomerun_sock_t sock, void *data, size_t *length, uint64_t timeout)
{
uint64_t stop_time = getcurrenttime() + timeout;
while (1) {
int ret = recv(sock, data, *length, 0);
if (ret < 0) {
if ((errno != EAGAIN) && (errno != EWOULDBLOCK) && (errno != EINPROGRESS)) {
return FALSE;
}
if (!hdhomerun_sock_wait_for_event(sock, POLLIN, stop_time)) {
return FALSE;
}
continue;
}
if (ret == 0) {
return FALSE;
}
*length = ret;
return TRUE;
}
}
bool_t hdhomerun_sock_recvfrom(hdhomerun_sock_t sock, uint32_t *remote_addr, uint16_t *remote_port, void *data, size_t *length, uint64_t timeout)
{
uint64_t stop_time = getcurrenttime() + timeout;
while (1) {
struct sockaddr_in sock_addr;
memset(&sock_addr, 0, sizeof(sock_addr));
socklen_t sockaddr_size = sizeof(sock_addr);
int ret = recvfrom(sock, data, *length, 0, (struct sockaddr *)&sock_addr, &sockaddr_size);
if (ret < 0) {
if ((errno != EAGAIN) && (errno != EWOULDBLOCK) && (errno != EINPROGRESS)) {
return FALSE;
}
if (!hdhomerun_sock_wait_for_event(sock, POLLIN, stop_time)) {
return FALSE;
}
continue;
}
if (ret == 0) {
return FALSE;
}
*remote_addr = ntohl(sock_addr.sin_addr.s_addr);
*remote_port = ntohs(sock_addr.sin_port);
*length = ret;
return TRUE;
}
}

450
hdhomerun_sock_windows.c Normal file
View file

@ -0,0 +1,450 @@
/*
* hdhomerun_sock_windows.c
*
* Copyright © 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
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
/*
* Implementation notes:
*
* API specifies timeout for each operation (or zero for non-blocking).
*
* It is not possible to rely on the OS socket timeout as this will fail to
* detect the command-response situation where data is sent successfully and
* the other end chooses not to send a response (other than the TCP ack).
*
* Windows supports select() however native WSA events are used to:
* - avoid problems with socket numbers above 1024.
* - wait without allowing other events handlers to run (important for use
* with win7 WMC).
*/
#include "hdhomerun.h"
#include <windows.h>
#include <iphlpapi.h>
int hdhomerun_local_ip_info(struct hdhomerun_local_ip_info_t ip_info_list[], int max_count)
{
PIP_ADAPTER_INFO AdapterInfo;
ULONG AdapterInfoLength = sizeof(IP_ADAPTER_INFO) * 16;
while (1) {
AdapterInfo = (IP_ADAPTER_INFO *)malloc(AdapterInfoLength);
if (!AdapterInfo) {
return -1;
}
ULONG LengthNeeded = AdapterInfoLength;
DWORD Ret = GetAdaptersInfo(AdapterInfo, &LengthNeeded);
if (Ret == NO_ERROR) {
break;
}
free(AdapterInfo);
if (Ret != ERROR_BUFFER_OVERFLOW) {
return -1;
}
if (AdapterInfoLength >= LengthNeeded) {
return -1;
}
AdapterInfoLength = LengthNeeded;
}
int count = 0;
PIP_ADAPTER_INFO Adapter = AdapterInfo;
while (Adapter) {
IP_ADDR_STRING *IPAddr = &Adapter->IpAddressList;
while (IPAddr) {
uint32_t ip_addr = ntohl(inet_addr(IPAddr->IpAddress.String));
uint32_t subnet_mask = ntohl(inet_addr(IPAddr->IpMask.String));
if (ip_addr == 0) {
IPAddr = IPAddr->Next;
continue;
}
if (count < max_count) {
struct hdhomerun_local_ip_info_t *ip_info = &ip_info_list[count];
ip_info->ip_addr = ip_addr;
ip_info->subnet_mask = subnet_mask;
}
count++;
IPAddr = IPAddr->Next;
}
if (count >= max_count) {
break;
}
Adapter = Adapter->Next;
}
free(AdapterInfo);
return count;
}
hdhomerun_sock_t hdhomerun_sock_create_udp(void)
{
/* Create socket. */
hdhomerun_sock_t sock = (hdhomerun_sock_t)socket(AF_INET, SOCK_DGRAM, 0);
if (sock == -1) {
return HDHOMERUN_SOCK_INVALID;
}
/* Set non-blocking */
unsigned long mode = 1;
if (ioctlsocket(sock, FIONBIO, &mode) != 0) {
closesocket(sock);
return HDHOMERUN_SOCK_INVALID;
}
/* Allow broadcast. */
int sock_opt = 1;
setsockopt(sock, SOL_SOCKET, SO_BROADCAST, (char *)&sock_opt, sizeof(sock_opt));
/* Success. */
return sock;
}
hdhomerun_sock_t hdhomerun_sock_create_tcp(void)
{
/* Create socket. */
hdhomerun_sock_t sock = (hdhomerun_sock_t)socket(AF_INET, SOCK_STREAM, 0);
if (sock == -1) {
return HDHOMERUN_SOCK_INVALID;
}
/* Set non-blocking */
unsigned long mode = 1;
if (ioctlsocket(sock, FIONBIO, &mode) != 0) {
closesocket(sock);
return HDHOMERUN_SOCK_INVALID;
}
/* Success. */
return sock;
}
void hdhomerun_sock_destroy(hdhomerun_sock_t sock)
{
closesocket(sock);
}
int hdhomerun_sock_getlasterror(void)
{
return WSAGetLastError();
}
uint32_t hdhomerun_sock_getsockname_addr(hdhomerun_sock_t sock)
{
struct sockaddr_in sock_addr;
int sockaddr_size = sizeof(sock_addr);
if (getsockname(sock, (struct sockaddr *)&sock_addr, &sockaddr_size) != 0) {
return 0;
}
return ntohl(sock_addr.sin_addr.s_addr);
}
uint16_t hdhomerun_sock_getsockname_port(hdhomerun_sock_t sock)
{
struct sockaddr_in sock_addr;
int sockaddr_size = sizeof(sock_addr);
if (getsockname(sock, (struct sockaddr *)&sock_addr, &sockaddr_size) != 0) {
return 0;
}
return ntohs(sock_addr.sin_port);
}
uint32_t hdhomerun_sock_getpeername_addr(hdhomerun_sock_t sock)
{
struct sockaddr_in sock_addr;
int sockaddr_size = sizeof(sock_addr);
if (getpeername(sock, (struct sockaddr *)&sock_addr, &sockaddr_size) != 0) {
return 0;
}
return ntohl(sock_addr.sin_addr.s_addr);
}
uint32_t hdhomerun_sock_getaddrinfo_addr(hdhomerun_sock_t sock, const char *name)
{
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(name, NULL, &hints, &sock_info) != 0) {
return 0;
}
struct sockaddr_in *sock_addr = (struct sockaddr_in *)sock_info->ai_addr;
uint32_t addr = ntohl(sock_addr->sin_addr.s_addr);
freeaddrinfo(sock_info);
return addr;
}
bool_t hdhomerun_sock_join_multicast_group(hdhomerun_sock_t sock, uint32_t multicast_ip, uint32_t local_ip)
{
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(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, (const char *)&imr, sizeof(imr)) != 0) {
return FALSE;
}
return TRUE;
}
bool_t hdhomerun_sock_leave_multicast_group(hdhomerun_sock_t sock, uint32_t multicast_ip, uint32_t local_ip)
{
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(sock, IPPROTO_IP, IP_DROP_MEMBERSHIP, (const char *)&imr, sizeof(imr)) != 0) {
return FALSE;
}
return TRUE;
}
bool_t hdhomerun_sock_bind(hdhomerun_sock_t sock, uint32_t local_addr, uint16_t local_port, bool_t allow_reuse)
{
int sock_opt = allow_reuse;
setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *)&sock_opt, sizeof(sock_opt));
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_addr);
sock_addr.sin_port = htons(local_port);
if (bind(sock, (struct sockaddr *)&sock_addr, sizeof(sock_addr)) != 0) {
return FALSE;
}
return TRUE;
}
bool_t hdhomerun_sock_connect(hdhomerun_sock_t sock, uint32_t remote_addr, uint16_t remote_port, uint64_t timeout)
{
/* Connect (non-blocking). */
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(remote_addr);
sock_addr.sin_port = htons(remote_port);
if (connect(sock, (struct sockaddr *)&sock_addr, sizeof(sock_addr)) != 0) {
if (WSAGetLastError() != WSAEWOULDBLOCK) {
return FALSE;
}
}
/* Wait for connect to complete (both success and failure will signal). */
WSAEVENT wsa_event = WSACreateEvent();
if (wsa_event == WSA_INVALID_EVENT) {
return FALSE;
}
if (WSAEventSelect(sock, wsa_event, FD_WRITE | FD_CLOSE) == SOCKET_ERROR) {
WSACloseEvent(wsa_event);
return FALSE;
}
DWORD ret = WaitForSingleObjectEx(wsa_event, (DWORD)timeout, FALSE);
WSACloseEvent(wsa_event);
if (ret != WAIT_OBJECT_0) {
return FALSE;
}
/* Detect success/failure. */
wsa_event = WSACreateEvent();
if (wsa_event == WSA_INVALID_EVENT) {
return FALSE;
}
if (WSAEventSelect(sock, wsa_event, FD_CLOSE) == SOCKET_ERROR) {
WSACloseEvent(wsa_event);
return FALSE;
}
ret = WaitForSingleObjectEx(wsa_event, 0, FALSE);
WSACloseEvent(wsa_event);
if (ret == WAIT_OBJECT_0) {
return FALSE;
}
return TRUE;
}
static bool_t hdhomerun_sock_wait_for_event(hdhomerun_sock_t sock, long event_type, uint64_t stop_time)
{
uint64_t current_time = getcurrenttime();
if (current_time >= stop_time) {
return FALSE;
}
WSAEVENT wsa_event = WSACreateEvent();
if (wsa_event == WSA_INVALID_EVENT) {
return FALSE;
}
if (WSAEventSelect(sock, wsa_event, event_type) == SOCKET_ERROR) {
WSACloseEvent(wsa_event);
return FALSE;
}
DWORD ret = WaitForSingleObjectEx(wsa_event, (DWORD)(stop_time - current_time), FALSE);
WSACloseEvent(wsa_event);
if (ret != WAIT_OBJECT_0) {
return FALSE;
}
return TRUE;
}
bool_t hdhomerun_sock_send(hdhomerun_sock_t sock, const void *data, size_t length, uint64_t timeout)
{
uint64_t stop_time = getcurrenttime() + timeout;
const uint8_t *ptr = (uint8_t *)data;
while (1) {
int ret = send(sock, (char *)ptr, (int)length, 0);
if (ret <= 0) {
if (WSAGetLastError() != WSAEWOULDBLOCK) {
return FALSE;
}
if (!hdhomerun_sock_wait_for_event(sock, FD_WRITE | FD_CLOSE, stop_time)) {
return FALSE;
}
continue;
}
if (ret < (int)length) {
ptr += ret;
length -= ret;
continue;
}
return TRUE;
}
}
bool_t hdhomerun_sock_sendto(hdhomerun_sock_t sock, uint32_t remote_addr, uint16_t remote_port, const void *data, size_t length, uint64_t timeout)
{
uint64_t stop_time = getcurrenttime() + timeout;
const uint8_t *ptr = (uint8_t *)data;
while (1) {
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(remote_addr);
sock_addr.sin_port = htons(remote_port);
int ret = sendto(sock, (char *)ptr, (int)length, 0, (struct sockaddr *)&sock_addr, sizeof(sock_addr));
if (ret <= 0) {
if (WSAGetLastError() != WSAEWOULDBLOCK) {
return FALSE;
}
if (!hdhomerun_sock_wait_for_event(sock, FD_WRITE | FD_CLOSE, stop_time)) {
return FALSE;
}
continue;
}
if (ret < (int)length) {
ptr += ret;
length -= ret;
continue;
}
return TRUE;
}
}
bool_t hdhomerun_sock_recv(hdhomerun_sock_t sock, void *data, size_t *length, uint64_t timeout)
{
uint64_t stop_time = getcurrenttime() + timeout;
while (1) {
int ret = recv(sock, (char *)data, (int)(*length), 0);
if (ret < 0) {
if (WSAGetLastError() != WSAEWOULDBLOCK) {
return FALSE;
}
if (!hdhomerun_sock_wait_for_event(sock, FD_READ | FD_CLOSE, stop_time)) {
return FALSE;
}
continue;
}
if (ret == 0) {
return FALSE;
}
*length = ret;
return TRUE;
}
}
bool_t hdhomerun_sock_recvfrom(hdhomerun_sock_t sock, uint32_t *remote_addr, uint16_t *remote_port, void *data, size_t *length, uint64_t timeout)
{
uint64_t stop_time = getcurrenttime() + timeout;
while (1) {
struct sockaddr_in sock_addr;
memset(&sock_addr, 0, sizeof(sock_addr));
int sockaddr_size = sizeof(sock_addr);
int ret = recvfrom(sock, (char *)data, (int)(*length), 0, (struct sockaddr *)&sock_addr, &sockaddr_size);
if (ret < 0) {
if (WSAGetLastError() != WSAEWOULDBLOCK) {
return FALSE;
}
if (!hdhomerun_sock_wait_for_event(sock, FD_READ | FD_CLOSE, stop_time)) {
return FALSE;
}
continue;
}
if (ret == 0) {
return FALSE;
}
*remote_addr = ntohl(sock_addr.sin_addr.s_addr);
*remote_port = ntohs(sock_addr.sin_port);
*length = ret;
return TRUE;
}
}

80
hdhomerun_types.h Normal file
View file

@ -0,0 +1,80 @@
/*
* hdhomerun_types.h
*
* Copyright © 2008-2009 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
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#define HDHOMERUN_STATUS_COLOR_NEUTRAL 0xFFFFFFFF
#define HDHOMERUN_STATUS_COLOR_RED 0xFFFF0000
#define HDHOMERUN_STATUS_COLOR_YELLOW 0xFFFFFF00
#define HDHOMERUN_STATUS_COLOR_GREEN 0xFF00C000
struct hdhomerun_device_t;
struct hdhomerun_device_allocation_t;
struct hdhomerun_tuner_status_t {
char channel[32];
char lock_str[32];
bool_t signal_present;
bool_t lock_supported;
bool_t lock_unsupported;
unsigned int signal_strength;
unsigned int signal_to_noise_quality;
unsigned int symbol_error_quality;
uint32_t raw_bits_per_second;
uint32_t packets_per_second;
};
struct hdhomerun_tuner_vstatus_t {
char vchannel[32];
char name[32];
char auth[32];
char cci[32];
char cgms[32];
bool_t not_subscribed;
bool_t not_available;
bool_t copy_protected;
};
struct hdhomerun_channelscan_program_t {
char program_str[64];
uint16_t program_number;
uint16_t virtual_major;
uint16_t virtual_minor;
uint16_t type;
char name[32];
};
#define HDHOMERUN_CHANNELSCAN_MAX_PROGRAM_COUNT 64
struct hdhomerun_channelscan_result_t {
char channel_str[64];
uint32_t channelmap;
uint32_t frequency;
struct hdhomerun_tuner_status_t status;
int program_count;
struct hdhomerun_channelscan_program_t programs[HDHOMERUN_CHANNELSCAN_MAX_PROGRAM_COUNT];
bool_t transport_stream_id_detected;
bool_t original_network_id_detected;
uint16_t transport_stream_id;
uint16_t original_network_id;
};
struct hdhomerun_plotsample_t {
int16_t real;
int16_t imag;
};

383
hdhomerun_video.c Normal file
View file

@ -0,0 +1,383 @@
/*
* hdhomerun_video.c
*
* 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
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "hdhomerun.h"
struct hdhomerun_video_sock_t {
pthread_mutex_t lock;
struct hdhomerun_debug_t *dbg;
hdhomerun_sock_t sock;
volatile size_t head;
volatile size_t tail;
uint8_t *buffer;
size_t buffer_size;
size_t advance;
pthread_t thread;
volatile bool_t terminate;
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];
};
static THREAD_FUNC_PREFIX hdhomerun_video_thread_execute(void *arg);
struct hdhomerun_video_sock_t *hdhomerun_video_create(uint16_t listen_port, bool_t allow_port_reuse, size_t buffer_size, struct hdhomerun_debug_t *dbg)
{
/* Create object. */
struct hdhomerun_video_sock_t *vs = (struct hdhomerun_video_sock_t *)calloc(1, sizeof(struct hdhomerun_video_sock_t));
if (!vs) {
hdhomerun_debug_printf(dbg, "hdhomerun_video_create: failed to allocate video object\n");
return NULL;
}
vs->dbg = dbg;
vs->sock = HDHOMERUN_SOCK_INVALID;
pthread_mutex_init(&vs->lock, NULL);
/* Reset sequence tracking. */
hdhomerun_video_flush(vs);
/* Buffer size. */
vs->buffer_size = (buffer_size / VIDEO_DATA_PACKET_SIZE) * VIDEO_DATA_PACKET_SIZE;
if (vs->buffer_size == 0) {
hdhomerun_debug_printf(dbg, "hdhomerun_video_create: invalid buffer size (%lu bytes)\n", (unsigned long)buffer_size);
goto error;
}
vs->buffer_size += VIDEO_DATA_PACKET_SIZE;
/* Create buffer. */
vs->buffer = (uint8_t *)malloc(vs->buffer_size);
if (!vs->buffer) {
hdhomerun_debug_printf(dbg, "hdhomerun_video_create: failed to allocate buffer (%lu bytes)\n", (unsigned long)vs->buffer_size);
goto error;
}
/* Create socket. */
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;
}
/* Expand socket buffer size. */
int rx_size = 1024 * 1024;
setsockopt(vs->sock, SOL_SOCKET, SO_RCVBUF, (char *)&rx_size, sizeof(rx_size));
/* Bind socket. */
if (!hdhomerun_sock_bind(vs->sock, INADDR_ANY, listen_port, allow_port_reuse)) {
hdhomerun_debug_printf(dbg, "hdhomerun_video_create: failed to bind socket (port %u)\n", listen_port);
goto error;
}
/* Start thread. */
if (pthread_create(&vs->thread, NULL, &hdhomerun_video_thread_execute, vs) != 0) {
hdhomerun_debug_printf(dbg, "hdhomerun_video_create: failed to start thread\n");
goto error;
}
/* Success. */
return vs;
error:
if (vs->sock != HDHOMERUN_SOCK_INVALID) {
hdhomerun_sock_destroy(vs->sock);
}
if (vs->buffer) {
free(vs->buffer);
}
free(vs);
return NULL;
}
void hdhomerun_video_destroy(struct hdhomerun_video_sock_t *vs)
{
vs->terminate = TRUE;
pthread_join(vs->thread, NULL);
hdhomerun_sock_destroy(vs->sock);
free(vs->buffer);
free(vs);
}
hdhomerun_sock_t hdhomerun_video_get_sock(struct hdhomerun_video_sock_t *vs)
{
return vs->sock;
}
uint16_t hdhomerun_video_get_local_port(struct hdhomerun_video_sock_t *vs)
{
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 port;
}
int hdhomerun_video_join_multicast_group(struct hdhomerun_video_sock_t *vs, uint32_t multicast_ip, uint32_t local_ip)
{
if (!hdhomerun_sock_join_multicast_group(vs->sock, multicast_ip, local_ip)) {
hdhomerun_debug_printf(vs->dbg, "hdhomerun_video_join_multicast_group: setsockopt failed (%d)\n", hdhomerun_sock_getlasterror());
return -1;
}
return 1;
}
void hdhomerun_video_leave_multicast_group(struct hdhomerun_video_sock_t *vs, uint32_t multicast_ip, uint32_t local_ip)
{
if (!hdhomerun_sock_leave_multicast_group(vs->sock, multicast_ip, local_ip)) {
hdhomerun_debug_printf(vs->dbg, "hdhomerun_video_leave_multicast_group: setsockopt failed (%d)\n", hdhomerun_sock_getlasterror());
}
}
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];
if (packet_identifier == 0x1FFF) {
return;
}
bool_t transport_error = ptr[1] >> 7;
if (transport_error) {
vs->transport_error_count++;
vs->sequence[packet_identifier] = 0xFF;
return;
}
uint8_t sequence = ptr[3] & 0x0F;
uint8_t previous_sequence = vs->sequence[packet_identifier];
vs->sequence[packet_identifier] = sequence;
if (previous_sequence == 0xFF) {
return;
}
if (sequence == ((previous_sequence + 1) & 0x0F)) {
return;
}
if (sequence == previous_sequence) {
return;
}
vs->sequence_error_count++;
}
static void hdhomerun_video_parse_rtp(struct hdhomerun_video_sock_t *vs, struct hdhomerun_pkt_t *pkt)
{
pkt->pos += 2;
uint32_t rtp_sequence = hdhomerun_pkt_read_u16(pkt);
pkt->pos += 8;
uint32_t previous_rtp_sequence = vs->rtp_sequence;
vs->rtp_sequence = rtp_sequence;
/* Initial case - first packet received. */
if (previous_rtp_sequence == 0xFFFFFFFF) {
return;
}
/* 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)
{
struct hdhomerun_video_sock_t *vs = (struct hdhomerun_video_sock_t *)arg;
struct hdhomerun_pkt_t pkt_inst;
while (!vs->terminate) {
struct hdhomerun_pkt_t *pkt = &pkt_inst;
hdhomerun_pkt_reset(pkt);
/* Receive. */
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) {
hdhomerun_video_parse_rtp(vs, pkt);
length = (int)(pkt->end - pkt->pos);
}
if (length != VIDEO_DATA_PACKET_SIZE) {
/* Data received but not valid - ignore. */
continue;
}
pthread_mutex_lock(&vs->lock);
/* Store in ring buffer. */
size_t head = vs->head;
uint8_t *ptr = vs->buffer + head;
memcpy(ptr, pkt->pos, length);
/* Stats. */
vs->packet_count++;
hdhomerun_video_stats_ts_pkt(vs, ptr + TS_PACKET_SIZE * 0);
hdhomerun_video_stats_ts_pkt(vs, ptr + TS_PACKET_SIZE * 1);
hdhomerun_video_stats_ts_pkt(vs, ptr + TS_PACKET_SIZE * 2);
hdhomerun_video_stats_ts_pkt(vs, ptr + TS_PACKET_SIZE * 3);
hdhomerun_video_stats_ts_pkt(vs, ptr + TS_PACKET_SIZE * 4);
hdhomerun_video_stats_ts_pkt(vs, ptr + TS_PACKET_SIZE * 5);
hdhomerun_video_stats_ts_pkt(vs, ptr + TS_PACKET_SIZE * 6);
/* Calculate new head. */
head += length;
if (head >= vs->buffer_size) {
head -= vs->buffer_size;
}
/* Check for buffer overflow. */
if (head == vs->tail) {
vs->overflow_error_count++;
pthread_mutex_unlock(&vs->lock);
continue;
}
vs->head = head;
pthread_mutex_unlock(&vs->lock);
}
return NULL;
}
uint8_t *hdhomerun_video_recv(struct hdhomerun_video_sock_t *vs, size_t max_size, size_t *pactual_size)
{
pthread_mutex_lock(&vs->lock);
size_t head = vs->head;
size_t tail = vs->tail;
if (vs->advance > 0) {
tail += vs->advance;
if (tail >= vs->buffer_size) {
tail -= vs->buffer_size;
}
vs->tail = tail;
}
if (head == tail) {
vs->advance = 0;
*pactual_size = 0;
pthread_mutex_unlock(&vs->lock);
return NULL;
}
size_t size = (max_size / VIDEO_DATA_PACKET_SIZE) * VIDEO_DATA_PACKET_SIZE;
if (size == 0) {
vs->advance = 0;
*pactual_size = 0;
pthread_mutex_unlock(&vs->lock);
return NULL;
}
size_t avail;
if (head > tail) {
avail = head - tail;
} else {
avail = vs->buffer_size - tail;
}
if (size > avail) {
size = avail;
}
vs->advance = size;
*pactual_size = size;
uint8_t *result = vs->buffer + tail;
pthread_mutex_unlock(&vs->lock);
return result;
}
void hdhomerun_video_flush(struct hdhomerun_video_sock_t *vs)
{
pthread_mutex_lock(&vs->lock);
vs->tail = vs->head;
vs->advance = 0;
vs->rtp_sequence = 0xFFFFFFFF;
int i;
for (i = 0; i < 0x2000; i++) {
vs->sequence[i] = 0xFF;
}
vs->packet_count = 0;
vs->transport_error_count = 0;
vs->network_error_count = 0;
vs->sequence_error_count = 0;
vs->overflow_error_count = 0;
pthread_mutex_unlock(&vs->lock);
}
void hdhomerun_video_debug_print_stats(struct hdhomerun_video_sock_t *vs)
{
struct hdhomerun_video_stats_t stats;
hdhomerun_video_get_stats(vs, &stats);
hdhomerun_debug_printf(vs->dbg, "video sock: pkt=%u net=%u te=%u miss=%u drop=%u\n",
(unsigned int)stats.packet_count, (unsigned int)stats.network_error_count,
(unsigned int)stats.transport_error_count, (unsigned int)stats.sequence_error_count,
(unsigned int)stats.overflow_error_count
);
}
void hdhomerun_video_get_stats(struct hdhomerun_video_sock_t *vs, struct hdhomerun_video_stats_t *stats)
{
memset(stats, 0, sizeof(struct hdhomerun_video_stats_t));
pthread_mutex_lock(&vs->lock);
stats->packet_count = vs->packet_count;
stats->network_error_count = vs->network_error_count;
stats->transport_error_count = vs->transport_error_count;
stats->sequence_error_count = vs->sequence_error_count;
stats->overflow_error_count = vs->overflow_error_count;
pthread_mutex_unlock(&vs->lock);
}

104
hdhomerun_video.h Normal file
View file

@ -0,0 +1,104 @@
/*
* hdhomerun_video.h
*
* Copyright © 2006 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
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifdef __cplusplus
extern "C" {
#endif
struct hdhomerun_video_sock_t;
struct hdhomerun_video_stats_t {
uint32_t packet_count;
uint32_t network_error_count;
uint32_t transport_error_count;
uint32_t sequence_error_count;
uint32_t overflow_error_count;
};
#define TS_PACKET_SIZE 188
#define VIDEO_DATA_PACKET_SIZE (188 * 7)
#define VIDEO_DATA_BUFFER_SIZE_1S (20000000 / 8)
#define VIDEO_RTP_DATA_PACKET_SIZE ((188 * 7) + 12)
/*
* Create a video/data socket.
*
* uint16_t listen_port: Port number to listen on. Set to 0 to auto-select.
* size_t buffer_size: Size of receive buffer. For 1 second of buffer use VIDEO_DATA_BUFFER_SIZE_1S.
* struct hdhomerun_debug_t *dbg: Pointer to debug logging object. May be NULL.
*
* Returns a pointer to the newly created control socket.
*
* When no longer needed, the socket should be destroyed by calling hdhomerun_control_destroy.
*/
extern LIBTYPE struct hdhomerun_video_sock_t *hdhomerun_video_create(uint16_t listen_port, bool_t allow_port_reuse, size_t buffer_size, struct hdhomerun_debug_t *dbg);
extern LIBTYPE void hdhomerun_video_destroy(struct hdhomerun_video_sock_t *vs);
/*
* Get the port the socket is listening on.
*
* Returns 16-bit port with native endianness, or 0 on error.
*/
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 void hdhomerun_video_leave_multicast_group(struct hdhomerun_video_sock_t *vs, uint32_t multicast_ip, uint32_t local_ip);
/*
* Read data from buffer.
*
* size_t max_size: The maximum amount of data to be returned.
* size_t *pactual_size: The caller-supplied pactual_size value will be updated to contain the amount
* of data available to the caller.
*
* Returns a pointer to the data, or NULL if no data is available.
* The data will remain valid until another call to hdhomerun_video_recv.
*
* The amount of data returned will always be a multiple of VIDEO_DATA_PACKET_SIZE (1316).
* Attempting to read a single TS frame (188 bytes) will not return data as it is less than
* the minimum size.
*
* The buffer is implemented as a ring buffer. It is possible for this function to return a small
* amount of data when more is available due to the wrap-around case.
*/
extern LIBTYPE uint8_t *hdhomerun_video_recv(struct hdhomerun_video_sock_t *vs, size_t max_size, size_t *pactual_size);
/*
* Flush the buffer.
*/
extern LIBTYPE void hdhomerun_video_flush(struct hdhomerun_video_sock_t *vs);
/*
* Debug print internal stats.
*/
extern LIBTYPE void hdhomerun_video_debug_print_stats(struct hdhomerun_video_sock_t *vs);
extern LIBTYPE void hdhomerun_video_get_stats(struct hdhomerun_video_sock_t *vs, struct hdhomerun_video_stats_t *stats);
/*
* Internal use only.
*/
extern LIBTYPE hdhomerun_sock_t hdhomerun_video_get_sock(struct hdhomerun_video_sock_t *vs);
#ifdef __cplusplus
}
#endif