package com.silicondust.libhdhomerun; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.Scanner; public class HDHomerun_DeviceSelector { private List<HDHomerun_Device> mHD_list; private HDHomerun_Debug mDbg; /* * 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. */ public HDHomerun_DeviceSelector(HDHomerun_Debug dbg) { mHD_list = new ArrayList<HDHomerun_Device>(); mDbg = dbg; } public void destroy(boolean destroy_devices) { if (destroy_devices) { Iterator<HDHomerun_Device> iter = mHD_list.iterator(); while(iter.hasNext()) { HDHomerun_Device dev = iter.next(); dev.destroy(); } } mHD_list.clear(); } /* * Get the number of devices in the list. */ public int get_device_count() { return mHD_list.size(); } /* * Populate device selector with devices from given source. * Returns the number of devices populated. */ int load_from_file(final String filename) { FileInputStream f; try { f = new FileInputStream(filename); } catch (FileNotFoundException e) { return 0; } while(true) { byte[] device_name = new byte[32]; try { if(0 >= f.read(device_name)) break; } catch (IOException e) { break; } HDHomerun_Device hd = null; try { hd = new HDHomerun_Device(new String(device_name), mDbg); } catch (Exception e) { continue; } if (null == hd) { continue; } add_device(hd); } try { f.close(); } catch (IOException e) { } return mHD_list.size(); } /* * Add/remove a device from the selector list. */ public void add_device(HDHomerun_Device hd) { Iterator<HDHomerun_Device> iter = mHD_list.iterator(); while(iter.hasNext()) { HDHomerun_Device dev = iter.next(); if (dev.equals(hd)) { return; } } mHD_list.add(hd); } void remove_device(HDHomerun_Device hd) { mHD_list.remove(hd); } /* * Find a device in the selector list. */ HDHomerun_Device find_device(int device_id, int tuner_index) { Iterator<HDHomerun_Device> iter = mHD_list.iterator(); while(iter.hasNext()) { HDHomerun_Device dev = iter.next(); if (dev.get_device_id() != device_id) { continue; } if (dev.get_tuner() != tuner_index) { continue; } return dev; } return null; } /* * 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. */ public HDHomerun_Device choose_and_lock(HDHomerun_Device prefered) { /* Test prefered device first. */ if (prefered != null) { if (choose_test(prefered)) { return prefered; } } /* Test other tuners. */ Iterator<HDHomerun_Device> iter = mHD_list.iterator(); while(iter.hasNext()) { HDHomerun_Device dev = iter.next(); if (dev.equals(prefered)) { continue; } if (choose_test(dev)) { return dev; } } mDbg.printf("hdhomerun_device_selector_choose_and_lock: no devices available\n"); return null; } private boolean choose_test(HDHomerun_Device test_hd) { String name = test_hd.get_name(); /* * Attempt to aquire lock. */ StringBuilder error = new StringBuilder(); int ret = test_hd.tuner_lockkey_request(error); if (ret > 0) { mDbg.printf(String.format("hdhomerun_device_selector_choose_test: device %s chosen\n", name)); return true; } if (ret < 0) { mDbg.printf(String.format("hdhomerun_device_selector_choose_test: device %s communication error\n", name)); return false; } /* * In use - check target. */ StringBuilder target_b = new StringBuilder(); ret = test_hd.get_tuner_target(target_b); if (ret < 0) { mDbg.printf(String.format("hdhomerun_device_selector_choose_test: device %s communication error\n", name)); return false; } if (ret == 0) { mDbg.printf(String.format("hdhomerun_device_selector_choose_test: device %s in use, failed to read target\n", name)); return false; } String target = new String(target_b); Scanner scn = new Scanner(target); scn.findInLine("//"); int[] a = new int[4]; int target_port = 0; a[0] = scn.nextInt(); if(a[0] == 0) { mDbg.printf(String.format("hdhomerun_device_selector_choose_test: device %s in use, no target set (%s)\n", name, target)); return false; } scn.next("."); a[1] = scn.nextInt(); if(a[1] == 0) { mDbg.printf(String.format("hdhomerun_device_selector_choose_test: device %s in use, no target set (%s)\n", name, target)); return false; } scn.next("."); a[2] = scn.nextInt(); if(a[2] == 0) { mDbg.printf(String.format("hdhomerun_device_selector_choose_test: device %s in use, no target set (%s)\n", name, target)); return false; } scn.next("."); a[3] = scn.nextInt(); if(a[3] == 0) { mDbg.printf(String.format("hdhomerun_device_selector_choose_test: device %s in use, no target set (%s)\n", name, target)); return false; } scn.next(":"); target_port = scn.nextInt(); if (0 == target_port) { mDbg.printf(String.format("hdhomerun_device_selector_choose_test: device %s in use, no target set (%s)\n", name, target)); return false; } int target_ip = ((a[0] << 24) | (a[1] << 16) | (a[2] << 8) | (a[3] << 0)); int local_ip = test_hd.get_local_machine_addr(); if (target_ip != local_ip) { mDbg.printf(String.format("hdhomerun_device_selector_choose_test: device %s in use by %s\n", name, target)); return false; } /* * Test local port. */ boolean inuse = false; HDHomerun_Sock test_sock = null; try { test_sock = new HDHomerun_Sock(0, target_port, 0, 0, false); } catch (Exception e) { inuse = true; } if (test_sock == null) { mDbg.printf(String.format("hdhomerun_device_selector_choose_test: device %s in use, failed to create test sock\n", name)); return false; } test_sock.destroy(); if (inuse) { mDbg.printf(String.format("hdhomerun_device_selector_choose_test: device %s in use by local machine\n", name)); return false; } /* * Dead local target, force clear lock. */ ret = test_hd.tuner_lockkey_force(); if (ret < 0) { mDbg.printf(String.format("hdhomerun_device_selector_choose_test: device %s communication error\n", name)); return false; } if (ret == 0) { mDbg.printf(String.format("hdhomerun_device_selector_choose_test: device %s in use by local machine, dead target, failed to force release lockkey\n", name)); return false; } mDbg.printf(String.format("hdhomerun_device_selector_choose_test: device %s in use by local machine, dead target, lockkey force successful\n", name)); /* * Attempt to aquire lock. */ ret = test_hd.tuner_lockkey_request(error); if (ret > 0) { mDbg.printf(String.format("hdhomerun_device_selector_choose_test: device %s chosen\n", name)); return true; } if (ret < 0) { mDbg.printf(String.format("hdhomerun_device_selector_choose_test: device %s communication error\n", name)); return false; } mDbg.printf(String.format("hdhomerun_device_selector_choose_test: device %s still in use after lockkey force (%s)\n", name, error)); return false; } }