MythTV  0.26-pre
hdhomerun_channelscan.c
Go to the documentation of this file.
00001 /*
00002  * hdhomerun_channelscan.c
00003  *
00004  * Copyright © 2007-2010 Silicondust USA Inc. <www.silicondust.com>.
00005  *
00006  * This library is free software; you can redistribute it and/or 
00007  * modify it under the terms of the GNU Lesser General Public
00008  * License as published by the Free Software Foundation; either
00009  * version 3 of the License, or (at your option) any later version.
00010  *
00011  * This library is distributed in the hope that it will be useful,
00012  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00014  * Lesser General Public License for more details.
00015  *
00016  * You should have received a copy of the GNU Lesser General Public
00017  * License along with this library.  If not, see <http://www.gnu.org/licenses/>.
00018  * 
00019  * As a special exception to the GNU Lesser General Public License,
00020  * you may link, statically or dynamically, an application with a
00021  * publicly distributed version of the Library to produce an
00022  * executable file containing portions of the Library, and
00023  * distribute that executable file under terms of your choice,
00024  * without any of the additional requirements listed in clause 4 of
00025  * the GNU Lesser General Public License.
00026  * 
00027  * By "a publicly distributed version of the Library", we mean
00028  * either the unmodified Library as distributed by Silicondust, or a
00029  * modified version of the Library that is distributed under the
00030  * conditions defined in the GNU Lesser General Public License.
00031  */
00032 
00033 #include "hdhomerun.h"
00034 
00035 struct hdhomerun_channelscan_t {
00036         struct hdhomerun_device_t *hd;
00037         uint32_t scanned_channels;
00038         struct hdhomerun_channel_list_t *channel_list;  
00039         struct hdhomerun_channel_entry_t *next_channel;
00040 };
00041 
00042 struct hdhomerun_channelscan_t *channelscan_create(struct hdhomerun_device_t *hd, const char *channelmap)
00043 {
00044         struct hdhomerun_channelscan_t *scan = (struct hdhomerun_channelscan_t *)calloc(1, sizeof(struct hdhomerun_channelscan_t));
00045         if (!scan) {
00046                 return NULL;
00047         }
00048 
00049         scan->hd = hd;
00050 
00051         scan->channel_list = hdhomerun_channel_list_create(channelmap);
00052         if (!scan->channel_list) {
00053                 free(scan);
00054                 return NULL;
00055         }
00056 
00057         scan->next_channel = hdhomerun_channel_list_last(scan->channel_list);
00058         return scan;
00059 }
00060 
00061 void channelscan_destroy(struct hdhomerun_channelscan_t *scan)
00062 {
00063         free(scan);
00064 }
00065 
00066 static int channelscan_find_lock(struct hdhomerun_channelscan_t *scan, uint32_t frequency, struct hdhomerun_channelscan_result_t *result)
00067 {
00068         /* Set channel. */
00069         char channel_str[64];
00070         sprintf(channel_str, "auto:%ld", (unsigned long)frequency);
00071 
00072         int ret = hdhomerun_device_set_tuner_channel(scan->hd, channel_str);
00073         if (ret <= 0) {
00074                 return ret;
00075         }
00076 
00077         /* Wait for lock. */
00078         ret = hdhomerun_device_wait_for_lock(scan->hd, &result->status);
00079         if (ret <= 0) {
00080                 return ret;
00081         }
00082         if (!result->status.lock_supported) {
00083                 return 1;
00084         }
00085 
00086         /* Wait for symbol quality = 100%. */
00087         uint64_t timeout = getcurrenttime() + 5000;
00088         while (1) {
00089                 ret = hdhomerun_device_get_tuner_status(scan->hd, NULL, &result->status);
00090                 if (ret <= 0) {
00091                         return ret;
00092                 }
00093 
00094                 if (result->status.symbol_error_quality == 100) {
00095                         return 1;
00096                 }
00097 
00098                 if (getcurrenttime() >= timeout) {
00099                         return 1;
00100                 }
00101 
00102                 msleep_approx(250);
00103         }
00104 }
00105 
00106 static void channelscan_extract_name(struct hdhomerun_channelscan_program_t *program, const char *line)
00107 {
00108         /* Find start of name. */
00109         const char *start = strchr(line, ' ');
00110         if (!start) {
00111                 return;
00112         }
00113         start++;
00114 
00115         start = strchr(start, ' ');
00116         if (!start) {
00117                 return;
00118         }
00119         start++;
00120 
00121         /* Find end of name. */
00122         const char *end = strstr(start, " (");
00123         if (!end) {
00124                 end = strchr(line, 0);
00125         }
00126 
00127         if (end <= start) {
00128                 return;
00129         }
00130 
00131         /* Extract name. */
00132         size_t length = (size_t)(end - start);
00133         if (length > sizeof(program->name) - 1) {
00134                 length = sizeof(program->name) - 1;
00135         }
00136 
00137         strncpy(program->name, start, length);
00138         program->name[length] = 0;
00139 }
00140 
00141 static int channelscan_detect_programs(struct hdhomerun_channelscan_t *scan, struct hdhomerun_channelscan_result_t *result, bool_t *pchanged, bool_t *pincomplete)
00142 {
00143         *pchanged = FALSE;
00144         *pincomplete = FALSE;
00145 
00146         char *streaminfo;
00147         int ret = hdhomerun_device_get_tuner_streaminfo(scan->hd, &streaminfo);
00148         if (ret <= 0) {
00149                 return ret;
00150         }
00151 
00152         char *next_line = streaminfo;
00153         int program_count = 0;
00154 
00155         while (1) {
00156                 char *line = next_line;
00157 
00158                 next_line = strchr(line, '\n');
00159                 if (!next_line) {
00160                         break;
00161                 }
00162                 *next_line++ = 0;
00163 
00164                 unsigned int transport_stream_id;
00165                 if (sscanf(line, "tsid=0x%x", &transport_stream_id) == 1) {
00166                         result->transport_stream_id = transport_stream_id;
00167                         result->transport_stream_id_detected = TRUE;
00168                         continue;
00169                 }
00170 
00171                 if (program_count >= HDHOMERUN_CHANNELSCAN_MAX_PROGRAM_COUNT) {
00172                         continue;
00173                 }
00174 
00175                 struct hdhomerun_channelscan_program_t program;
00176                 memset(&program, 0, sizeof(program));
00177 
00178                 strncpy(program.program_str, line, sizeof(program.program_str));
00179                 program.program_str[sizeof(program.program_str) - 1] = 0;
00180 
00181                 unsigned int program_number;
00182                 unsigned int virtual_major, virtual_minor;
00183                 if (sscanf(line, "%u: %u.%u", &program_number, &virtual_major, &virtual_minor) != 3) {
00184                         if (sscanf(line, "%u: %u", &program_number, &virtual_major) != 2) {
00185                                 continue;
00186                         }
00187                         virtual_minor = 0;
00188                 }
00189 
00190                 program.program_number = program_number;
00191                 program.virtual_major = virtual_major;
00192                 program.virtual_minor = virtual_minor;
00193 
00194                 channelscan_extract_name(&program, line);
00195 
00196                 if (strstr(line, "(control)")) {
00197                         program.type = HDHOMERUN_CHANNELSCAN_PROGRAM_CONTROL;
00198                 } else if (strstr(line, "(encrypted)")) {
00199                         program.type = HDHOMERUN_CHANNELSCAN_PROGRAM_ENCRYPTED;
00200                 } else if (strstr(line, "(no data)")) {
00201                         program.type = HDHOMERUN_CHANNELSCAN_PROGRAM_NODATA;
00202                         *pincomplete = TRUE;
00203                 } else {
00204                         program.type = HDHOMERUN_CHANNELSCAN_PROGRAM_NORMAL;
00205                         if ((program.virtual_major == 0) || (program.name[0] == 0)) {
00206                                 *pincomplete = TRUE;
00207                         }
00208                 }
00209 
00210                 if (memcmp(&result->programs[program_count], &program, sizeof(program)) != 0) {
00211                         memcpy(&result->programs[program_count], &program, sizeof(program));
00212                         *pchanged = TRUE;
00213                 }
00214 
00215                 program_count++;
00216         }
00217 
00218         if (program_count == 0) {
00219                 *pincomplete = TRUE;
00220         }
00221         if (result->program_count != program_count) {
00222                 result->program_count = program_count;
00223                 *pchanged = TRUE;
00224         }
00225 
00226         return 1;
00227 }
00228 
00229 int channelscan_advance(struct hdhomerun_channelscan_t *scan, struct hdhomerun_channelscan_result_t *result)
00230 {
00231         memset(result, 0, sizeof(struct hdhomerun_channelscan_result_t));
00232 
00233         struct hdhomerun_channel_entry_t *entry = scan->next_channel;
00234         if (!entry) {
00235                 return 0;
00236         }
00237 
00238         /* Combine channels with same frequency. */
00239         result->frequency = hdhomerun_channel_entry_frequency(entry);
00240         strncpy(result->channel_str, hdhomerun_channel_entry_name(entry), sizeof(result->channel_str) - 1);
00241         result->channel_str[sizeof(result->channel_str) - 1] = 0;
00242 
00243         while (1) {
00244                 entry = hdhomerun_channel_list_prev(scan->channel_list, entry);
00245                 if (!entry) {
00246                         scan->next_channel = NULL;
00247                         break;
00248                 }
00249 
00250                 if (hdhomerun_channel_entry_frequency(entry) != result->frequency) {
00251                         scan->next_channel = entry;
00252                         break;
00253                 }
00254 
00255                 char *ptr = strchr(result->channel_str, 0);
00256                 sprintf(ptr, ", %s", hdhomerun_channel_entry_name(entry));
00257         }
00258 
00259         return 1;
00260 }
00261 
00262 int channelscan_detect(struct hdhomerun_channelscan_t *scan, struct hdhomerun_channelscan_result_t *result)
00263 {
00264         scan->scanned_channels++;
00265 
00266         /* Find lock. */
00267         int ret = channelscan_find_lock(scan, result->frequency, result);
00268         if (ret <= 0) {
00269                 return ret;
00270         }
00271         if (!result->status.lock_supported) {
00272                 return 1;
00273         }
00274 
00275         /* Detect programs. */
00276         result->program_count = 0;
00277 
00278         uint64_t timeout;
00279         if (strstr(hdhomerun_device_get_model_str(scan->hd), "atsc")) {
00280                 timeout = getcurrenttime() + 4000;
00281         } else {
00282                 timeout = getcurrenttime() + 10000;
00283         }
00284 
00285         uint64_t complete_time = getcurrenttime() + 1000;
00286 
00287         while (1) {
00288                 bool_t changed, incomplete;
00289                 ret = channelscan_detect_programs(scan, result, &changed, &incomplete);
00290                 if (ret <= 0) {
00291                         return ret;
00292                 }
00293 
00294                 if (changed) {
00295                         complete_time = getcurrenttime() + 1000;
00296                 }
00297 
00298                 if (!incomplete && (getcurrenttime() >= complete_time)) {
00299                         break;
00300                 }
00301 
00302                 if (getcurrenttime() >= timeout) {
00303                         break;
00304                 }
00305 
00306                 msleep_approx(250);
00307         }
00308 
00309         /* Lock => skip overlapping channels. */
00310         uint32_t max_next_frequency = result->frequency - 5500000;
00311         while (1) {
00312                 if (!scan->next_channel) {
00313                         break;
00314                 }
00315 
00316                 if (hdhomerun_channel_entry_frequency(scan->next_channel) <= max_next_frequency) {
00317                         break;
00318                 }
00319 
00320                 scan->next_channel = hdhomerun_channel_list_prev(scan->channel_list, scan->next_channel);
00321         }
00322 
00323         /* Success. */
00324         return 1;
00325 }
00326 
00327 uint8_t channelscan_get_progress(struct hdhomerun_channelscan_t *scan)
00328 {
00329         struct hdhomerun_channel_entry_t *entry = scan->next_channel;
00330         if (!entry) {
00331                 return 100;
00332         }
00333 
00334         uint32_t channels_remaining = 1;
00335         uint32_t frequency = hdhomerun_channel_entry_frequency(entry);
00336 
00337         while (1) {
00338                 entry = hdhomerun_channel_list_prev(scan->channel_list, entry);
00339                 if (!entry) {
00340                         break;
00341                 }
00342 
00343                 if (hdhomerun_channel_entry_frequency(entry) != frequency) {
00344                         channels_remaining++;
00345                         frequency = hdhomerun_channel_entry_frequency(entry);
00346                 }
00347         }
00348 
00349         return scan->scanned_channels * 100 / (scan->scanned_channels + channels_remaining);
00350 }
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends