Some live streams (eg youtube) don't remove fragments in order to allow
seeking back in time (live + vod).
When gst_m3u8_client_has_next_fragment is called, we are getting wrong fragment
because current_file points in first file of the fragments list resulting in
watching the stream from the beginning again.
This patch sets current_file to nth fragment for live streams, then on
gst_m3u8_client_has_next_fragment will keep up with the live stream.
https://bugzilla.gnome.org/show_bug.cgi?id=753344
This reverts commit 4ca3a22b6b.
The connection-speed=0 is used as a special value in the property
of hlsdemux to mean 'automatic' selection, m3u8.c doesn't need
to know about that as it should be as simple as possible.
So this patch hides this automatic selection documented in hlsdemux
into m3u8 logic and I think the gets harder to understand the code.
It also makes the hlsdemux unit tests work again
https://bugzilla.gnome.org/show_bug.cgi?id=749328
In live situations, it is not uncommon for the current fragment to end
up out of the (updated) play range (lowest/highest sequence). But the next
fragment to play *is* present in the play range.
When advancing, if we can't find the current GstM3U8MediaFile, don't abort
straight away. Instead, look if a GstM3U8MediaFile with the next sequence value
is present, and if so switch to it.
https://bugzilla.gnome.org/show_bug.cgi?id=750028
It's better to just select some random variant playlist instead of stopping,
chances are that it's still continuing to work and we might just have to
select a different variant again later.
We should only refresh the currently selected variant playlist (if any,
otherwise the main playlist), not the main playlist. And only try to
refresh the main playlist if updating the variant playlist fails.
Some servers (Wowza) use the request of the main playlist to create a
"session", which is then part of the URI of the variant playlist and
also the fragments. Refreshing the main playlist would generate a new
session, and the server rate limits that usually. And after a few retries
the server just kicks us out.
Also as a side effect we now use the same downloader for all playlists, so
that we only have 2 instead of 3 connections to the server. And also
previously we just ignored the downloaded data from the main playlist that
the base class gave to us.
When the segment is very short it might be the case that the
typefinding fails and when finishing the segment hlsdemux would
consider the remaining data (pending_buffer) as an encryption
leftover.
This patch fixes it and makes sure an error is properly posted
if typefind failed by refactoring buffer handling to a function
and using it from the data_received and finish_fragment functions.
We also have to update the current_file GList pointer in the M3U playlist
client, otherwise we are just continuing playback from the current position
instead of seeking.
upstream might send buffer lists instead of buffers and hlssink's
probe won't get called and a new segment won't be created when needed.
This patch fixes it by adding a chain_list function to the sink pad
that will just pass through the whole bufferlist if no segment needs
to be requested at the moment or convert the list into buffers to
check the proper timestamp to request the next key-unit that will
start the segment.
https://bugzilla.gnome.org/show_bug.cgi?id=746906
Move the property from subclasses to adaptivedemux, it allows
selecing the percentage of the measured bitrate to be used when
selecting stream bitrates
Allow the playlist-length to accept '0' as a value, indicating
that no segment should be removed from the playlist. This allows
generating playlists to be used as VOD when complete.
Allows to set a bitrate directly instead of measuring it internally
based on the received chunks. The connection-speed was removed from
mssdemux and hlsdemux as it is now in the base class
Add more power to the chunk_received function (renamed to data_received)
and also to the fragment_finish function.
The data_received function must parse/decrypt the data if necessary and
also push it using the new push_buffer function that is exposed now. The
default implementation gets data from the stream adapter (all available)
and pushes it.
The fragment_finish function must also advance the fragment. The default
implementation only advances the fragment.
This allows the subsegment handling in dashdemux to continuously download
the same file from the server instead of stopping at every subsegment
boundary and starting a new request
Optimize loop by moving condition outside of it and reuse the
find_next_fragment function to check if there is next instead of
replicating the same loop
Duration queries can be done a few times per second and would cause
the segment list to be traversed for every one. Caching the duration
prevents that.
The duration values in playlists are approximate only, and for
playlist versions 2 and older they are only rounded integer values.
They cannot be used to timestamp buffers. This resulted in playback
gaps and skips because the actual duration of fragments is slightly
different. The solution is to only set the pts of the very first
buffer processed, not for each fragment.
hlsdemux assumes that seeking is not allowed for live streams,
however seek is possible if there are sufficient fragments in the
manifest. For example the BBC have live streams that contain 2 hours
of fragments.
The seek code for both live and on-demand is common code. The
difference between them is that an offset has to be calculated
for the timecode of the first fragment in the live playlist.
When hlsdemux starts to play a live stream, the possible seek range
is between 0 and A seconds. After some time has passed, the beginning of
the stream will no longer be available in the playlist and the seek
range is between B and C seconds.
Seek range:
start 0 ........... A
later B ........... C
This commit adds code to keep a note of the B and C values
and the highest sequence number it has seen. Every time it updates the
media playlist, it walks the list of fragments, seeing if there is a
fragment with sequence number > highest_seen_sequence. If so, the values
of B and C are updated. The value of B is used when timestamping
buffers.
It also makes sure the seek range is never closer than three fragments
from the end of the playlist - see 6.3.3. "Playing the Playlist file"
of the HLS draft.
https://bugzilla.gnome.org/show_bug.cgi?id=725435
For small amounts some data might be mistyped and it would cause
the pipeline to fail. For example if you have AAC inside mpegts,
for small amounts, the AAC samples would cause the typefinder to
think it is AAC and not mpegts.
https://bugzilla.gnome.org/show_bug.cgi?id=736061
If typefind fails, check to see if the buffer is too short for typefind. If this is the case,
prepend the decrypted buffer to the pending buffer and try again the next time around.
https://bugzilla.gnome.org/show_bug.cgi?id=740458
According to the HLS spec the remainder of the line following
the comma on EXTINF tag is not required. This patch removes
the fake title and saves some bytes on the playlist.
https://bugzilla.gnome.org/show_bug.cgi?id=741096
In gst_hls_demux_get_next_fragment() the next fragment URI gets
stored in next_fragment_uri, but the gst_hls_demux_updates_loop()
can at any time update the playlist, rendering this string invalid.
Therefore, any data (like key, iv, URIs) that is taken from a
GstM3U8Client needs to be copied. In addition, accessing the
internals of a GstM3U8Client requires locking.
https://bugzilla.gnome.org/show_bug.cgi?id=737793
If EOS or ERROR happens before the download loop thread has reached its
g_cond_wait() call, then the g_cond_signal doesn't have any effect and
the download loop thread stucks later.
https://bugzilla.gnome.org/show_bug.cgi?id=735663
The internal pad still keeps its EOS flag and event as it can be assigned
after the flush-start/stop pair is sent. The EOS is assigned from the streaming
thread so this is racy.
To be sure to clear it, it has to be done after setting the source to READY to
be sure that its streaming thread isn't running.
https://bugzilla.gnome.org/show_bug.cgi?id=736012
Previously we only refetched the playlist if downloading a fragment
has failed once. We should also do that if it failed a second or third time,
chances are that the playlist was updated now and contains new URIs.
Instead always use the low bandwith playlist making things go smoother
as the current heuristic is rather set for normal playback, and
currently it does not behave properly.
https://bugzilla.gnome.org/show_bug.cgi?id=734445
Only reset the decryption engine on the first buffer of a fragment,
not again for the second buffer. This fixes corrupting the second
buffer of a fragment.
https://bugzilla.gnome.org/show_bug.cgi?id=731968
This can happen if the playlists have moved due to the variant playlist
now being redirected to another target. This currently only works as long
as the referenced playlists don't change in relation to the variant
playlist, and the new location is purely due to a new path triggered by a
new redirection target of the variant playlist, or a new redirection
target of the playlist itself.
https://bugzilla.gnome.org/show_bug.cgi?id=731164
data does not have to be freed at all here, it's a pointer to
an arbitrary position inside the current line. Also don't reuse
the data variable for anything else, that will cause crashes
in playlists that have the I-frame playlist URI followed by
other attributes.
CID 1212127
Only the first buffer of a fragment has its timestamp set, so only
update the segment.position when pushing those buffers to avoid
having GST_CLOCK_TIME_NONE set to the position
https://bugzilla.gnome.org/show_bug.cgi?id=729364
Otherwise we will never recover from previous errors, and especially
will never start again after a flushing seek if downstream returned
GST_FLOW_FLUSHING to us.
hlsdemux can't rely on the source to push flushes on a seek on ready
as that might not make sense. So always resort to flushing the
internal proxy pads by pushing flush events from the source's src pad.
Also as the seeking is not required anymore, only seek if there is
really a byte range to be used. And store a ref to the source's
src pad to avoid doing get_static_pad for every fragment.
In decryption scenario, a buffer is always stored to be sent later
to wait for more data or EOS to be able to strip the final bytes
if requested. In case an error hapenned this buffer can be ignored
and not pushed downstream.
Handle some more error cases:
1) When the source element fails to go to ready
2) When decryption fails
3) When there is no source to handle a specific URI
4) When the URI is invalid
Set up a message handling function to catch errors from the internal
source and store the last return code to identify error situations
when returning from a fragment download.
Also moves the duration increase to after the download when we
know if it was successful or not
When using the internal source, hlsdemux doesn't know the caps of
the input before adding the pad, so remove the arguments that would
use that as it is always NULL.
And use an specific flag to signal when a pad switch is required.
Using the discont flag is a bad idea now because when a fragment
download fails it will lead to exposing a pad group without any
data, causing decodebin to abort.
When receving EOS from the internal src, increase the current positon
by the fragment duration to allow correct restoring of download position
if the bitrate changes
Use the same properties as uridownloader to keep connections alive
between consecutive fragments downloads.
1) set keep-alive property to true
2) keep the element in READY instead of in NULL
Measure the download bitrate to be able to select
the best playlist.
As the buffers are directly pushed downstream and it
might block. The time is only measured from the download
until the pad push and it is started again after the push
returns.
Now the decryption is done buffer by buffer instead of on the
whole fragment at once. As it expects multiples of 16 bytes a
GstAdapter was added to properly chunk the buffers.
Also the last buffer must be resized depending on the value of the
last byte of the fragment, so hlsdemux always keeps a pending buffer
as it doesn't know if it is the last one yet
The GstElement is directly linked into a ghost pad and
its buffers are pushed as received downstream. This way the
buffers are small enough and not a whole fragment that usually
causes extra latency and makes buffering harder
They are very confusing for people, and more often than not
also just not very accurate. Seeing 'last reviewed: 2005' in
your docs is not very confidence-inspiring. Let's just remove
those comments.
But only add this for non-live playlists. For live playlists we already
have another thread that is periodically updating playlists.
Reason for this is that sometimes downloading a fragment can fail because
the URIs have changed or expired since last time.
Sequence numbers in different playlists are not guaranteed to be the same for the
same position, e.g. fragments could have different durations in different playlists.
In theory we should do exactly the same for live playlists, but unfortunately we can't
because doing this kind of seeking requires the complete playlist since we started
playback. For live playlists the server is however dropping fragments in the beginning
over time and we have no absolute time references.
Recent refactoring causes this code to be called with either a NULL
fragment, or a non NULL fragment. In the former case, we don't have
a buffer. In the latter case, the original code dealing with DISCONT
assumed the buffer was valid. Testing for a NULL buffer here thus
does not seem to change the intent, and fixes:
Coverity 1195147
hlsdemux causes a null pointer dereference if the media playlist
does not contain any media files. The gst_m3u8_client_get_duration
function assumes that demux->client->current->files is valid when
computing duration.
gst_m3u8_client_update needed to be modified to check for the
case of downloading an M3U8 file that doesn't contain any media
files, and returning an error to gsthlsdemux.c
This bug can be reproduced by creating a master m3u8 file that
contains one media playlist that points back to the master m3u8
file. For example create a file called bug725134.m3u8:
#EXTM3U
#EXT-X-VERSION:4
#EXT-X-STREAM-INF:PROGRAM-ID=1, BANDWIDTH=1251135, CODECS="avc1.42001f mp4a.40.2", RESOLUTION=640x352
bug725134.m3u8
https://bugzilla.gnome.org/show_bug.cgi?id=725134
hlsdemux does not check for the '"' character in #EXT-X-STREAM-INF
attributes. The CODECS parameter is an example of an attribute
that might use the '"' symbol and might contain a ',' character
inside this quoted string.
For example: CODECS="avc1.77.30, mp4a.40.2"
hlsdemux does not correctly parse the RESOLUTION attribute, it
assumes that an '=' character is used to delineate the width
and height values, but the HLS RFC states that a 'x' character
must be used as the delimiter between width and height.
https://bugzilla.gnome.org/show_bug.cgi?id=725140
...instead of adding them from the start of playlist every time. This
among other things fixes timestamps for live streams, where the playlist
is some kind of ringbuffer of fragments and thus adding from the beginning
of the playlist will miss the past fragments.
https://bugzilla.gnome.org/show_bug.cgi?id=724983
We now download fragments as fast as possible and push them downstream
while another thread is just responsible for updating live playlists
every now and then.
This simplifies the code a lot and together with the new buffering
mode for adaptive streams in multiqueue makes streams start much faster.
Also simplify threading a bit and hopefully make the GstTask usage safer.
And only afterwards wait until a fragment was played. Otherwise we're keeping
our cache most of the time at "fragments-cache" fragments minus one.
Also allow setting "fragments-cache" to 1 now to start playback even faster.
nettle is used by newer versions of gnutls, while older versions of gnutls
used libgcrypt. Support both for now as not every distro has nettle yet.
nettle is preferred as it is more efficient to use and much smaller.
It only gets the sink flag set when it adds the multifilesink, that
happens in null->ready and it might be too late. Set the flag
explicitly on the constructor.
https://bugzilla.gnome.org/show_bug.cgi?id=711086
This patch fixes three memory leaks in hlsdemux, one that occurs
during normal operation and two that occur during error conditions.
The gst_hls_demux_get_next_fragment function calls
gst_fragment_get_buffer which increments the reference count
on the buffer but gst_hls_demux_get_next_fragment never calls unref on
the buffer. This means that the reference count for each downloaded
fragment never gets to zero and so its memory is never released.
This patch adds a call to gst_buffer_unref after the flags have been
updated on the buffer.
There is a leak-on-error in gst_hls_demux_decrypt_fragment if it fails
to download the key file. If the key fails to download, null is
returned without doing an unref on the encrypted fragment. The
semantics of gst_hls_demux_decrypt_fragment is that it takes ownership
of the encrypted fragment and releases it before returning.
There is a leak-on-error in gst_hls_src_buf_to_utf8_playlist in the
unlikely event that the gst_buffer_map fails. In the "happy path"
operation of gst_hls_src_buf_to_utf8_playlist the buffer gets an unref
before the function returns, therefore the error condition must do the
same.
https://bugzilla.gnome.org/show_bug.cgi?id=710881
On some live HLS streams, gst_hls_demux_switch_playlist causes
assertion failures because it tried to dereference a NULL fragment.
This is because g_queue_peek_tail sometimes was returning NULL and
this case was not being checked.
This patch does two things:
* move the g_queue_peek_tail inside the semaphore protection
* check if q_queue_peek_tail returns NULL
https://bugzilla.gnome.org/show_bug.cgi?id=708849
Every encrypted fragment will be a multiple of 128 bits, the last byte
contains the number of bytes that were added as padding in the end
and should be removed.
https://bugzilla.gnome.org/show_bug.cgi?id=701673
When using an HLS encrypted stream, an assertion failure is thrown:
(gst-launch-1.0:31028): GLib-GObject-WARNING **: cannot register
existing type `GstFragment'
(gst-launch-1.0:31028): GLib-CRITICAL **: g_once_init_leave: assertion
`result != 0' failed
Eventually tracked this down to the call gst_fragment_new()
in function gst_hls_demux_decrypt_fragment.
The GstFragment class is defined in ext/hls/gstfragment.c and in
gst-libs/gst/uridownloader/gstfragment.c. Having two class definitions
with the same name causes the assert failure when trying to allocate
GstFragment. Deleting the version from hls and editing the
Makefile.am solves this assert failure.
https://bugzilla.gnome.org/show_bug.cgi?id=704555