|
MythTV
0.26-pre
|
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 }
1.7.6.1