[vlc-commits] [Git][videolan/vlc][master] 3 commits: sout: hls: keep up segmentation with stream time

Steve Lhomme (@robUx4) gitlab at videolan.org
Mon Sep 30 14:31:56 UTC 2024



Steve Lhomme pushed to branch master at VideoLAN / VLC


Commits:
d981176d by Alaric Senat at 2024-09-30T13:51:48+00:00
sout: hls: keep up segmentation with stream time

Allow multiple segments to be output by the same pass.
This is mainly to handle parallel tracks that differ in segment sizes,
if one has its muxer outputting very small segments against a track with
the maximal segment size, the small segments must be output at a faster
rate.

This work is in preparation of the I-frame based segmentation where this
behavior will likely exist.

- - - - -
9f3b4e6e by Alaric Senat at 2024-09-30T13:51:48+00:00
sout: hls: implement I-frame segmentation

This patch implements a best effort algorithm to try and start segments
with an I-Frame. While optional in the RFC, this guarantees sane seeking
points and no artifacts on the first connection of clients in the middle
of the stream.

Obviously, the input of the module should be a feed that has a GOP
generally smaller than the requested segment size, otherwise, the
algorithm will fall back to the old behavior and create a segment that
isn't self decodable.

This is still preparation work for the new Chromecast backend, in
particular to support seeking in the exposed media window.

Refs #27391

- - - - -
e759f7e7 by Alaric Senat at 2024-09-30T13:51:48+00:00
sout: hls: signal user when segments aren't self decodable

Segments not starting with a sync point are legal in the HLS ref.
However, using them as a seek point is hazardous. Warn the user that
their stream might have issues fixable by adapting the encoding
parameters.

- - - - -


1 changed file:

- modules/stream_out/hls/hls.c


Changes:

=====================================
modules/stream_out/hls/hls.c
=====================================
@@ -96,6 +96,11 @@ typedef struct hls_playlist
 
     bool ended;
 
+    /**
+     * Total duration of the muxed data.
+     */
+    vlc_tick_t muxed_duration;
+
     struct vlc_list node;
 } hls_playlist_t;
 
@@ -146,6 +151,10 @@ typedef struct
     struct hls_storage *manifest;
     httpd_url_t *http_manifest;
 
+    /**
+     * Global advancement of the stream in media time.
+     */
+    vlc_tick_t elapsed_stream_time;
     vlc_tick_t first_pcr;
 
     size_t current_memory_cached;
@@ -471,23 +480,42 @@ static hls_block_chain_t ExtractCommonSegment(hls_block_chain_t *muxed_output,
 {
     hls_block_chain_t segment = {.begin = muxed_output->begin};
 
+    vlc_tick_t gop_length = 0;
     block_t *prev = NULL;
+    block_t *segment_end = NULL;
     for (block_t *it = muxed_output->begin; it != NULL; it = it->p_next)
     {
-        if (segment.length + it->i_length > max_segment_length)
+        if (it->i_flags & BLOCK_FLAG_HEADER)
         {
-            muxed_output->begin = it;
-
-            if (prev != NULL)
-                prev->p_next = NULL;
-            return segment;
+            segment_end = prev;
+            segment.length += gop_length;
+            gop_length = 0;
         }
-        segment.length += it->i_length;
-        muxed_output->length -= it->i_length;
+        if (segment.length + gop_length + it->i_length > max_segment_length)
+        {
+            if (segment_end == NULL)
+            {
+                segment_end = prev;
+                segment.length += gop_length;
+                gop_length = 0;
+            }
+            break;
+        }
+        gop_length += it->i_length;
         prev = it;
     }
 
-    hls_block_chain_Reset(muxed_output);
+    if (segment_end != NULL)
+    {
+        muxed_output->begin = segment_end->p_next;
+        segment_end->p_next = NULL;
+        muxed_output->length -= segment.length;
+    }
+    else
+    {
+        segment.length = gop_length;
+        hls_block_chain_Reset(muxed_output);
+    }
     return segment;
 }
 
@@ -522,6 +550,14 @@ static hls_block_chain_t ExtractSegment(hls_playlist_t *playlist)
     return ExtractCommonSegment(&playlist->muxed_output, seglen);
 }
 
