]> git.sesse.net Git - vlc/blobdiff - modules/access/rtp/input.c
LGPL
[vlc] / modules / access / rtp / input.c
index fc58bc572c1b1f844618595c8873b7ffddd53067..b8c5394661fa5576d66b5615fe3bf64daa8c3f87 100644 (file)
@@ -6,14 +6,14 @@
  * Copyright © 2008 Rémi Denis-Courmont
  *
  * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2.0
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
  * of the License, or (at your option) any later version.
  *
  * This library is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
  *
  * You should have received a copy of the GNU Lesser General Public
  * License along with this library; if not, write to the Free Software
 #include <vlc_block.h>
 #include <vlc_network.h>
 
+#include <limits.h>
 #include <unistd.h>
 #ifdef HAVE_POLL
 # include <poll.h>
 #endif
 
 #include "rtp.h"
-#include <srtp.h>
-
-static bool fd_dead (int fd)
-{
-    struct pollfd ufd = { .fd = fd, };
-    return (poll (&ufd, 1, 0) > 0) && (ufd.revents & POLLHUP);
-}
+#ifdef HAVE_SRTP
+# include <srtp.h>
+#endif
 
 /**
- * Gets a datagram from the network.
- * @param fd datagram file descriptor
- * @return a block or NULL on fatal error (socket dead)
+ * Processes a packet received from the RTP socket.
  */
