MythTV  0.26-pre
hdhomerun_video.c
Go to the documentation of this file.
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 }
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends