00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
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
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
00077 hdhomerun_video_flush(vs);
00078
00079
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
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
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
00102 int rx_size = 1024 * 1024;
00103 setsockopt(vs->sock, SOL_SOCKET, SO_RCVBUF, (char *)&rx_size, sizeof(rx_size));
00104
00105
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
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
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
00239 if (previous_rtp_sequence == 0xFFFFFFFF) {
00240 return;
00241 }
00242
00243
00244 if (rtp_sequence == ((previous_rtp_sequence + 1) & 0xFFFF)) {
00245 return;
00246 }
00247
00248
00249 vs->network_error_count++;
00250
00251
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
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
00282 continue;
00283 }
00284
00285 pthread_mutex_lock(&vs->lock);
00286
00287
00288 size_t head = vs->head;
00289 uint8_t *ptr = vs->buffer + head;
00290 memcpy(ptr, pkt->pos, length);
00291
00292
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
00303 head += length;
00304 if (head >= vs->buffer_size) {
00305 head -= vs->buffer_size;
00306 }
00307
00308
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 }