package com.silicondust.libhdhomerun;
import java.io.UnsupportedEncodingException;
import java.util.List;
import java.util.Scanner;
import com.silicondust.libhdhomerun.HDHomerun_Types.hdhomerun_channelscan_result_t;
import com.silicondust.libhdhomerun.HDHomerun_Types.hdhomerun_plotsample_t;
import com.silicondust.libhdhomerun.HDHomerun_Types.hdhomerun_tuner_status_t;
import com.silicondust.libhdhomerun.HDHomerun_Types.hdhomerun_tuner_vstatus_t;
import com.silicondust.libhdhomerun.HDHomerun_Video.hdhomerun_video_stats_t;
public final class HDHomerun_Device {
public static final int HDHOMERUN_DEVICE_MAX_TUNE_TO_LOCK_TIME = 1500;
public static final int HDHOMERUN_DEVICE_MAX_LOCK_TO_DATA_TIME = 2000;
public static final int HDHOMERUN_DEVICE_MAX_TUNE_TO_DATA_TIME = (HDHOMERUN_DEVICE_MAX_TUNE_TO_LOCK_TIME + HDHOMERUN_DEVICE_MAX_LOCK_TO_DATA_TIME);
public static final String HDHOMERUN_TARGET_PROTOCOL_UDP = "udp";
public static final String HDHOMERUN_TARGET_PROTOCOL_RTP = "rtp";
private HDHomerun_Control mCS;
private HDHomerun_Video mVS;
private HDHomerun_Debug mDbg;
private HDHomerun_ChannelScan mScan;
private int mMulticast_ip;
private int mMulticast_port;
private int mDevice_id;
private int mTuner;
private int mLockkey;
private String mName;
private String mModel;
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
final HDHomerun_Device other = (HDHomerun_Device) obj;
return (mDevice_id == other.mDevice_id && other.mTuner == mTuner);
}
/*
* 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>
*/
public HDHomerun_Device(int device_id, int device_ip, int tuner, HDHomerun_Debug dbg) throws Exception
{
createDevice(device_id, device_ip, tuner, dbg);
}
private void createDevice(int device_id, int device_ip, int tuner, HDHomerun_Debug dbg) throws Exception
{
mDbg = dbg;
if ((device_id == 0) && (device_ip == 0) && (tuner == 0)) {
throw new Exception();
}
if (set_device(device_id, device_ip) <= 0) {
throw new Exception();
}
if (set_tuner(tuner) <= 0) {
throw new Exception();
}
}
private static boolean is_hex_char(char c)
{
if ((c >= '0') && (c <= '9')) {
return true;
}
if ((c >= 'A') && (c <= 'F')) {
return true;
}
if ((c >= 'a') && (c <= 'f')) {
return true;
}
return false;
}
public HDHomerun_Device(final String device_str, HDHomerun_Debug dbg) throws Exception
{
int i;
for (i = 0; i < 8; i++) {
if (!is_hex_char(device_str.charAt(i))) {
throw new Exception();
}
}
if (device_str.length() == 8) {
Scanner scn = new Scanner(device_str);
int device_id = scn.nextInt(16);
if (device_id == 0) {
throw new Exception();
}
createDevice(device_id, 0, 0, dbg);
return;
}
if (device_str.length() > 8 && device_str.charAt(8) == '-') {
Scanner scn = new Scanner(device_str);
int device_id = scn.nextInt(16);
scn.next("-");
int tuner = scn.nextInt();
if (0 == device_id || 0 == tuner) {
throw new Exception();
}
createDevice(device_id, 0, tuner, dbg);
return;
}
throw new Exception();
}
public void destroy()
{
mScan = null;
if (null != mVS) {
mVS.destroy();
mVS = null;
}
if (null != mCS) {
mCS.destroy();
mCS = null;
}
}
/*
* Get the device id, ip, or tuner of the device instance.
*/
public final String get_name()
{
return mName;
}
public int get_device_id()
{
return mDevice_id;
}
public int get_device_ip()
{
if (mMulticast_ip != 0) {
return mMulticast_ip;
}
if (null != mCS) {
return mCS.get_device_ip();
}
return 0;
}
public int get_device_id_requested()
{
if (mMulticast_ip != 0) {
return 0;
}
if (null != mCS) {
return mCS.get_device_id_requested();
}
return 0;
}
public int get_device_ip_requested()
{
if (mMulticast_ip != 0) {
return mMulticast_ip;
}
if (mCS != null) {
return mCS.get_device_ip_requested();
}
return 0;
}
public long get_tuner()
{
return mTuner;
}
public int set_device(int device_id, int device_ip)
{
if ((device_id == 0) && (device_ip == 0)) {
mDbg.printf("hdhomerun_device_set_device: device not specified\n");
return -1;
}
if (HDHomerun_Discover.hdhomerun_discover_is_ip_multicast(device_ip)) {
return set_device_multicast(device_ip);
}
return set_device_normal(device_id, device_ip);
}
private int set_device_normal(int device_id, int device_ip)
{
if (null == mCS) {
mCS = new HDHomerun_Control(0, 0, mDbg);
if (null == mCS) {
mDbg.printf("hdhomerun_device_set_device: failed to create control object\n");
return -1;
}
}
mCS.set_device(device_id, device_ip);
if ((device_id == 0) || (device_id == HDHomerun_Pkt.HDHOMERUN_DEVICE_ID_WILDCARD)) {
device_id = mCS.get_device_id();
}
mMulticast_ip = 0;
mMulticast_port = 0;
mDevice_id = device_id;
mTuner = 0;
mLockkey = 0;
mName = String.format("%08X-%d", mDevice_id, mTuner);
mModel = "";
return 1;
}
private int set_device_multicast(int multicast_ip)
{
if (null != mCS) {
mCS.destroy();
mCS = null;
}
mMulticast_ip = multicast_ip;
mMulticast_port = 0;
mDevice_id = 0;
mTuner = 0;
mLockkey = 0;
int ip = multicast_ip;
mName = String.format("%d.%d.%d.%d", (ip >> 24) & 0xFF, (ip >> 16) & 0xFF, (ip >> 8) & 0xFF, (ip >> 0) & 0xFF);
mModel = "multicast";
return 1;
}
public int set_tuner(int tuner)
{
if (mMulticast_ip != 0) {
if (mTuner != 0) {
mDbg.printf("hdhomerun_device_set_tuner: tuner cannot be specified in multicast mode\n");
return -1;
}
return 1;
}
mTuner = tuner;
mName = String.format("%08X-%d", mDevice_id, mTuner);
return 1;
}
public int set_tuner_from_str(final String tuner_str)
{
Scanner scn = new Scanner(tuner_str);
int tuner = scn.nextInt();
if (tuner != 0 || 0 == tuner_str.compareTo("0")) {
set_tuner(tuner);
return 1;
}
if (null != scn.findInLine("/tuner")) {
tuner = scn.nextInt();
if(tuner > 0){
set_tuner(tuner);
return 1;
}
}
return -1;
}
/*
* 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.
*/
public int get_local_machine_addr()
{
if (null != mCS) {
return mCS.get_local_addr();
}
return 0;
}
/*
* 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.
*/
public int get_tuner_status(StringBuilder pstatus_str, hdhomerun_tuner_status_t status)
{
if (null == mCS) {
mDbg.printf("hdhomerun_device_get_tuner_status: device not set\n");
return -1;
}
status.reset();
String name = String.format("/tuner%d/status", mTuner);
StringBuilder status_str_b = new StringBuilder();
int ret = mCS.get(name, status_str_b, null);
if (ret <= 0) {
return ret;
}
String status_str = new String(status_str_b);
if (pstatus_str != null) {
pstatus_str = status_str_b;
}
if (status != null) {
Scanner scn = new Scanner(status_str);
if (null != scn.findInLine("ch=")) {
status.channel = String.format("%31s", scn.next());
}
scn = new Scanner(status_str);
if (null != scn.findInLine("lock=")) {
status.lock_str = scn.next();
}
status.signal_strength = get_status_parse(status_str, "ss=");
status.signal_to_noise_quality = get_status_parse(status_str, "snq=");
status.symbol_error_quality = get_status_parse(status_str, "seq=");
status.raw_bits_per_second = get_status_parse(status_str, "bps=");
status.packets_per_second = get_status_parse(status_str, "pps=");
status.signal_present = (status.signal_strength >= 45);
if (status.lock_str.compareTo("none") != 0) {
if (status.lock_str.charAt(0) == '(') {
status.lock_unsupported = true;
} else {
status.lock_supported = true;
}
}
}
return 1;
}
private int get_status_parse(final String status_str, final String tag)
{
Scanner scn = new Scanner(status_str);
if (null == scn.findInLine(tag)) {
return 0;
}
return scn.nextInt();
}
public int get_tuner_vstatus(StringBuilder pvstatus_str, hdhomerun_tuner_vstatus_t vstatus)
{
if (null == mCS) {
mDbg.printf("hdhomerun_device_get_tuner_vstatus: device not set\n");
return -1;
}
vstatus.reset();
String var_name = String.format("/tuner%d/vstatus", mTuner);
StringBuilder vstatus_str_b = new StringBuilder();
int ret = mCS.get(var_name, vstatus_str_b, null);
if (ret <= 0) {
return ret;
}
String vstatus_str = new String(vstatus_str_b);
if (pvstatus_str != null) {
pvstatus_str = vstatus_str_b;
}
if (vstatus != null) {
Scanner scn = new Scanner(vstatus_str);
if (null != scn.findInLine("vch=")) {
vstatus.vchannel = scn.next();
scn = new Scanner(vstatus_str);
}
if (null != scn.findInLine("name=")) {
vstatus.name = scn.next();
scn = new Scanner(vstatus_str);
}
if (null != scn.findInLine("auth=")) {
vstatus.auth = scn.next();
scn = new Scanner(vstatus_str);
}
if (null != scn.findInLine("cci=")) {
vstatus.cci = scn.next();
scn = new Scanner(vstatus_str);
}
if (null != scn.findInLine("cgms=")) {
vstatus.cgms = scn.next();
scn = new Scanner(vstatus_str);
}
if (vstatus.auth.compareTo("not-subscribed") == 0) {
vstatus.not_subscribed = true;
}
if (vstatus.auth.compareTo("error") == 0) {
vstatus.not_available = true;
}
if (vstatus.auth.compareTo("dialog") == 0) {
vstatus.not_available = true;
}
if (vstatus.cci.compareTo("protected") == 0) {
vstatus.copy_protected = true;
}
if (vstatus.cgms.compareTo("protected") == 0) {
vstatus.copy_protected = true;
}
}
return 1;
}
public int get_tuner_streaminfo(StringBuilder pstreaminfo)
{
if (null == mCS) {
mDbg.printf("hdhomerun_device_get_tuner_streaminfo: device not set\n");
return -1;
}
String name = String.format("/tuner%d/streaminfo", mTuner);
return mCS.get(name, pstreaminfo, null);
}
public int get_tuner_channel(StringBuilder pchannel)
{
if (mCS == null) {
mDbg.printf("hdhomerun_device_get_tuner_channel: device not set\n");
return -1;
}
String name = String.format("/tuner%d/channel", mTuner);
return mCS.get(name, pchannel, null);
}
public int get_tuner_vchannel(StringBuilder pvchannel)
{
if (null == mCS) {
mDbg.printf("hdhomerun_device_get_tuner_vchannel: device not set\n");
return -1;
}
String name = String.format("/tuner%d/vchannel", mTuner);
return mCS.get(name, pvchannel, null);
}
public int get_tuner_channelmap(StringBuilder pchannelmap)
{
if (null == mCS) {
mDbg.printf("hdhomerun_device_get_tuner_channelmap: device not set\n");
return -1;
}
String name = String.format("/tuner%d/channelmap", mTuner);
return mCS.get(name, pchannelmap, null);
}
public int get_tuner_filter(StringBuilder pfilter)
{
if (null == mCS) {
mDbg.printf("hdhomerun_device_get_tuner_filter: device not set\n");
return -1;
}
String name = String.format("/tuner%d/filter", mTuner);
return mCS.get(name, pfilter, null);
}
public int get_tuner_program(StringBuilder pprogram)
{
if (null == mCS) {
mDbg.printf("hdhomerun_device_get_tuner_program: device not set\n");
return -1;
}
String name = String.format("/tuner%d/program", mTuner);
return mCS.get(name, pprogram, null);
}
public int get_tuner_target(StringBuilder ptarget)
{
if (null == mCS) {
mDbg.printf("hdhomerun_device_get_tuner_target: device not set\n");
return -1;
}
String name = String.format("/tuner%d/target", mTuner);
return mCS.get(name, ptarget, null);
}
public int get_tuner_plotsample(List<hdhomerun_plotsample_t> samples)
{
if (mCS == null) {
mDbg.printf("hdhomerun_device_get_tuner_plotsample: device not set\n");
return -1;
}
String name = String.format("/tuner%d/plotsample", mTuner);
return get_tuner_plotsample_internal(name, samples);
}
public int get_oob_plotsample(List<hdhomerun_plotsample_t> samples)
{
if (mCS == null) {
mDbg.printf("hdhomerun_device_get_oob_plotsample: device not set\n");
return -1;
}
return get_tuner_plotsample_internal("/oob/plotsample", samples);
}
private int get_tuner_plotsample_internal(final String name, List<hdhomerun_plotsample_t> samples)
{
StringBuilder result_b = new StringBuilder();
int ret = mCS.get(name, result_b, null);
if (ret <= 0) {
return ret;
}
String result = new String(result_b);
Scanner scn = new Scanner(result);
while (true) {
String token = scn.findInLine(" ");
if (null == token) {
break;
}
Scanner tokScn = new Scanner(token);
int raw = tokScn.nextInt();
if (raw == 0) {
break;
}
short real = (short) ((raw >> 12) & 0x0FFF);
if (0 < (real & 0x0800))
real |= 0xF000;
short imag = (short) ((raw >> 0) & 0x0FFF);
if (0 < (imag & 0x0800))
imag |= 0xF000;
samples.add(new HDHomerun_Types.hdhomerun_plotsample_t(real, imag));
}
return 1;
}
public int get_tuner_lockkey_owner(StringBuilder powner)
{
if (null == mCS) {
mDbg.printf("hdhomerun_device_get_tuner_lockkey_owner: device not set\n");
return -1;
}
String name = String.format("/tuner%d/lockkey", mTuner);
return mCS.get(name, powner, null);
}
public int get_ir_target(StringBuilder ptarget)
{
if (null == mCS) {
mDbg.printf("hdhomerun_device_get_ir_target: device not set\n");
return -1;
}
return mCS.get("/ir/target", ptarget, null);
}
public int get_lineup_location(StringBuilder plocation)
{
if (null == mCS) {
mDbg.printf("hdhomerun_device_get_lineup_location: device not set\n");
return -1;
}
return mCS.get("/lineup/location", plocation, null);
}
public int get_version(StringBuilder pversion_str, int[] pversion_num)
{
if (null == mCS) {
mDbg.printf("hdhomerun_device_get_version: device not set\n");
return -1;
}
StringBuilder version_str_b = new StringBuilder();
int ret = mCS.get("/sys/version", version_str_b, null);
if (ret <= 0) {
return ret;
}
String version_str = new String(version_str_b);
if (pversion_str != null) {
pversion_str = version_str_b;
}
if (null != pversion_num) {
Scanner scn = new Scanner(version_str);
pversion_num[0] = scn.nextInt();
}
return 1;
}
public int get_supported(final String prefix, StringBuilder pstr)
{
if (null == mCS) {
mDbg.printf("hdhomerun_device_set_tuner_channel: device not set\n");
return -1;
}
StringBuilder features = new StringBuilder();
int ret = mCS.get("/sys/features", features, null);
if (ret <= 0) {
return ret;
}
if (null == prefix) {
pstr = features;
return 1;
}
int index = features.indexOf(prefix);
if (index == -1)
return 0;
index += prefix.length();
pstr.append(features.substring(index));
if(pstr.charAt(pstr.length()-1) == '\n')
pstr.setCharAt(pstr.length()-1, (char) 0);
return 1;
}
private static boolean hdhomerun_device_get_tuner_status_lock_is_bcast(hdhomerun_tuner_status_t status)
{
if (status.lock_str.compareTo("8vsb") == 0) {
return true;
}
if (status.lock_str.substring(0, 2).compareTo("t8") == 0) {
return true;
}
if (status.lock_str.substring(0, 2).compareTo("t7") == 0) {
return true;
}
if (status.lock_str.substring(0, 2).compareTo("t6") == 0) {
return true;
}
return false;
}
public static int hdhomerun_device_get_tuner_status_ss_color(hdhomerun_tuner_status_t status)
{
int ss_yellow_min;
int ss_green_min;
if (!status.lock_supported) {
return HDHomerun_Types.HDHOMERUN_STATUS_COLOR_NEUTRAL;
}
if (hdhomerun_device_get_tuner_status_lock_is_bcast(status)) {
ss_yellow_min = 50; /* -30dBmV */
ss_green_min = 75; /* -15dBmV */
} else {
ss_yellow_min = 80; /* -12dBmV */
ss_green_min = 90; /* -6dBmV */
}
if (status.signal_strength >= ss_green_min) {
return HDHomerun_Types.HDHOMERUN_STATUS_COLOR_GREEN;
}
if (status.signal_strength >= ss_yellow_min) {
return HDHomerun_Types.HDHOMERUN_STATUS_COLOR_YELLOW;
}
return HDHomerun_Types.HDHOMERUN_STATUS_COLOR_RED;
}
public static int hdhomerun_device_get_tuner_status_snq_color(hdhomerun_tuner_status_t status)
{
if (status.signal_to_noise_quality >= 70) {
return HDHomerun_Types.HDHOMERUN_STATUS_COLOR_GREEN;
}
if (status.signal_to_noise_quality >= 50) {
return HDHomerun_Types.HDHOMERUN_STATUS_COLOR_YELLOW;
}
return HDHomerun_Types.HDHOMERUN_STATUS_COLOR_RED;
}
public static int hdhomerun_device_get_tuner_status_seq_color(hdhomerun_tuner_status_t status)
{
if (status.symbol_error_quality >= 100) {
return HDHomerun_Types.HDHOMERUN_STATUS_COLOR_GREEN;
}
return HDHomerun_Types.HDHOMERUN_STATUS_COLOR_RED;
}
public final String get_model_str()
{
if (null != mModel && mModel.length() > 0) {
return mModel;
}
if (null == mCS) {
mDbg.printf("hdhomerun_device_get_model_str: device not set\n");
return null;
}
StringBuilder model_str = new StringBuilder();;
int ret = mCS.get("/sys/model", model_str, null);
if (ret < 0) {
return null;
}
if (ret == 0) {
mModel = "hdhomerun_atsc";
return mModel;
}
mModel = new String(model_str);
return mModel;
}
/*
* 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.
*/
public int set_tuner_channel(final String channel)
{
if (null == mCS) {
mDbg.printf("hdhomerun_device_set_tuner_channel: device not set\n");
return -1;
}
String name = String.format("/tuner%d/channel", mTuner);
return mCS.set_with_lockkey(name, channel, mLockkey, null, null);
}
public int set_tuner_vchannel(final String vchannel)
{
if (null == mCS) {
mDbg.printf("hdhomerun_device_set_tuner_vchannel: device not set\n");
return -1;
}
String name = String.format("/tuner%d/vchannel", mTuner);
return mCS.set_with_lockkey(name, vchannel, mLockkey, null, null);
}
public int set_tuner_channelmap(final String channelmap)
{
if (null == mCS) {
mDbg.printf("hdhomerun_device_set_tuner_channelmap: device not set\n");
return -1;
}
String name = String.format("/tuner%d/channelmap", mTuner);
return mCS.set_with_lockkey(name, channelmap, mLockkey, null, null);
}
public int set_tuner_filter(final String filter)
{
if (null == mCS) {
mDbg.printf("hdhomerun_device_set_tuner_filter: device not set\n");
return -1;
}
String name = String.format("/tuner%d/filter", mTuner);
return mCS.set_with_lockkey(name, filter, mLockkey, null, null);
}
private boolean set_tuner_filter_by_array_append(byte[] pptr, int[] currPos, int endPos, int range_begin, int range_end)
{
int available = endPos - currPos[0];
int required;
byte[] append = null;
try {
if (range_begin == range_end)
append = String.format("0x%04x", range_begin).getBytes("UTF8");
else
append = String.format("0x%04x-0x%04x ", range_begin, range_end).getBytes("UTF8");
} catch (UnsupportedEncodingException e) {
return false;
}
required = append.length + 1;
if (required > available) {
return false;
}
for(int i = 0; i < required; ++i)
pptr[currPos[0]++] = append[i];
return true;
}
// expects array of 0x2000
public int set_tuner_filter_by_array(byte[] filter_array)
{
byte[] filter = new byte[1024];
int[] currPos = new int[1];
currPos[0] = 0;
final int end = 1024;
int range_begin = 0xFFFF;
int range_end = 0xFFFF;
int len = filter_array.length - 1; //0x1FFF
for (int i = 0; i <= len; i++) {
if (0 == filter_array[i]) {
if (range_begin == 0xFFFF) {
continue;
}
if (!set_tuner_filter_by_array_append(filter, currPos, end, range_begin, range_end)) {
return 0;
}
range_begin = 0xFFFF;
range_end = 0xFFFF;
continue;
}
if (range_begin == 0xFFFF) {
range_begin = i;
range_end = i;
continue;
}
range_end = i;
}
if (range_begin != 0xFFFF) {
if (!set_tuner_filter_by_array_append(filter, currPos, end, range_begin, range_end)) {
return 0;
}
}
/* Remove trailing space. */
if (filter[currPos[0]] == ' ') {
filter[currPos[0]] = 0;
}
try {
return set_tuner_filter(new String(filter, "UTF8"));
} catch (UnsupportedEncodingException e) {
return 0;
}
}
public int set_tuner_program(final String program)
{
if (null == mCS) {
mDbg.printf("hdhomerun_device_set_tuner_program: device not set\n");
return -1;
}
String name = String.format("/tuner%d/program", mTuner);
return mCS.set_with_lockkey(name, program, mLockkey, null, null);
}
public int set_tuner_target(final String target)
{
if (null == mCS) {
mDbg.printf("hdhomerun_device_set_tuner_target: device not set\n");
return -1;
}
String name = String.format("/tuner%d/target", mTuner);
return mCS.set_with_lockkey(name, target, mLockkey, null, null);
}
public int set_ir_target(final String target)
{
if (null == mCS) {
mDbg.printf("hdhomerun_device_set_ir_target: device not set\n");
return -1;
}
return mCS.set("/ir/target", target, null, null);
}
public int set_lineup_location(final String location)
{
if (null == mCS) {
mDbg.printf("hdhomerun_device_set_lineup_location: device not set\n");
return -1;
}
return mCS.set("/lineup/location", location, null, null);
}
public int set_sys_dvbc_modulation(final String modulation_list)
{
if (null == mCS) {
mDbg.printf("hdhomerun_device_set_sys_dvbc_modulation: device not set\n");
return -1;
}
return mCS.set("/sys/dvbc_modulation", modulation_list, null, null);
}
/*
* 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.
*/
public int get_var(final String name, StringBuilder pvalue, StringBuilder perror)
{
if (null == mCS) {
mDbg.printf("hdhomerun_device_get_var: device not set\n");
return -1;
}
return mCS.get(name, pvalue, perror);
}
public int set_var(final String name, final String value, StringBuilder pvalue, StringBuilder perror)
{
if (null == mCS) {
mDbg.printf("hdhomerun_device_set_var: device not set\n");
return -1;
}
return mCS.set_with_lockkey(name, value, mLockkey, pvalue, 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.
*/
public int tuner_lockkey_request(StringBuilder perror)
{
if (mMulticast_ip != 0) {
return 1;
}
if (null == mCS) {
mDbg.printf("hdhomerun_device_tuner_lockkey_request: device not set\n");
return -1;
}
int new_lockkey = Math.abs(HDHomerun_OS.random_get32());
String name = String.format("/tuner%d/lockkey", mTuner);
String new_lockkey_str = String.format("%d", new_lockkey);
int ret = mCS.set_with_lockkey(name, new_lockkey_str, mLockkey, null, perror);
if (ret <= 0) {
mLockkey = 0;
return ret;
}
mLockkey = new_lockkey;
return ret;
}
public int tuner_lockkey_release()
{
if (mMulticast_ip != 0) {
return 1;
}
if (null == mCS) {
mDbg.printf("hdhomerun_device_tuner_lockkey_release: device not set\n");
return -1;
}
if (mLockkey == 0) {
return 1;
}
String name = String.format("/tuner%d/lockkey", mTuner);
int ret = mCS.set_with_lockkey(name, "none", mLockkey, null, null);
mLockkey = 0;
return ret;
}
public int tuner_lockkey_force()
{
if (mMulticast_ip != 0) {
return 1;
}
if (null == mCS) {
mDbg.printf("hdhomerun_device_tuner_lockkey_force: device not set\n");
return -1;
}
String name = String.format("/tuner%d/lockkey", mTuner);
int ret = mCS.set(name, "force", null, null);
mLockkey = 0;
return ret;
}
/*
* Intended only for non persistent connections; eg, hdhomerun_config.
*/
public void tuner_lockkey_use_value(int lockkey)
{
if (mMulticast_ip != 0) {
return;
}
mLockkey = 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.
*/
public int wait_for_lock(hdhomerun_tuner_status_t status)
{
/* Delay for SS reading to be valid (signal present). */
HDHomerun_OS.msleep_minimum(250);
/* Wait for up to 2.5 seconds for lock. */
long timeout = HDHomerun_OS.getcurrenttime() + 2500;
while (true) {
/* Get status to check for lock. Quality numbers will not be valid yet. */
int ret = get_tuner_status(null, status);
if (ret <= 0) {
return ret;
}
if (!status.signal_present) {
return 1;
}
if (status.lock_supported || status.lock_unsupported) {
return 1;
}
if (HDHomerun_OS.getcurrenttime() >= timeout) {
return 1;
}
HDHomerun_OS.msleep_approx(250);
}
}
/*
* 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.
*/
public int stream_start()
{
get_video_sock();
if (null == mVS) {
return -1;
}
/* Set target. */
if (mMulticast_ip != 0) {
int ret = mVS.join_multicast_group(mMulticast_ip);
if (ret <= 0) {
return ret;
}
} else {
int ret = set_tuner_target_to_local(HDHOMERUN_TARGET_PROTOCOL_RTP);
if (ret == 0) {
ret = set_tuner_target_to_local(HDHOMERUN_TARGET_PROTOCOL_UDP);
}
if (ret <= 0) {
return ret;
}
}
/* Flush video buffer. */
HDHomerun_OS.msleep_minimum(64);
mVS.flush();
/* Success. */
return 1;
}
private int set_tuner_target_to_local(final String protocol)
{
if (null == mCS) {
mDbg.printf("hdhomerun_device_set_tuner_target_to_local: device not set\n");
return -1;
}
if (null == mVS) {
mDbg.printf("hdhomerun_device_set_tuner_target_to_local: video not initialized\n");
return -1;
}
/* Set target. */
int local_ip = mCS.get_local_addr();
int local_port = mVS.get_local_port();
String target = String.format("%s://%d.%d.%d.%d:%d",
protocol,
(int)(local_ip >> 24) & 0xFF, (int)(local_ip >> 16) & 0xFF,
(int)(local_ip >> 8) & 0xFF, (int)(local_ip >> 0) & 0xFF,
(int)local_port
);
return set_tuner_target(target);
}
public byte[] stream_recv(int max_size, int[] pactual_size)
{
if (null == mVS) {
mDbg.printf("hdhomerun_device_stream_recv: video not initialized\n");
return null;
}
return mVS.recv(max_size, pactual_size);
}
public void stream_flush()
{
if (null == mVS) {
mDbg.printf("hdhomerun_device_stream_flush: video not initialized\n");
return;
}
mVS.flush();
}
public void stream_stop()
{
if (null == mVS) {
mDbg.printf("hdhomerun_device_stream_stop: video not initialized\n");
return;
}
if (mMulticast_ip != 0) {
mVS.leave_multicast_group();
} else {
set_tuner_target("none");
}
}
/*
* Channel scan API.
*/
public int channelscan_init(final String channelmap)
{
try {
mScan = new HDHomerun_ChannelScan(this, channelmap);
} catch (Exception e) {
mScan = null;
}
if (null == mScan) {
mDbg.printf("hdhomerun_device_channelscan_init: failed to create scan object\n");
return -1;
}
return 1;
}
public int channelscan_advance(hdhomerun_channelscan_result_t result)
{
if (mScan == null) {
mDbg.printf("hdhomerun_device_channelscan_advance: scan not initialized\n");
return 0;
}
int ret = mScan.advance(result);
if (ret <= 0) { /* Free scan if normal finish or fatal error */
mScan = null;
}
return ret;
}
public int channelscan_detect(hdhomerun_channelscan_result_t result)
{
if (mScan == null) {
mDbg.printf("hdhomerun_device_channelscan_detect: scan not initialized\n");
return 0;
}
int ret = mScan.detect(result);
if (ret < 0) { /* Free scan if fatal error */
mScan = null;
}
return ret;
}
public byte channelscan_get_progress()
{
if (mScan == null) {
mDbg.printf("hdhomerun_device_channelscan_get_progress: scan not initialized\n");
return 0;
}
return mScan.get_progress();
}
/*
* 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.
*/
public HDHomerun_Control get_control_sock()
{
return mCS;
}
public HDHomerun_Video get_video_sock()
{
if (mVS != null) {
return mVS;
}
try {
mVS = new HDHomerun_Video(mMulticast_port, HDHomerun_Video.VIDEO_DATA_BUFFER_SIZE_1S * 2, (0 != mMulticast_port), mDbg);
} catch (Exception e) {
mVS = null;
}
if (null == mVS) {
mDbg.printf("hdhomerun_device_get_video_sock: failed to create video object\n");
return null;
}
return mVS;
}
/*
* Debug print internal stats.
*/
public void debug_print_video_stats()
{
if (!mDbg.enabled()) {
return;
}
if (null != mCS) {
String name = String.format("/tuner%d/debug", mTuner);
StringBuilder debug_str = new StringBuilder();
StringBuilder error_str = new StringBuilder();
int ret = mCS.get(name, debug_str, error_str);
if (ret < 0) {
mDbg.printf("video dev: communication error getting debug stats\n");
return;
}
if (error_str != null && error_str.length() > 0) {
mDbg.printf(String.format("video dev: %s\n", error_str));
} else {
mDbg.printf(String.format("video dev: %s\n", debug_str));
}
}
if (mVS != null) {
mVS.debug_print_stats();
}
}
public void get_video_stats(hdhomerun_video_stats_t stats)
{
if (mVS == null) {
mDbg.printf("hdhomerun_device_stream_flush: video not initialized\n");
stats.reset();
return;
}
mVS.get_stats(stats);
}
}