|
MythTV
0.26-pre
|
00001 /* 00002 * hdhomerun_video.c 00003 * 00004 * Copyright © 2006-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_video_sock_t { 00036 pthread_mutex_t lock; 00037 struct hdhomerun_debug_t *dbg; 00038 00039 hdhomerun_sock_t sock; 00040 uint32_t multicast_ip; 00041 00042 volatile size_t head; 00043 volatile size_t tail; 00044 uint8_t *buffer; 00045 size_t buffer_size; 00046 size_t advance; 00047 00048 pthread_t thread; 00049 volatile bool_t terminate; 00050 00051 volatile uint32_t packet_count; 00052 volatile uint32_t transport_error_count; 00053 volatile uint32_t network_error_count; 00054 volatile uint32_t sequence_error_count; 00055 volatile uint32_t overflow_error_count; 00056 00057 volatile uint32_t rtp_sequence; 00058 volatile uint8_t sequence[0x2000]; 00059 }; 00060 00061 static THREAD_FUNC_PREFIX hdhomerun_video_thread_execute(void *arg); 00062 00063 struct hdhomerun_video_sock_t *hdhomerun_video_create(uint16_t listen_port, bool_t allow_port_reuse, size_t buffer_size, struct hdhomerun_debug_t *dbg) 00064 { 00065 /* Create object. */ 00066 struct hdhomerun_video_sock_t *vs = (struct hdhomerun_video_sock_t *)calloc(1, sizeof(struct hdhomerun_video_sock_t)); 00067 if (!vs) { 00068 hdhomerun_debug_printf(dbg, "hdhomerun_video_create: failed to allocate video object\n"); 00069 return NULL; 00070 } 00071 00072 vs->dbg = dbg; 00073 vs->sock = HDHOMERUN_SOCK_INVALID; 00074 pthread_mutex_init(&vs->lock, NULL); 00075 00076 /* Reset sequence tracking. */ 00077 hdhomerun_video_flush(vs); 00078 00079 /* Buffer size. */ 00080 vs->buffer_size = (buffer_size / VIDEO_DATA_PACKET_SIZE) * VIDEO_DATA_PACKET_SIZE; 00081 if (vs->buffer_size == 0) { 00082 hdhomerun_debug_printf(dbg, "hdhomerun_video_create: invalid buffer size (%lu bytes)\n", (unsigned long)buffer_size); 00083 goto error; 00084 } 00085 vs->buffer_size += VIDEO_DATA_PACKET_SIZE; 00086 00087 /* Create buffer. */ 00088 vs->buffer = (uint8_t *)malloc(vs->buffer_size); 00089 if (!vs->buffer) { 00090 hdhomerun_debug_printf(dbg, "hdhomerun_video_create: failed to allocate buffer (%lu bytes)\n", (unsigned long)vs->buffer_size); 00091 goto error; 00092 } 00093 00094 /* Create socket. */ 00095 vs->sock = hdhomerun_sock_create_udp(); 00096 if (vs->sock == HDHOMERUN_SOCK_INVALID) { 00097 hdhomerun_debug_printf(dbg, "hdhomerun_video_create: failed to allocate socket\n"); 00098 goto error; 00099 } 00100 00101 /* Expand socket buffer size. */ 00102 int rx_size = 1024 * 1024; 00103 setsockopt(vs->sock, SOL_SOCKET, SO_RCVBUF, (char *)&rx_size, sizeof(rx_size)); 00104 00105 /* Bind socket. */ 00106 if (!hdhomerun_sock_bind(vs->sock, INADDR_ANY, listen_port, allow_port_reuse)) { 00107 hdhomerun_debug_printf(dbg, "hdhomerun_video_create: failed to bind socket (port %u)\n", listen_port); 00108 goto error; 00109 } 00110 00111 /* Start thread. */ 00112 if (pthread_create(&vs->thread, NULL, &hdhomerun_video_thread_execute, vs) != 0) { 00113 hdhomerun_debug_printf(dbg, "hdhomerun_video_create: failed to start thread\n"); 00114 goto error; 00115 } 00116 00117 /* Success. */ 00118 return vs; 00119 00120 error: 00121 if (vs->sock != HDHOMERUN_SOCK_INVALID) { 00122 hdhomerun_sock_destroy(vs->sock); 00123 } 00124 if (vs->buffer) { 00125 free(vs->buffer); 00126 } 00127 free(vs); 00128 return NULL; 00129 } 00130 00131 void hdhomerun_video_destroy(struct hdhomerun_video_sock_t *vs) 00132 { 00133 vs->terminate = TRUE; 00134 pthread_join(vs->thread, NULL); 00135 00136 hdhomerun_sock_destroy(vs->sock); 00137 free(vs->buffer); 00138 00139 free(vs); 00140 } 00141 00142 hdhomerun_sock_t hdhomerun_video_get_sock(struct hdhomerun_video_sock_t *vs) 00143 { 00144 return vs->sock; 00145 } 00146 00147 uint16_t hdhomerun_video_get_local_port(struct hdhomerun_video_sock_t *vs) 00148 { 00149 uint16_t port = hdhomerun_sock_getsockname_port(vs->sock); 00150 if (port == 0) { 00151 hdhomerun_debug_printf(vs->dbg, "hdhomerun_video_get_local_port: getsockname failed (%d)\n", hdhomerun_sock_getlasterror()); 00152 return 0; 00153 } 00154 00155 return port; 00156 } 00157 00158 int hdhomerun_video_join_multicast_group(struct hdhomerun_video_sock_t *vs, uint32_t multicast_ip, uint32_t local_ip) 00159 { 00160 if (vs->multicast_ip != 0) { 00161 hdhomerun_video_leave_multicast_group(vs); 00162 } 00163 00164 struct ip_mreq imr; 00165 memset(&imr, 0, sizeof(imr)); 00166 imr.imr_multiaddr.s_addr = htonl(multicast_ip); 00167 imr.imr_interface.s_addr = htonl(local_ip); 00168 00169 if (setsockopt(vs->sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, (const char *)&imr, sizeof(imr)) != 0) { 00170 hdhomerun_debug_printf(vs->dbg, "hdhomerun_video_join_multicast_group: setsockopt failed (%d)\n", hdhomerun_sock_getlasterror()); 00171 return -1; 00172 } 00173 00174 vs->multicast_ip = multicast_ip; 00175 return 1; 00176 } 00177 00178 int hdhomerun_video_leave_multicast_group(struct hdhomerun_video_sock_t *vs) 00179 { 00180 if (vs->multicast_ip == 0) { 00181 return 1; 00182 } 00183 00184 struct ip_mreq imr; 00185 memset(&imr, 0, sizeof(imr)); 00186 imr.imr_multiaddr.s_addr = htonl(vs->multicast_ip); 00187 imr.imr_interface.s_addr = htonl(INADDR_ANY); 00188 00189 if (setsockopt(vs->sock, IPPROTO_IP, IP_DROP_MEMBERSHIP, (const char *)&imr, sizeof(imr)) != 0) { 00190 hdhomerun_debug_printf(vs->dbg, "hdhomerun_video_leave_multicast_group: setsockopt failed (%d)\n", hdhomerun_sock_getlasterror()); 00191 } 00192 00193 vs->multicast_ip = 0; 00194 return 1; 00195 } 00196 00197 static void hdhomerun_video_stats_ts_pkt(struct hdhomerun_video_sock_t *vs, uint8_t *ptr) 00198 { 00199 uint16_t packet_identifier = ((uint16_t)(ptr[1] & 0x1F) << 8) | (uint16_t)ptr[2]; 00200 if (packet_identifier == 0x1FFF) { 00201 return; 00202 } 00203 00204 bool_t transport_error = ptr[1] >> 7; 00205 if (transport_error) { 00206 vs->transport_error_count++; 00207 vs->sequence[packet_identifier] = 0xFF; 00208 return; 00209 } 00210 00211 uint8_t sequence = ptr[3] & 0x0F; 00212 00213 uint8_t previous_sequence = vs->sequence[packet_identifier]; 00214 vs->sequence[packet_identifier] = sequence; 00215 00216 if (previous_sequence == 0xFF) { 00217 return; 00218 } 00219 if (sequence == ((previous_sequence + 1) & 0x0F)) { 00220 return; 00221 } 00222 if (sequence == previous_sequence) { 00223 return; 00224 } 00225 00226 vs->sequence_error_count++; 00227 } 00228 00229 static void hdhomerun_video_parse_rtp(struct hdhomerun_video_sock_t *vs, struct hdhomerun_pkt_t *pkt) 00230 { 00231 pkt->pos += 2; 00232 uint32_t rtp_sequence = hdhomerun_pkt_read_u16(pkt); 00233 pkt->pos += 8; 00234 00235 uint32_t previous_rtp_sequence = vs->rtp_sequence; 00236 vs->rtp_sequence = rtp_sequence; 00237 00238 /* Initial case - first packet received. */ 00239 if (previous_rtp_sequence == 0xFFFFFFFF) { 00240 return; 00241 } 00242 00243 /* Normal case - next sequence number. */ 00244 if (rtp_sequence == ((previous_rtp_sequence + 1) & 0xFFFF)) { 00245 return; 00246 } 00247 00248 /* Error case - sequence missed. */ 00249 vs->network_error_count++; 00250 00251 /* Restart pid sequence check after packet loss. */ 00252 int i; 00253 for (i = 0; i < 0x2000; i++) { 00254 vs->sequence[i] = 0xFF; 00255 } 00256 } 00257 00258 static THREAD_FUNC_PREFIX hdhomerun_video_thread_execute(void *arg) 00259 { 00260 struct hdhomerun_video_sock_t *vs = (struct hdhomerun_video_sock_t *)arg; 00261 struct hdhomerun_pkt_t pkt_inst; 00262 00263 while (!vs->terminate) { 00264 struct hdhomerun_pkt_t *pkt = &pkt_inst; 00265 hdhomerun_pkt_reset(pkt); 00266 00267 /* Receive. */ 00268 size_t length = VIDEO_RTP_DATA_PACKET_SIZE; 00269 if (!hdhomerun_sock_recv(vs->sock, pkt->end, &length, 25)) { 00270 continue; 00271 } 00272 00273 pkt->end += length; 00274 00275 if (length == VIDEO_RTP_DATA_PACKET_SIZE) { 00276 hdhomerun_video_parse_rtp(vs, pkt); 00277 length = (int)(pkt->end - pkt->pos); 00278 } 00279 00280 if (length != VIDEO_DATA_PACKET_SIZE) { 00281 /* Data received but not valid - ignore. */ 00282 continue; 00283 } 00284 00285 pthread_mutex_lock(&vs->lock); 00286 00287 /* Store in ring buffer. */ 00288 size_t head = vs->head; 00289 uint8_t *ptr = vs->buffer + head; 00290 memcpy(ptr, pkt->pos, length); 00291 00292 /* Stats. */ 00293 vs->packet_count++; 00294 hdhomerun_video_stats_ts_pkt(vs, ptr + TS_PACKET_SIZE * 0); 00295 hdhomerun_video_stats_ts_pkt(vs, ptr + TS_PACKET_SIZE * 1); 00296 hdhomerun_video_stats_ts_pkt(vs, ptr + TS_PACKET_SIZE * 2); 00297 hdhomerun_video_stats_ts_pkt(vs, ptr + TS_PACKET_SIZE * 3); 00298 hdhomerun_video_stats_ts_pkt(vs, ptr + TS_PACKET_SIZE * 4); 00299 hdhomerun_video_stats_ts_pkt(vs, ptr + TS_PACKET_SIZE * 5); 00300 hdhomerun_video_stats_ts_pkt(vs, ptr + TS_PACKET_SIZE * 6); 00301 00302 /* Calculate new head. */ 00303 head += length; 00304 if (head >= vs->buffer_size) { 00305 head -= vs->buffer_size; 00306 } 00307 00308 /* Check for buffer overflow. */ 00309 if (head == vs->tail) { 00310 vs->overflow_error_count++; 00311 pthread_mutex_unlock(&vs->lock); 00312 continue; 00313 } 00314 00315 vs->head = head; 00316 00317 pthread_mutex_unlock(&vs->lock); 00318 } 00319 00320 return NULL; 00321 } 00322 00323 uint8_t *hdhomerun_video_recv(struct hdhomerun_video_sock_t *vs, size_t max_size, size_t *pactual_size) 00324 { 00325 pthread_mutex_lock(&vs->lock); 00326 00327 size_t head = vs->head; 00328 size_t tail = vs->tail; 00329 00330 if (vs->advance > 0) { 00331 tail += vs->advance; 00332 if (tail >= vs->buffer_size) { 00333 tail -= vs->buffer_size; 00334 } 00335 00336 vs->tail = tail; 00337 } 00338 00339 if (head == tail) { 00340 vs->advance = 0; 00341 *pactual_size = 0; 00342 pthread_mutex_unlock(&vs->lock); 00343 return NULL; 00344 } 00345 00346 size_t size = (max_size / VIDEO_DATA_PACKET_SIZE) * VIDEO_DATA_PACKET_SIZE; 00347 if (size == 0) { 00348 vs->advance = 0; 00349 *pactual_size = 0; 00350 pthread_mutex_unlock(&vs->lock); 00351 return NULL; 00352 } 00353 00354 size_t avail; 00355 if (head > tail) { 00356 avail = head - tail; 00357 } else { 00358 avail = vs->buffer_size - tail; 00359 } 00360 if (size > avail) { 00361 size = avail; 00362 } 00363 vs->advance = size; 00364 *pactual_size = size; 00365 uint8_t *result = vs->buffer + tail; 00366 00367 pthread_mutex_unlock(&vs->lock); 00368 return result; 00369 } 00370 00371 void hdhomerun_video_flush(struct hdhomerun_video_sock_t *vs) 00372 { 00373 pthread_mutex_lock(&vs->lock); 00374 00375 vs->tail = vs->head; 00376 vs->advance = 0; 00377 00378 vs->rtp_sequence = 0xFFFFFFFF; 00379 00380 int i; 00381 for (i = 0; i < 0x2000; i++) { 00382 vs->sequence[i] = 0xFF; 00383 } 00384 00385 vs->packet_count = 0; 00386 vs->transport_error_count = 0; 00387 vs->network_error_count = 0; 00388 vs->sequence_error_count = 0; 00389 vs->overflow_error_count = 0; 00390 00391 pthread_mutex_unlock(&vs->lock); 00392 } 00393 00394 void hdhomerun_video_debug_print_stats(struct hdhomerun_video_sock_t *vs) 00395 { 00396 struct hdhomerun_video_stats_t stats; 00397 hdhomerun_video_get_stats(vs, &stats); 00398 00399 hdhomerun_debug_printf(vs->dbg, "video sock: pkt=%lu net=%lu te=%lu miss=%lu drop=%lu\n", 00400 (unsigned long)stats.packet_count, (unsigned long)stats.network_error_count, 00401 (unsigned long)stats.transport_error_count, (unsigned long)stats.sequence_error_count, 00402 (unsigned long)stats.overflow_error_count 00403 ); 00404 } 00405 00406 void hdhomerun_video_get_stats(struct hdhomerun_video_sock_t *vs, struct hdhomerun_video_stats_t *stats) 00407 { 00408 memset(stats, 0, sizeof(struct hdhomerun_video_stats_t)); 00409 00410 pthread_mutex_lock(&vs->lock); 00411 00412 stats->packet_count = vs->packet_count; 00413 stats->network_error_count = vs->network_error_count; 00414 stats->transport_error_count = vs->transport_error_count; 00415 stats->sequence_error_count = vs->sequence_error_count; 00416 stats->overflow_error_count = vs->overflow_error_count; 00417 00418 pthread_mutex_unlock(&vs->lock); 00419 }
1.7.6.1