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 uint8_t *buffer;
00038 size_t buffer_size;
00039 volatile size_t head;
00040 volatile size_t tail;
00041 size_t advance;
00042 volatile bool_t terminate;
00043 pthread_t thread;
00044 int sock;
00045 uint32_t rtp_sequence;
00046 struct hdhomerun_debug_t *dbg;
00047 volatile uint32_t packet_count;
00048 volatile uint32_t transport_error_count;
00049 volatile uint32_t network_error_count;
00050 volatile uint32_t sequence_error_count;
00051 volatile uint32_t overflow_error_count;
00052 volatile uint8_t sequence[0x2000];
00053 };
00054
00055 static THREAD_FUNC_PREFIX hdhomerun_video_thread_execute(void *arg);
00056
00057 struct hdhomerun_video_sock_t *hdhomerun_video_create(uint16_t listen_port, size_t buffer_size, struct hdhomerun_debug_t *dbg)
00058 {
00059
00060 struct hdhomerun_video_sock_t *vs = (struct hdhomerun_video_sock_t *)calloc(1, sizeof(struct hdhomerun_video_sock_t));
00061 if (!vs) {
00062 hdhomerun_debug_printf(dbg, "hdhomerun_video_create: failed to allocate video object\n");
00063 return NULL;
00064 }
00065
00066 vs->dbg = dbg;
00067 vs->sock = -1;
00068 pthread_mutex_init(&vs->lock, NULL);
00069
00070
00071 hdhomerun_video_flush(vs);
00072
00073
00074 vs->buffer_size = (buffer_size / VIDEO_DATA_PACKET_SIZE) * VIDEO_DATA_PACKET_SIZE;
00075 if (vs->buffer_size == 0) {
00076 hdhomerun_debug_printf(dbg, "hdhomerun_video_create: invalid buffer size (%lu bytes)\n", (unsigned long)buffer_size);
00077 goto error;
00078 }
00079 vs->buffer_size += VIDEO_DATA_PACKET_SIZE;
00080
00081
00082 vs->buffer = (uint8_t *)malloc(vs->buffer_size);
00083 if (!vs->buffer) {
00084 hdhomerun_debug_printf(dbg, "hdhomerun_video_create: failed to allocate buffer (%lu bytes)\n", (unsigned long)vs->buffer_size);
00085 goto error;
00086 }
00087
00088
00089 vs->sock = (int)socket(AF_INET, SOCK_DGRAM, 0);
00090 if (vs->sock == -1) {
00091 hdhomerun_debug_printf(dbg, "hdhomerun_video_create: failed to allocate socket\n");
00092 goto error;
00093 }
00094
00095
00096 int rx_size = 1024 * 1024;
00097 setsockopt(vs->sock, SOL_SOCKET, SO_RCVBUF, (char *)&rx_size, sizeof(rx_size));
00098
00099
00100 setsocktimeout(vs->sock, SOL_SOCKET, SO_SNDTIMEO, 1000);
00101 setsocktimeout(vs->sock, SOL_SOCKET, SO_RCVTIMEO, 1000);
00102
00103
00104 struct sockaddr_in sock_addr;
00105 memset(&sock_addr, 0, sizeof(sock_addr));
00106 sock_addr.sin_family = AF_INET;
00107 sock_addr.sin_addr.s_addr = htonl(INADDR_ANY);
00108 sock_addr.sin_port = htons(listen_port);
00109 if (bind(vs->sock, (struct sockaddr *)&sock_addr, sizeof(sock_addr)) != 0) {
00110 hdhomerun_debug_printf(dbg, "hdhomerun_video_create: failed to bind socket (port %u)\n", listen_port);
00111 goto error;
00112 }
00113
00114
00115 if (pthread_create(&vs->thread, NULL, &hdhomerun_video_thread_execute, vs) != 0) {
00116 hdhomerun_debug_printf(dbg, "hdhomerun_video_create: failed to start thread\n");
00117 goto error;
00118 }
00119
00120
00121 return vs;
00122
00123 error:
00124 if (vs->sock != -1) {
00125 close(vs->sock);
00126 }
00127 if (vs->buffer) {
00128 free(vs->buffer);
00129 }
00130 free(vs);
00131 return NULL;
00132 }
00133
00134 void hdhomerun_video_destroy(struct hdhomerun_video_sock_t *vs)
00135 {
00136 vs->terminate = TRUE;
00137 pthread_join(vs->thread, NULL);
00138
00139 close(vs->sock);
00140 free(vs->buffer);
00141
00142 free(vs);
00143 }
00144
00145 uint16_t hdhomerun_video_get_local_port(struct hdhomerun_video_sock_t *vs)
00146 {
00147 struct sockaddr_in sock_addr;
00148 socklen_t sockaddr_size = sizeof(sock_addr);
00149 if (getsockname(vs->sock, (struct sockaddr*)&sock_addr, &sockaddr_size) != 0) {
00150 hdhomerun_debug_printf(vs->dbg, "hdhomerun_video_get_local_port: getsockname failed (%d)\n", sock_getlasterror);
00151 return 0;
00152 }
00153
00154 return ntohs(sock_addr.sin_port);
00155 }
00156
00157 static void hdhomerun_video_stats_ts_pkt(struct hdhomerun_video_sock_t *vs, uint8_t *ptr)
00158 {
00159 uint16_t packet_identifier = ((uint16_t)(ptr[1] & 0x1F) << 8) | (uint16_t)ptr[2];
00160 if (packet_identifier == 0x1FFF) {
00161 return;
00162 }
00163
00164 bool_t transport_error = ptr[1] >> 7;
00165 if (transport_error) {
00166 vs->transport_error_count++;
00167 vs->sequence[packet_identifier] = 0xFF;
00168 return;
00169 }
00170
00171 uint8_t continuity_counter = ptr[3] & 0x0F;
00172 uint8_t previous_sequence = vs->sequence[packet_identifier];
00173
00174 if (continuity_counter == ((previous_sequence + 1) & 0x0F)) {
00175 vs->sequence[packet_identifier] = continuity_counter;
00176 return;
00177 }
00178 if (previous_sequence == 0xFF) {
00179 vs->sequence[packet_identifier] = continuity_counter;
00180 return;
00181 }
00182 if (continuity_counter == previous_sequence) {
00183 return;
00184 }
00185
00186 vs->sequence_error_count++;
00187 vs->sequence[packet_identifier] = continuity_counter;
00188 }
00189
00190 static void hdhomerun_video_parse_rtp(struct hdhomerun_video_sock_t *vs, struct hdhomerun_pkt_t *pkt)
00191 {
00192 pkt->pos += 2;
00193 uint32_t rtp_sequence = hdhomerun_pkt_read_u16(pkt);
00194 pkt->pos += 8;
00195
00196 if (rtp_sequence != ((vs->rtp_sequence + 1) & 0xFFFF)) {
00197 if (vs->rtp_sequence != 0xFFFFFFFF) {
00198 vs->network_error_count++;
00199
00200
00201
00202 int i;
00203 for (i = 0; i < sizeof(vs->sequence) / sizeof(uint8_t) ; i++)
00204 vs->sequence[i] = 0xFF;
00205 }
00206 }
00207
00208 vs->rtp_sequence = rtp_sequence;
00209 }
00210
00211 static THREAD_FUNC_PREFIX hdhomerun_video_thread_execute(void *arg)
00212 {
00213 struct hdhomerun_video_sock_t *vs = (struct hdhomerun_video_sock_t *)arg;
00214 struct hdhomerun_pkt_t pkt_inst;
00215
00216 while (!vs->terminate) {
00217 struct hdhomerun_pkt_t *pkt = &pkt_inst;
00218 hdhomerun_pkt_reset(pkt);
00219
00220
00221 int length = recv(vs->sock, (char *)pkt->end, VIDEO_RTP_DATA_PACKET_SIZE, 0);
00222 pkt->end += length;
00223
00224 if (length == VIDEO_RTP_DATA_PACKET_SIZE) {
00225 hdhomerun_video_parse_rtp(vs, pkt);
00226 length = (int)(pkt->end - pkt->pos);
00227 }
00228
00229 if (length != VIDEO_DATA_PACKET_SIZE) {
00230 if (length > 0) {
00231
00232 continue;
00233 }
00234 if (sock_getlasterror_socktimeout) {
00235
00236 continue;
00237 }
00238 vs->terminate = TRUE;
00239 return NULL;
00240 }
00241
00242 pthread_mutex_lock(&vs->lock);
00243
00244
00245 size_t head = vs->head;
00246 uint8_t *ptr = vs->buffer + head;
00247 memcpy(ptr, pkt->pos, length);
00248
00249
00250 vs->packet_count++;
00251 hdhomerun_video_stats_ts_pkt(vs, ptr + TS_PACKET_SIZE * 0);
00252 hdhomerun_video_stats_ts_pkt(vs, ptr + TS_PACKET_SIZE * 1);
00253 hdhomerun_video_stats_ts_pkt(vs, ptr + TS_PACKET_SIZE * 2);
00254 hdhomerun_video_stats_ts_pkt(vs, ptr + TS_PACKET_SIZE * 3);
00255 hdhomerun_video_stats_ts_pkt(vs, ptr + TS_PACKET_SIZE * 4);
00256 hdhomerun_video_stats_ts_pkt(vs, ptr + TS_PACKET_SIZE * 5);
00257 hdhomerun_video_stats_ts_pkt(vs, ptr + TS_PACKET_SIZE * 6);
00258
00259
00260 head += length;
00261 if (head >= vs->buffer_size) {
00262 head -= vs->buffer_size;
00263 }
00264
00265
00266 if (head == vs->tail) {
00267 vs->overflow_error_count++;
00268 pthread_mutex_unlock(&vs->lock);
00269 continue;
00270 }
00271
00272
00273 vs->head = head;
00274
00275 pthread_mutex_unlock(&vs->lock);
00276 }
00277
00278 return NULL;
00279 }
00280
00281 uint8_t *hdhomerun_video_recv(struct hdhomerun_video_sock_t *vs, size_t max_size, size_t *pactual_size)
00282 {
00283 pthread_mutex_lock(&vs->lock);
00284
00285 size_t head = vs->head;
00286 size_t tail = vs->tail;
00287
00288 if (vs->advance > 0) {
00289 tail += vs->advance;
00290 if (tail >= vs->buffer_size) {
00291 tail -= vs->buffer_size;
00292 }
00293
00294
00295 vs->tail = tail;
00296 }
00297
00298 if (head == tail) {
00299 vs->advance = 0;
00300 *pactual_size = 0;
00301 pthread_mutex_unlock(&vs->lock);
00302 return NULL;
00303 }
00304
00305 size_t size = (max_size / VIDEO_DATA_PACKET_SIZE) * VIDEO_DATA_PACKET_SIZE;
00306 if (size == 0) {
00307 vs->advance = 0;
00308 *pactual_size = 0;
00309 pthread_mutex_unlock(&vs->lock);
00310 return NULL;
00311 }
00312
00313 size_t avail;
00314 if (head > tail) {
00315 avail = head - tail;
00316 } else {
00317 avail = vs->buffer_size - tail;
00318 }
00319 if (size > avail) {
00320 size = avail;
00321 }
00322 vs->advance = size;
00323 *pactual_size = size;
00324 uint8_t *result = vs->buffer + tail;
00325
00326 pthread_mutex_unlock(&vs->lock);
00327 return result;
00328 }
00329
00330 void hdhomerun_video_flush(struct hdhomerun_video_sock_t *vs)
00331 {
00332 pthread_mutex_lock(&vs->lock);
00333
00334 vs->tail = vs->head;
00335 vs->advance = 0;
00336
00337
00338 int i;
00339 for (i = 0; i < sizeof(vs->sequence) / sizeof(uint8_t) ; i++)
00340 vs->sequence[i] = 0xFF;
00341
00342 vs->rtp_sequence = 0xFFFFFFFF;
00343
00344 vs->packet_count = 0;
00345 vs->transport_error_count = 0;
00346 vs->network_error_count = 0;
00347 vs->sequence_error_count = 0;
00348 vs->overflow_error_count = 0;
00349
00350 pthread_mutex_unlock(&vs->lock);
00351 }
00352
00353 void hdhomerun_video_debug_print_stats(struct hdhomerun_video_sock_t *vs)
00354 {
00355 struct hdhomerun_video_stats_t stats;
00356 hdhomerun_video_get_stats(vs, &stats);
00357
00358 hdhomerun_debug_printf(vs->dbg, "video sock: pkt=%ld net=%ld te=%ld miss=%ld drop=%ld\n",
00359 stats.packet_count, stats.network_error_count,
00360 stats.transport_error_count, stats.sequence_error_count,
00361 stats.overflow_error_count
00362 );
00363 }
00364
00365 void hdhomerun_video_get_stats(struct hdhomerun_video_sock_t *vs, struct hdhomerun_video_stats_t *stats)
00366 {
00367 memset(stats, 0, sizeof(struct hdhomerun_video_stats_t));
00368
00369 pthread_mutex_lock(&vs->lock);
00370
00371 stats->packet_count = vs->packet_count;
00372 stats->network_error_count = vs->network_error_count;
00373 stats->transport_error_count = vs->transport_error_count;
00374 stats->sequence_error_count = vs->sequence_error_count;
00375 stats->overflow_error_count = vs->overflow_error_count;
00376
00377 pthread_mutex_unlock(&vs->lock);
00378 }