#!/usr/bin/perl -w
# Copyright 2008 Robert ("Bob") Igo of StormLogic, LLC and mythic.tv.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program 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 General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
use Switch;
use Tweaker::Script;
package Tweaker::Script;
set_known_options( 'all' ); # When Tweaker handles multiple instances of a script, this will be expanded to
# handle options for a given card, based on what it supports, e.g. ATSC, QAM, NTSC-coax, NTSC-Svideo, etc.
# The current version of this script will run once and set up defaults for all detected cards, choosing
# a default configuration based on some assumptions of common usage. Modes in this array should be listed
# from least desirable to most desirable. E.g. ["NTSC", "ATSC", "QAM"]
my @capture_card_patterns = (
# DIGITAL
# -------
# ATSC-only devices
[ "pcHDTV hd-2000", [".*0400.*109e.*036e.*r11.*7063.*2000.*"],
["DVB", ["ATSC"]]],
# [ "BBTI Air2PC v2", [".*0280.*13d0.*2103.*r02.*13d0.*2103.*"], # ??? dupe 1; mihanson
# ["DVB", ["ATSC"]]],
# DVB-T devices
[ "DViCO FusionHDTV DVB-T Lite", [".*0400.*109e.*036e.*r11.*18ac.*db10.*"],
# secondary device "0480" "109e" "0878" -r11 "18ac" "d500"
["DVB", ["DVB-T"]]],
[ "DViCO FusionHDTV DVB-T1", [".*0400.*14f1.*8800.*18ac.*db00.*"],
["DVB", ["DVB-T"]]], # also has composite and S-Video in for frame grabbing
[ "DViCO FusionHDTV DVB-T Plus", [".*0400.*14f1.*8800.*8800.*db10.*"],
["DVB", ["DVB-T"]]],
[ "DViCO FusionHDTV dual Express", [".*0400.*14f1.*8852.*18ac.*db78.*"],
["DVB", ["DVB-T"]]],
[ "Twinhan VisionPlus DVB-T", [".*0400.*109e.*036e.*r11.*1822.*0001.*"], # ??? dupe 2; jbman, Girkers
["DVB", ["DVB-T"]]],
# [ "EU Hauppauge PVR-500 (DVB-T)", [".*0400.*4444.*0016.*0070.*e807.*"],
# ["DVB", ["DVB-T"]]],
[ "Avermedia A777", [".*0480.*1131.*7133.*1461.*2c05.*", ".*0480.*1131.*7134.*1461.*2c05.*"],
["DVB", ["DVB-T"]]],
[ "Compro T300", [".*0480.*1131.*7134.*185b.*c900.*", ".*0480.*1131.*7134.*7134.*c900.*"],
["DVB", ["DVB-T"]]],
# DVB-C devices
[ "Siemens DVB-C", [".*0480.*1131.*7146.*110a.*0000.*"],
["DVB", ["DVB-C"]]],
# DVB-S devices
[ "VisionPlus 1020A", [".*0480.*109e.*0878.*1822.*0001.*"],
["DVB", ["DVB-S"]]],
# [ "Technisat Skystar2", [".*0280.*13d0.*2103.*r01.*13d0.*2103.*"], # ??? dupe 1; nbdwt73, neutron68
# ["DVB", ["DVB-S"]]],
[ "Twinhan 102g", [".*0400.*109e.*036e.*r11.*1822.*0001.*"], # ??? dupe 2; neutron68
# secondary device "0480" "109e" "0878" -r11 "1822" "0001"
["DVB", ["DVB-S"]]],
# ATSC/QAM devices
[ "Kworld ATSC-110", [".*0480.*1131.*7133.*17de.*"],
["DVB", ["ATSC", "QAM"]]],
[ "pcHDTV hd-3000", [".*0400.*14f1.*8800.*3000.*"],
["DVB", ["ATSC", "QAM"]]], # the hd-3000 has several sub-devices; this pattern matches the digital tuner
[ "BBTI HD5000AV / AirStar 2 TV", [".*0280.*13d0.*2103.*r02.*13d0.*2103.*"], # ??? dupe 1; manicmike
["DVB", ["ATSC", "QAM", "DVB-T"]]],
[ "pcHDTV hd-5500", [".*0400.*14f1.*8800.*7063.*5500.*"],
["DVB", ["ATSC", "QAM"]]],
[ "Hauppauge WinTV-HVR-1800", [".*0400.*14f1.*8880.*0070.*7801.*"],
["DVB", ["ATSC", "QAM"]]],
[ "DViCO Fusion Express", [".*0400.*14f1.*8852.*r02.*18ac.*d500.*"],
["DVB", ["ATSC", "QAM"]]],
# card has one sub-device; this pattern matches the primary device
[ "DViCO Fusion HDTV5 Lite", [".*0400.*109e.*036e.*r11.*18ac.*d500.*"],
# secondary device "0480" "109e" "0878" -r11 "18ac" "d500"
["DVB", ["ATSC", "QAM"]]],
# ANALOG
# -------
# NTSC cards
# MPEG-2 type
[ "Hauppauge PVR-150", [".*0400.*4444.*0016.*0070.*8003.*", ".*0400.*4444.*0016.*0070.*8801.*"],
["MPEG", ["NTSC"]]],
[ "Hauppauge PVR-250", [".*0200.*10ec.*8139.*1799.*5000.*", ".*0400.*4444.*0016.*0070.*4009.*", ".*0400.*4444.*0016.*0070.*f7f9.*"],
["MPEG", ["NTSC"]]],
[ "Hauppauge PVR-350", [".*0400.*4444.*0803.*0070.*4000.*"],
["MPEG", ["NTSC"]]],
# NTSC/PAL cards
# MPEG-2 type
[ "Hauppauge PVR-500", [".*0400.*4444.*0016.*0070.*e817.*", ".*0400.*4444.*0016.*0070.*e807.*"], # look for each of two devices on the card, since it's dual-tuner
["MPEG", ["NTSC"]]],
# Frame grabbers
# [ "bt878-based frame grabbers", [".*0400.*109e.*036e.*", ".*0480.*109e.*0878.*"],
# ["V4L", ["NTSC"]]],
# USB patterns
[ "Dvico Dual Digital 4 rev2", [".* 0fe9:db98 .*"],
["DVB", ["DVB-T"]]],
);
# "Hauppauge Nova-T 500 Dual DVB-T"
#T: Bus=08 Lev=01 Prnt=01 Port=00 Cnt=01 Dev#= 2 Spd=480 MxCh= 0
#D: Ver= 2.00 Cls=00(>ifc ) Sub=00 Prot=00 MxPS=64 #Cfgs= 1
#P: Vendor=2040 ProdID=9941 Rev= 1.00
#S: Manufacturer=Hauppauge
#S: Product=WinTV Nova-DT
#S: SerialNumber=4027351140
#C:* #Ifs= 1 Cfg#= 1 Atr=a0 MxPwr=500mA
#I:* If#= 0 Alt= 0 #EPs= 4 Cls=ff(vend.) Sub=00 Prot=00 Driver=dvb_usb_dib0700
#E: Ad=01(O) Atr=02(Bulk) MxPS= 512 Ivl=125us
#E: Ad=81(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms
#E: Ad=82(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms
#E: Ad=83(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms
# "Hauppauge Nova-T-500"
#T: Bus=08 Lev=01 Prnt=01 Port=00 Cnt=01 Dev#= 2 Spd=480 MxCh= 0
#D: Ver= 2.00 Cls=00(>ifc ) Sub=00 Prot=00 MxPS=64 #Cfgs= 1
#P: Vendor=2040 ProdID=9950 Rev= 1.00
#S: Manufacturer=Hauppauge
#S: Product=WinTV Nova-DT
#S: SerialNumber=4027353863
#C:* #Ifs= 1 Cfg#= 1 Atr=80 MxPwr=500mA
#I: If#= 0 Alt= 0 #EPs= 4 Cls=ff(vend.) Sub=00 Prot=00 Driver=dvb_usb_dib0700
#E: Ad=01(O) Atr=02(Bulk) MxPS= 512 Ivl=125us
#E: Ad=81(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms
#E: Ad=82(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms
#E: Ad=83(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms
# Try to implement the given option.
sub implement_option {
my($option) = @_;
$dbconnectionstring = get_mythtv_connection_string();
if (connect_to_db("DBI:mysql:$dbconnectionstring")) {
my $logger = get_logger('tweaker.script');
# Get a list of all PCI and USB devices for later processing
my @device_list = split('\n', execute_shell_command("lspci -mn ; lsusb"));
# Iterate through the list of known tuner cards, then the list of known network tuners.
# For each known tuner, configure a safe default for it if it's found. Set up
# well-named video sources for any mode supported by any tuner, and pick a default.
# Determine the recording priority for a given device using the following priority rules,
# from top to bottom, the top being the most significant, and the bottom being the least
# significant
# Input types in order of decreasing priority:
# Digital cable, digital satellite, digital OTA, analog cable, analog line-in, analog OTA
# Device types in order of decreasing priority:
# local device, remote device
# Capture types in order of decreasing priority:
# MPEG-2 creators (or passthrough of MPEG-2), MPEG-4 creators, frame grabbers
# For example, if device1 supports a better input type than device2, device1 is always preferred.
# Ties are broken by the next level down. For example, if device1 and device2 both support the
# same input type, the tie is broken based on whether it's a local or remote device. Finally,
# ties of input type and device type are broken by the output of the device. It should be
# noted that it's possible to tie in all categories, and MythTV will break ties based on the
# order in which the devices are defined.
# For the following subroutines, $relative_device_count represents which device this is in
# the context of the total devices of its type. $global_device_count represents which device
# this is amongst all capture devices. Each returns a list of row names and values
# for the capturecard table, appropriate for the relevant type of device.
# Create an entry in the capturecard table for the given tuner.
sub make_capturecard_SQL {
my($global_device_count, $relative_device_count, $cardtype, $tuner_number)=@_;
# $tuner_number is only used for HDHomeRuns
my $defaultinput="";
my $hostname = execute_shell_command("hostname") || "localhost";
my $checkfields = [["cardid", "$global_device_count"],
["hostname", "$hostname"],
["cardtype", "$cardtype"]];
my $setfields;
my $logger = get_logger('tweaker.script');
# Because other devices use the /dev/video* and /dev/vbi* device
# files, the "device count" is used to determine which number follows the base
# device file name, e.g. /dev/video1, /dev/vbi1
sub make_V4L_capturecard_SQL {
my($relative_device_count)=@_; # 0-indexed
return [["videodevice", "/dev/video$relative_device_count"],
["audiodevice", "/dev/dsp"],
["vbidevice", "/dev/vbi$relative_device_count"],
["audioratelimit", "0"]];
}
# Because other devices use the /dev/video* and /dev/vbi* device
# files, the "device count" is used to determine which number follows the base
# device file name, e.g. /dev/video1, /dev/vbi1
sub make_MPEG_capturecard_SQL {
my($relative_device_count, $defaultinput)=@_; # 0-indexed
return [["videodevice", "/dev/video$relative_device_count"],
["defaultinput", $defaultinput]];
}
sub make_DVB_capturecard_SQL {
my($relative_device_count, # 0-indexed
$defaultinput)=@_;
my $logger = get_logger('tweaker.script');
$logger->debug("DEFAULTINPUT: $defaultinput");
return [["videodevice", "$relative_device_count"], # Rather than being a device file, it's a 0-indexed value indicating
# which of the N available DVB devices this is. Since a card can have more than one DVB device on it, there may
# be more DVB "videodevice"s on a system than there are distinct "cardid"s.
["defaultinput", $defaultinput],
["dvb_on_demand", "1"]];
}
# special case of DVB device
sub make_HDHOMERUN_capturecard_SQL {
my($hdhr_hex_id, $tuner_number, $defaultinput)=@_;
return [["videodevice", "$hdhr_hex_id"],
["defaultinput", $defaultinput],
["dvb_on_demand", "0"],
["dbox2_port", $tuner_number] # 0 or 1
];
}
$logger->debug("CARD TYPE: $cardtype");
switch($cardtype) {
case "V4L" {
$setfields = make_V4L_capturecard_SQL($relative_device_count);
}
case "DVB" {
$defaultinput = "DVBInput";
$setfields = make_DVB_capturecard_SQL($relative_device_count, $defaultinput);
}
case "HDHOMERUN" {
$defaultinput = "MPEG2TS";
$setfields = make_HDHOMERUN_capturecard_SQL($relative_device_count, $tuner_number, $defaultinput);
}
case "MPEG" {
$defaultinput = "Tuner 1";
$setfields = make_MPEG_capturecard_SQL($relative_device_count, $defaultinput);
}
}
change_or_make_entry("capturecard", $setfields, $checkfields);
return $defaultinput;
}
# Create an entry in videosource corresponding to the sub-type of input this device takes
# Return the sourceid for the entry we just made, which is a rough metric of desirability.
sub verify_or_make_videosource_SQL {
# sub-types are NTSC, PAL, ATSC, QAM, DVB-S, DVB-T, DVB-C, etc.
my($sub_type)=@_;
my $logger = get_logger('tweaker.script');
$logger->debug("\t\t\tUpdating or adding videosource for sub-type: $sub_type");
switch($sub_type) {
# North American options, from least desirable to most desirable (this is slightly arbitrary)
case "NTSC" {
change_or_make_entry("videosource", [["name", "analog_broadcast"], ["freqtable", "try-all"]], [["sourceid", 1]]);
# This is one of two cases where a sub_type can be used in two ways. Both videosources are made, but the preferred one is
# cable, the most common option.
change_or_make_entry("videosource", [["name", "analog_cable"], ["freqtable", "try-all"]], [["sourceid", 5]]);
return 5;
}
case "ATSC" {
change_or_make_entry("videosource", [["name", "digital_broadcast"], ["freqtable", "try-all"]], [["sourceid", 10]]);
return 10;
}
case "QAM" {
change_or_make_entry("videosource", [["name", "digital_cable"], ["freqtable", "try-all"]], [["sourceid", 20]]);
return 20;
}
# Options for the rest of the planet
case "PAL" {
change_or_make_entry("videosource", [["name", "analog_broadcast"], ["freqtable", "try-all"]], [["sourceid", 1]]);
# This is one of two cases where a sub_type can be used in two ways. Both videosources are made, but the preferred one is
# cable, the most common option.
change_or_make_entry("videosource", [["name", "analog_cable"], ["freqtable", "try-all"]], [["sourceid", 5]]);
return 5;
}
case "DVB-T" {
change_or_make_entry("videosource", [["name", "digital_broadcast"], ["freqtable", "try-all"]], [["sourceid", 10]]);
return 10;
}
case "DVB-C" {
change_or_make_entry("videosource", [["name", "digital_cable"], ["freqtable", "try-all"]], [["sourceid", 20]]);
return 20;
}
# Planet-wide options
case "DVB-S" {
change_or_make_entry("videosource", [["name", "digital_satellite"], ["freqtable", "try-all"]], [["sourceid", 15]]);
return 15;
}
}
}
sub make_cardinput_SQL {
my($tuner_card_number, $sourceid, $inputname, $priority_modifier)=@_;
change_or_make_entry("cardinput", [["sourceid", $sourceid], ["cardid", $tuner_card_number],
["inputname", $inputname], ["preference", $sourceid], ["tunechan", ""],
["startchan", "Please add"], ["freetoaironly", "1"],
["recpriority", $sourceid+$priority_modifier]],
[["cardinputid", $tuner_card_number]]);
}
sub max {
my($a, $b)=@_;
if ($a > $b) {
return $a;
} else {
return $b;
}
}
my $global_device_count=0;
# Configure each supported tuner/capture device detected on the system.
# built-in, PCI, PCI Express, or USB
$logger->debug("Processing built-in, PCI, PCI Express or USB devices...");
for my $a_device (@device_list) {
$logger->debug("DEVICE: $a_device");
my $match=0;
for my $device_data (@capture_card_patterns) {
$logger->debug("\tIs there a @$device_data[0] at this location?");
for my $patterns (@$device_data[1]) {
for my $pattern (@$patterns) {
if ($match) {
next;
} else {
$logger->debug("\t\tPATTERN: $pattern");
$match += ($a_device =~ m/$pattern/i);
if ($match) {
$global_device_count++; # 1-indexed
$logger->debug("\t\tfound one!");
# Each device has a device type (e.g. MPEG, DVB, V4L) that it supports, and a
# list of sub_types (e.g. PAL, NTSC, DVB-S, DVB-T, DVB-C, ATSC, QAM).
# The device type is used to configure the capturecard table and the cardinput table.
# The sub_types are used to populate the videosource table.
# The device type and "best" sub_type are used to set the 'sourceid' field of the cardinput table.
for my $typeblock (@$device_data[2]) {
my $device_type = @$typeblock[0];
$logger->debug("\t\tDEVICE TYPE: $device_type"); # e.g. "DVB", "MPEG", etc.
$cardtypes{$device_type}++;
my $sourceid = -1;
for my $sub_types (@$typeblock[1]) {
for my $sub_type (@$sub_types) {
$logger->debug("\t\tSUB-TYPE: $sub_type\n"); # e.g. "DVB-S", "QAM", etc.
# ensure that the videosource table has an entry for each sub_type
# that this device supports; use the "best" one for the device by
# default
#
# POPULATE videosource table
#
$sourceid = max(verify_or_make_videosource_SQL($sub_type), $sourceid);
}
}
$logger->debug("\t\t\"BEST\" SOURCE ID: $sourceid\n");
#
# POPULATE capturecard table
#
my $defaultinput = make_capturecard_SQL($global_device_count, $cardtypes{$device_type}-1, $device_type, -1);
#
# POPULATE cardinput table
#
make_cardinput_SQL($global_device_count, $sourceid, $defaultinput, 0);
}
}
}
}
}
}
}
# network tuner devices
$logger->debug("Processing network devices...");
# get our IP address
my $ip_address;
open(SHELL, "ifconfig | grep \"inet addr\" | grep -v 127 |");
while() {
my @line = split(/:| +/);
$ip_address = $line[3];
}
close(SHELL);
# ??? eventually, just add LocalIPCheck.pl's code to Tweaker/Script.pm and avoid bizarre perl-to-shell-to-perl calls at this level
my $ip_class = execute_shell_command("echo $ip_address | LocalIPCheck.pl; echo \$?");
if ($ip_class > 0) { # we're on a LAN, not the internet
# Look for HDHomeRun, a DVB ATSC/QAM device
$sourceid=0;
$logger->debug("\tLooking for SiliconDust HDHomeRun...");
open(SHELL2, "hdhomerun_config discover |");
while() {
if (m/\d+\.\d+\.\d+\.\d+/) {
my @line = split(/ /);
my $hdhr_hex_id = $line[2];
$logger->debug("\tfound $hdhr_hex_id");
$global_device_count++; # 1-indexed
$cardtypes{"DVB"}++;
#
# POPULATE videosource table
#
for my $sub_type ("ATSC", "QAM") {
$sourceid = max(verify_or_make_videosource_SQL($sub_type), $sourceid);
}
$logger->debug("\t\t\"BEST\" SOURCE ID: $sourceid\n");
#
# POPULATE capturecard table
#
# there are two tuners per HDHomeRun
for (my $sub_tuner_count = 0; $sub_tuner_count < 2; $sub_tuner_count++) {
my $defaultinput = make_capturecard_SQL($global_device_count+$sub_tuner_count, $hdhr_hex_id, "HDHOMERUN", $sub_tuner_count);
#
# POPULATE cardinput table
#
make_cardinput_SQL($global_device_count+$sub_tuner_count, $sourceid, $defaultinput, -4); # the -4 is to make it less desirable
# than a local device
}
$global_device_count++;
}
}
close(SHELL2);
} # else: don't scan the internet!
# Filesystem Tweaks for tuners
# This only works with the Nova-T-500 card, but it doesn't hurt any other cards
execute_shell_command("echo \"#switch on onboard amplifier on Nova-T-500 card\" > /etc/modprobe.d/dvb-usb-dib0700");
execute_shell_command("echo \"options dvb-usb-dib0700 force_lna_activation=1\" >> /etc/modprobe.d/dvb-usb-dib0700");
} else {
my $logger = get_logger('tweaker.script');
$logger->error("Unable to connect to mythconverg database");
$logger->error("Unable to implement option $option.");
return -1;
}
}
# Try to get a Recommendation Level for $option.
sub poll_options {
my($option) = @_;
recommendation_level("recommended", "Everyone should scan for tuners to autoconfigure.");
}
# Unimplemented in 0.7
sub check_option {
help;
}
# Unimplemented in 0.7
sub count_iterations {
help;
}
process_parameters;