[vlc-devel] [PATCH] HttpLive stream_filter: alternative version of ChooseSegment function

Łukasz Korbel korbel85 at gmail.com
Tue Mar 6 23:54:51 CET 2012


HLS: Alternative way of choosing first segment to play

    The goal is to allow user to specify how playlist will be played.
Default bahaviour is to choose first segment from playlist.
    Option --hls-play-all, --no-hls-play-all If enabled (default)
always first segment in playlist will be choosed for starting point.
If disabled segment is choosed from the ending of playlist according
to value of --hls-delay.
    Option --hls-delay [seconds] Specify how far before list end must
be first segment to play. If set to 0 (default) the latest possible
segment is choosed, which according to HLS specification is last but
two segment in playlist.

diff --git a/modules/stream_filter/httplive.c b/modules/stream_filter/httplive.c
index 71730f4..48bd061 100644
--- a/modules/stream_filter/httplive.c
+++ b/modules/stream_filter/httplive.c
@@ -49,11 +49,24 @@
 static int  Open (vlc_object_t *);
 static void Close(vlc_object_t *);

+#define HLS_PLAYALL_TEXT N_("Play whole playlist (default enabled)")
+#define HLS_PLAYALL_LONGTEXT N_("Start playback of live stream from first "\
+                              "segment in the playlist. If disabled playback "\
+                              "starting segment is choosed based on
hls-delay "\
+                              "value.")
+#define HLS_DELAY_TEXT N_("Minimal playback delay (seconds, default: 0)")
+#define HLS_DELAY_LONGTEXT N_("Define minimal delay of live stream playback. "\
+                              "If this values is set to 0 vlc will
use minimal"\
+                              " delay, starting from last but two segment.")
+
 vlc_module_begin()
     set_category(CAT_INPUT)
     set_subcategory(SUBCAT_INPUT_STREAM_FILTER)
     set_description(N_("Http Live Streaming stream filter"))
     set_capability("stream_filter", 20)
+    add_bool( "hls-play-all", true, HLS_PLAYALL_TEXT,HLS_PLAYALL_LONGTEXT,
+              true )
+    add_integer( "hls-delay",  -1, HLS_DELAY_TEXT,  HLS_DELAY_LONGTEXT,  true )
     set_callbacks(Open, Close)
 vlc_module_end()

@@ -83,6 +96,8 @@ typedef struct hls_stream_s
     int         version;    /* protocol version should be 1 */
     int         sequence;   /* media sequence number */
     int         duration;   /* maximum duration per segment (s) */
+    bool        b_play_all; /* start always from first segment in playlist */
+    int         delay;      /* minimal delay of live stream playback
(seconds)*/
     uint64_t    bandwidth;  /* bandwidth usage of segments (bits per second)*/
     uint64_t    size;       /* stream length is calculated by taking the sum
                                foreach segment of (segment->duration
* hls->bandwidth/8) */
@@ -165,56 +180,44 @@ static void segment_Free(segment_t *segment);
 /****************************************************************************
  *
  ****************************************************************************/