+static bool IsSegmentSelfDecodable(const hls_block_chain_t *segment)
+{
+    if (segment->begin == NULL)
+        return false;
+
+    return segment->begin->i_flags & BLOCK_FLAG_HEADER;
+}
+
 static int ExtractAndAddSegment(hls_playlist_t *playlist,
                                 sout_stream_sys_t *sys)
 {
@@ -536,6 +572,8 @@ static int ExtractAndAddSegment(hls_playlist_t *playlist,
             hls_storage_GetSize(to_be_removed->storage);
     }
 
+    const bool self_decodable = IsSegmentSelfDecodable(&segment);
+    const vlc_tick_t length = segment.length;
     const int status = hls_segment_queue_NewSegment(
         &playlist->segments, segment.begin, segment.length);
     if (unlikely(status != VLC_SUCCESS))
@@ -545,6 +583,25 @@ static int ExtractAndAddSegment(hls_playlist_t *playlist,
                   playlist->segments.total_segments + 1);
         return status;
     }
+    playlist->muxed_duration += length;
+
+    if (!self_decodable)
+    {
+        vlc_warning(
+            playlist->logger,
+            "Segment '%u' does not start with a synchronization frame. It will "
+            "not be decodable on its own and will likely fail as a seek point.",
+            playlist->segments.total_segments);
+        if (playlist->type != HLS_PLAYLIST_TYPE_WEBVTT)
+        {
+            vlc_warning(
+                playlist->logger,
+                "It is probably due to a GOP being too large to fit in %" PRIi64
+                "s segments. Please adjust your encoding parameters or set a "
+                "larger segment size.",
+                SEC_FROM_VLC_TICK(playlist->config->segment_length));
+        }
+    }
 
     vlc_debug(playlist->logger,
               "Segment '%u' created",
@@ -613,8 +670,14 @@ static ssize_t AccessOutWrite(sout_access_out_t *access, block_t *block)
     {
         hls_playlists_foreach (it)
         {
-            if (ExtractAndAddSegment(it, sys) != VLC_SUCCESS)
-                return -1;
+            while (IsSegmentReady(it->type,
+                                  &it->muxed_output,
+                                  sys->config.segment_length) &&
+                   it->muxed_duration < sys->elapsed_stream_time)
+            {
+                if (ExtractAndAddSegment(it, sys) != VLC_SUCCESS)
+                    return -1;
+            }
         }
     }
     return size;
@@ -664,7 +727,7 @@ static sout_mux_t *CreatePlaylistMuxer(sout_access_out_t *access,
     switch(type)
     {
         case HLS_PLAYLIST_TYPE_TS:
-            return sout_MuxNew(access, "ts");
+            return sout_MuxNew(access, "ts{use-key-frames}");
         case HLS_PLAYLIST_TYPE_WEBVTT:
             return CreateSubtitleSegmenter(access, config);
     }
@@ -692,6 +755,7 @@ static hls_playlist_t *CreatePlaylist(sout_stream_t *stream,
     playlist->type = type;
     playlist->config = &sys->config;
     playlist->ended = false;
+    playlist->muxed_duration = 0;
 
     playlist->url = FormatPlaylistManifestURL(playlist);
     if (unlikely(playlist->url == NULL))
@@ -904,14 +968,15 @@ static void SetPCR(sout_stream_t *stream, vlc_tick_t pcr)
         return;
     }
 
-    const vlc_tick_t stream_time = pcr - sys->first_pcr;
+    sys->elapsed_stream_time = pcr - sys->first_pcr;
     const hls_playlist_t *playlist;
     vlc_list_foreach_const (playlist, &sys->media_playlists, node)
     {
         if (playlist->type != HLS_PLAYLIST_TYPE_WEBVTT)
             continue;
 
-        hls_sub_segmenter_SignalStreamUpdate(playlist->mux, stream_time);
+        hls_sub_segmenter_SignalStreamUpdate(playlist->mux,
+                                             sys->elapsed_stream_time);
     }
 }
 
@@ -1054,6 +1119,7 @@ static int Open(vlc_object_t *this)
     vlc_list_init(&sys->variant_playlists);
     vlc_list_init(&sys->media_playlists);
 
+    sys->elapsed_stream_time = 0;
     sys->first_pcr = VLC_TICK_INVALID;
 
     sys->current_memory_cached = 0;



View it on GitLab: https://code.videolan.org/videolan/vlc/-/compare/f5d6a8e1bd6bb925888b48bd9593eb0ab74f6230...e759f7e7f3e543a9831d599a9e24d3a0ea9408e5

-- 
View it on GitLab: https://code.videolan.org/videolan/vlc/-/compare/f5d6a8e1bd6bb925888b48bd9593eb0ab74f6230...e759f7e7f3e543a9831d599a9e24d3a0ea9408e5
You're receiving this email because of your account on code.videolan.org.


VideoLAN code repository instance


More information about the vlc-commits mailing list