-static block_t *rtp_dgram_recv (vlc_object_t *obj, int fd)
+static void rtp_process (demux_t *demux, block_t *block)
 {
-    block_t *block = block_Alloc (0xffff);
-    ssize_t len;
+    demux_sys_t *sys = demux->p_sys;
 
-    do
-    {
-        block_cleanup_push (block);
-        len = net_Read (obj, fd, NULL,
-                        block->p_buffer, block->i_buffer, false);
-        vlc_cleanup_pop ();
+    if (block->i_buffer < 2)
+        goto drop;
+    const uint8_t ptype = rtp_ptype (block);
+    if (ptype >= 72 && ptype <= 76)
+        goto drop; /* Muxed RTCP, ignore for now FIXME */
 
-        if (((len <= 0) && fd_dead (fd)) || !vlc_object_alive (obj))
-        {   /* POLLHUP -> permanent (DCCP) socket error */
-            block_Release (block);
-            return NULL;
+#ifdef HAVE_SRTP
+    if (sys->srtp != NULL)
+    {
+        size_t len = block->i_buffer;
+        if (srtp_recv (sys->srtp, block->p_buffer, &len))
+        {
+            msg_Dbg (demux, "SRTP authentication/decryption failed");
+            goto drop;
         }
+        block->i_buffer = len;
     }
-    while (len == -1);
-
-    return block_Realloc (block, 0, len);
-}
-
-
-/**
- * Gets a framed RTP packet.
- * @param fd stream file descriptor
- * @return a block or NULL in case of fatal error
- */
-static block_t *rtp_stream_recv (vlc_object_t *obj, int fd)
-{
-    ssize_t len = 0;
-    uint8_t hdr[2]; /* frame header */
+#endif
 
-    /* Receives the RTP frame header */
-    do
-    {
-        ssize_t val = net_Read (obj, fd, NULL, hdr + len, 2 - len, false);
-        if (val <= 0)
-            return NULL;
-        len += val;
+    /* TODO: use SDP and get rid of this hack */
+    if (unlikely(sys->autodetect))
+    {   /* Autodetect payload type, _before_ rtp_queue() */
+        rtp_autodetect (demux, sys->session, block);
+        sys->autodetect = false;
     }
-    while (len < 2);
-
-    block_t *block = block_Alloc (GetWBE (hdr));
 
-    /* Receives the RTP packet */
-    for (ssize_t i = 0; i < len;)
-    {
-        ssize_t val;
+    rtp_queue (demux, sys->session, block);
+    return;
+drop:
+    block_Release (block);
+}
 
-        block_cleanup_push (block);
-        val = net_Read (obj, fd, NULL,
-                        block->p_buffer + i, block->i_buffer - i, false);
-        vlc_cleanup_pop ();
+static int rtp_timeout (mtime_t deadline)
+{
+    if (deadline == VLC_TS_INVALID)
+        return -1; /* infinite */
 
-        if (val <= 0)
-        {
-            block_Release (block);
-            return NULL;
-        }
-        i += val;
-    }
+    mtime_t t = mdate ();
+    if (t >= deadline)
+        return 0;
 
-    return block;
+    t = (deadline - t) / (CLOCK_FREQ / INT64_C(1000));
+    if (unlikely(t > INT_MAX))
+        return INT_MAX;
+    return t;
 }
 
-
-static block_t *rtp_recv (demux_t *demux)
+/**
+ * RTP/RTCP session thread for datagram sockets
+ */
+void *rtp_dgram_thread (void *opaque)
 {
-    demux_sys_t *p_sys = demux->p_sys;
+    demux_t *demux = opaque;
+    demux_sys_t *sys = demux->p_sys;
+    mtime_t deadline = VLC_TS_INVALID;
+    int rtp_fd = sys->fd;
 
-    for (block_t *block;; block_Release (block))
-    {
-        block = p_sys->framed_rtp
-                ? rtp_stream_recv (VLC_OBJECT (demux), p_sys->fd)
-                : rtp_dgram_recv (VLC_OBJECT (demux), p_sys->fd);
-        if (block == NULL)
-        {
-            msg_Err (demux, "RTP flow stopped");
-            break; /* fatal error */
-        }
+    struct pollfd ufd[1];
+    ufd[0].fd = rtp_fd;
+    ufd[0].events = POLLIN;
 
-        if (block->i_buffer < 2)
+    for (;;)
+    {
+        int n = poll (ufd, 1, rtp_timeout (deadline));
+        if (n == -1)
             continue;
 
-        /* FIXME */
-        const uint8_t ptype = rtp_ptype (block);
-        if (ptype >= 72 && ptype <= 76)
-            continue; /* Muxed RTCP, ignore for now */
+        int canc = vlc_savecancel ();
+        if (n == 0)
+            goto dequeue;
 
-        if (p_sys->srtp)
+        if (ufd[0].revents)
         {
-            size_t len = block->i_buffer;
-            int canc, err;
+            n--;
+            if (unlikely(ufd[0].revents & POLLHUP))
+                break; /* RTP socket dead (DCCP only) */
+
+            block_t *block = block_Alloc (0xffff); /* TODO: p_sys->mru */
+            if (unlikely(block == NULL))
+                break; /* we are totallly screwed */
 
-            canc = vlc_savecancel ();
-            err = srtp_recv (p_sys->srtp, block->p_buffer, &len);
-            vlc_restorecancel (canc);
-            if (err)
+            ssize_t len = recv (rtp_fd, block->p_buffer, block->i_buffer, 0);
+            if (len != -1)
             {
-                msg_Dbg (demux, "SRTP authentication/decryption failed");
-                continue;
+                block->i_buffer = len;
+                rtp_process (demux, block);
+            }
+            else
+            {
+                msg_Warn (demux, "RTP network error: %m");
+                block_Release (block);
             }
-            block->i_buffer = len;
         }
-        return block; /* success! */
+
+    dequeue:
+        if (!rtp_dequeue (demux, sys->session, &deadline))
+            deadline = VLC_TS_INVALID;
+        vlc_restorecancel (canc);
     }
     return NULL;
 }
 
-
-static void timer_cleanup (void *timer)
-{
-    vlc_timer_destroy (timer);
-}
-
-static void rtp_process (void *data);
-
-void *rtp_thread (void *data)
+/**
+ * RTP/RTCP session thread for stream sockets (framed RTP)
+ */
+void *rtp_stream_thread (void *opaque)
 {
-    demux_t *demux = data;
-    demux_sys_t *p_sys = demux->p_sys;
-    bool autodetect = true, reorder = false;
-
-    if (vlc_timer_create (&p_sys->timer, rtp_process, data))
-        return NULL;
-    vlc_cleanup_push (timer_cleanup, &p_sys->timer);
+#ifndef WIN32
+    demux_t *demux = opaque;
+    demux_sys_t *sys = demux->p_sys;
+    int fd = sys->fd;
 
     for (;;)
     {
-        block_t *block = rtp_recv (demux);
-        if (block == NULL)
+        /* There is no reordering on stream sockets, so no timeout. */
+        ssize_t val;
+
+        uint16_t frame_len;
+        if (recv (fd, &frame_len, 2, MSG_WAITALL) != 2)
             break;
 
-        if (autodetect)
-        {   /* Autodetect payload type, _before_ rtp_queue() */
-            /* No need for lock - the queue is empty. */
-            if (rtp_autodetect (demux, p_sys->session, block))
-            {
-                block_Release (block);
-                continue;
-            }
-            autodetect = false;
-        }
+        block_t *block = block_Alloc (ntohs (frame_len));
+        if (unlikely(block == NULL))
+            break;
 
-        vlc_mutex_lock (&p_sys->lock);
-        rtp_queue (demux, p_sys->session, block);
-        vlc_mutex_unlock (&p_sys->lock);
+        block_cleanup_push (block);
+        val = recv (fd, block->p_buffer, block->i_buffer, MSG_WAITALL);
+        vlc_cleanup_pop ();
+
+        if (val != (ssize_t)block->i_buffer)
+        {
+            block_Release (block);
+            break;
+        }
 
-        rtp_process (demux);
+        int canc = vlc_savecancel ();
+        rtp_process (demux, block);
+        rtp_dequeue_force (demux, sys->session);
+        vlc_restorecancel (canc);
     }
-    vlc_cleanup_run ();
+#else
+    (void) opaque;
+#endif
     return NULL;
 }
-
-
-/**
- * Process one RTP packet from the de-jitter queue.
- */
-static void rtp_process (void *data)
-{
-    demux_t *demux = data;
-    demux_sys_t *p_sys = demux->p_sys;
-    mtime_t deadline;
-
-    vlc_mutex_lock (&p_sys->lock);
-    if (rtp_dequeue (demux, p_sys->session, &deadline))
-        vlc_timer_schedule (&p_sys->timer, true, deadline, 0);
-    vlc_mutex_unlock (&p_sys->lock);
-}