mirror of
https://github.com/Silicondust/libhdhomerun
synced 2025-07-05 12:36:48 -07:00
libhdhomerun release 20150826
This commit is contained in:
parent
c28fd61832
commit
a2475b9991
31 changed files with 7828 additions and 1 deletions
59
Makefile
Normal file
59
Makefile
Normal 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
|
30
README.md
30
README.md
|
@ -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
32
hdhomerun.h
Normal 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
406
hdhomerun_channels.c
Normal 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
52
hdhomerun_channels.h
Normal 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
347
hdhomerun_channelscan.c
Normal 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
41
hdhomerun_channelscan.h
Normal 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
693
hdhomerun_config.c
Normal 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
431
hdhomerun_control.c
Normal 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
103
hdhomerun_control.h
Normal 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
449
hdhomerun_debug.c
Normal 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(¤t_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
52
hdhomerun_debug.h
Normal 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
1359
hdhomerun_device.c
Normal file
File diff suppressed because it is too large
Load diff
257
hdhomerun_device.h
Normal file
257
hdhomerun_device.h
Normal 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
447
hdhomerun_device_selector.c
Normal 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;
|
||||
}
|
86
hdhomerun_device_selector.h
Normal file
86
hdhomerun_device_selector.h
Normal 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
496
hdhomerun_discover.c
Normal 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
78
hdhomerun_discover.h
Normal 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
37
hdhomerun_os.h
Normal 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
128
hdhomerun_os_posix.c
Normal 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
63
hdhomerun_os_posix.h
Normal 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
152
hdhomerun_os_windows.c
Normal 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
95
hdhomerun_os_windows.h
Normal 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
239
hdhomerun_pkt.c
Normal 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
170
hdhomerun_pkt.h
Normal 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
59
hdhomerun_sock.h
Normal 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
451
hdhomerun_sock_posix.c
Normal 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
450
hdhomerun_sock_windows.c
Normal 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
80
hdhomerun_types.h
Normal 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
383
hdhomerun_video.c
Normal 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
104
hdhomerun_video.h
Normal 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
|
Loading…
Add table
Add a link
Reference in a new issue