+static const char *const ext[] = {
+    "#EXT-X-TARGETDURATION",
+    "#EXT-X-MEDIA-SEQUENCE",
+    "#EXT-X-KEY",
+    "#EXT-X-ALLOW-CACHE",
+    "#EXT-X-ENDLIST",
+    "#EXT-X-STREAM-INF",
+    "#EXT-X-DISCONTINUITY",
+    "#EXT-X-VERSION"
+};
+
 static bool isHTTPLiveStreaming(stream_t *s)
 {
-    const uint8_t *peek;
+    const uint8_t *peek, *peek_end;

-    int size = stream_Peek(s->p_source, &peek, 46);
-    if (size < 7)
+    int64_t i_size = stream_Peek(s->p_source, &peek, 46);
+    if (i_size < 1)
         return false;

-    if (memcmp(peek, "#EXTM3U", 7) != 0)
+    if (strncasecmp((const char*)peek, "#EXTM3U", 7) != 0)
         return false;

-    peek += 7;
-    size -= 7;
-
     /* Parse stream and search for
      * EXT-X-TARGETDURATION or EXT-X-STREAM-INF tag, see
      * http://tools.ietf.org/html/draft-pantos-http-live-streaming-04#page-8 */
-    while (size--)
-    {
-        static const char *const ext[] = {
-            "TARGETDURATION",
-            "MEDIA-SEQUENCE",
-            "KEY",
-            "ALLOW-CACHE",
-            "ENDLIST",
-            "STREAM-INF",
-            "DISCONTINUITY",
-            "VERSION"
-        };
-
-        if (*peek++ != '#')
-            continue;
-
-        if (size < 6)
-            continue;
-
-        if (memcmp(peek, "EXT-X-", 6))
-            continue;
-
-        peek += 6;
-        size -= 6;
-
-        for (size_t i = 0; i < ARRAY_SIZE(ext); i++)
+    peek_end = peek + i_size;
+    while(peek <= peek_end)
+    {
+        if (*peek == '#')
         {
-            size_t len = strlen(ext[i]);
-            if (size < len)
-                continue;
-            if (!memcmp(peek, ext[i], len))
-                return true;
+            for (unsigned int i = 0; i < ARRAY_SIZE(ext); i++)
+            {
+                char *p = strstr((const char*)peek, ext[i]);
+                if (p != NULL)
+                    return true;
+            }
         }
+        peek++;
     }

     return false;
@@ -229,6 +232,8 @@ static hls_stream_t *hls_New(vlc_array_t
*hls_stream, const int id, const uint64
     hls->id = id;
     hls->bandwidth = bw;
     hls->duration = -1;/* unknown */
+    hls->b_play_all = true;
+    hls->delay = 0;
     hls->size = 0;
     hls->sequence = 0; /* default is 0 */
     hls->version = 1;  /* default protocol version */
@@ -425,48 +430,51 @@ static segment_t *segment_Find(hls_stream_t
*hls, const int sequence)
     return NULL;
 }

+static int segment_Duration(hls_stream_t *hls, const int index)
+{
+    segment_t *segment = segment_GetSegment(hls, index);
+    assert(segment);
+
+    /*if (segment->duration > hls->duration)
+    {
+        msg_Err(hls, "EXTINF:%d duration is larger than
EXT-X-TARGETDURATION:%d",
+                segment->duration, hls->duration);
+    }*/
+
+    return segment->duration;
+}
+
 static int ChooseSegment(stream_t *s, const int current)
 {
     stream_sys_t *p_sys = (stream_sys_t *)s->p_sys;
     hls_stream_t *hls = hls_Get(p_sys->hls_stream, current);
     if (hls == NULL) return 0;

-    /* Choose a segment to start which is no closer than
-     * 3 times the target duration from the end of the playlist.
-     */
     int wanted = 0;
-    int duration = 0;
-    int sequence = 0;
     int count = vlc_array_count(hls->segments);
-    int i = p_sys->b_live ? count - 1 : 0;

-    while((i >= 0) && (i < count))
+    /* If playing live stream and hls-play-all is disabled start playback from
+    hls-delay number of seconds before end of playlist. Otherwise start from
+    first segment in playlist (wanted = 0) */
+    if (p_sys->b_live && !hls->b_play_all)
     {
-        segment_t *segment = segment_GetSegment(hls, i);
-        assert(segment);
-
-        if (segment->duration > hls->duration)
-        {
-            msg_Err(s, "EXTINF:%d duration is larger than
EXT-X-TARGETDURATION:%d",
-                    segment->duration, hls->duration);
-        }
-
-        duration += segment->duration;
-        if (duration >= 3 * hls->duration)
-        {
-            /* Start point found */
-            wanted = p_sys->b_live ? i : 0;
-            sequence = segment->sequence;
-            break;
-        }
-
-        if (p_sys->b_live)
-            i-- ;
-        else
-            i++;
+        /* Must start at least before last two segments.
+        See: http://tools.ietf.org/html/draft-pantos-http-live-streaming-00#section-6.2.2
*/
+        int duration = 0;
+        duration += segment_Duration( hls, count -1); //last seg
+        duration += segment_Duration( hls, count -2); //last but one seg
+
+        int i= count - 3;  //first available
+        while (duration < hls->delay && i >= 0)
+            duration += segment_Duration( hls, i--);
+        wanted = i;
     }

-    msg_Info(s, "Choose segment %d/%d (sequence=%d)", wanted, count, sequence);
+    //query for starting segment sequence number
+    segment_t *segment = segment_GetSegment(hls, wanted);
+    int sequence = segment->sequence;
+
+    msg_Info(s, "Choose segment %d/%d (sequence=%d)", wanted+1,
count, sequence);
     return wanted;
 }

@@ -671,6 +679,8 @@ static int parse_StreamInformation(stream_t *s,
vlc_array_t **hls_stream,
     char *psz_uri = relative_URI(s->p_sys->m3u8, uri);

     *hls = hls_New(*hls_stream, id, bw, psz_uri ? psz_uri : uri);
+    (*hls)->b_play_all = var_InheritBool(s, "hls-play-all");
+    (*hls)->delay = var_InheritInteger(s, "hls-delay");

     free(psz_uri);

@@ -1010,6 +1020,8 @@ static int parse_M3U8(stream_t *s, vlc_array_t
*streams, uint8_t *buffer, const
         {
             /* No Meta playlist used */
             hls = hls_New(streams, 0, 0, p_sys->m3u8);
+            hls->b_play_all = var_InheritBool(s, "hls-play-all");
+            hls->delay = var_InheritInteger(s, "hls-delay");
             if (hls)
             {
                 /* Get TARGET-DURATION first */
@@ -1470,10 +1482,10 @@ static int hls_DownloadSegmentData(stream_t
*s, hls_stream_t *hls, segment_t *se
         return VLC_EGENERIC;
     }
     mtime_t duration = mdate() - start;
-    if (hls->bandwidth == 0 && segment->duration > 0)
+    if (hls->bandwidth == 0)
     {
         /* Try to estimate the bandwidth for this stream */
-        hls->bandwidth = (uint64_t)(((double)segment->size * 8) /
((double)segment->duration));
+        hls->bandwidth = (uint64_t)((double)segment->size /
((double)duration / 1000000.0));
     }

     /* If the segment is encrypted, decode it */
@@ -1856,16 +1868,6 @@ static int Open(vlc_object_t *p_this)
     }
     p_sys->m3u8 = psz_uri;

-    char *new_path;
-    if (asprintf(&new_path, "%s.ts", s->psz_path) < 0)
-    {
-        free(p_sys->m3u8);
-        free(p_sys);
-        return VLC_ENOMEM;
-    }
-    free(s->psz_path);
-    s->psz_path = new_path;
-
     p_sys->bandwidth = 0;
     p_sys->b_live = true;
     p_sys->b_meta = false;
@@ -2404,12 +2406,14 @@ static int segment_Seek(stream_t *s, const uint64_t pos)
         vlc_mutex_lock(&p_sys->download.lock_wait);
         p_sys->download.seek = p_sys->playback.segment;
         vlc_cond_signal(&p_sys->download.wait);
+        vlc_mutex_unlock(&p_sys->download.lock_wait);

         /* Wait for download to be finished */
+        vlc_mutex_lock(&p_sys->download.lock_wait);
         msg_Info(s, "seek to segment %d", p_sys->playback.segment);
-        while ((p_sys->download.seek != -1) ||
-	       ((p_sys->download.segment - p_sys->playback.segment < 3) &&
-                (p_sys->download.segment < count)))
+        while (((p_sys->download.seek != -1) ||
+                (p_sys->download.segment - p_sys->playback.segment < 3)) &&
+                (p_sys->download.segment < (count - 6)))
         {
             vlc_cond_wait(&p_sys->download.wait, &p_sys->download.lock_wait);
             if (!vlc_object_alive(s) || s->b_error) break;



More information about the vlc-devel mailing list