Jordan Yelloz f5634c2aac gstmediasourcetrackbuffer: Improved buffered ranges calculation
Now when the buffered list is requested, the tolerance for merging two ranges
when there's a small gap between them is MAX(0.1sec, max frame duration * 2).

Previously it was hardcoded to 0.01sec. The specification suggests that it
could be something like the max frame duration * 2.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/8512>
2025-03-26 21:44:13 +00:00

363 lines
9.5 KiB
C

/* GStreamer
*
* SPDX-License-Identifier: LGPL-2.1
*
* Copyright (C) 2022, 2023 Collabora Ltd.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "gstmediasourcetrackbuffer-private.h"
#include "gstmediasourcesamplemap-private.h"
#include "gstmediasource.h"
#include "gstmselogging-private.h"
typedef struct
{
gboolean enabled;
GstClockTime group_start;
GstClockTime group_end;
GstClockTimeDiff offset;
GstClockTime last_dts;
GstClockTime last_duration;
} Timestamps;
struct _GstMediaSourceTrackBuffer
{
GstObject parent_instance;
GstMediaSourceSampleMap *samples;
Timestamps timestamps;
guint eos;
guint32 master_cookie;
GCond new_data_cond;
GMutex new_data_mutex;
};
#define g_array_new_ranges() \
(g_array_new (TRUE, FALSE, sizeof (GstMediaSourceRange)))
#define NEW_DATA_LOCK(a) (g_mutex_lock (&a->new_data_mutex))
#define NEW_DATA_UNLOCK(a) (g_mutex_unlock (&a->new_data_mutex))
#define NEW_DATA_SIGNAL(a) (g_cond_signal (&a->new_data_cond))
#define NEW_DATA_WAIT(a) (g_cond_wait (&a->new_data_cond, &a->new_data_mutex))
#define NEW_DATA_WAIT_UNTIL(a, d) \
(g_cond_wait_until (&a->new_data_cond, &a->new_data_mutex, d))
static void timestamps_init (Timestamps * self, gboolean enabled);
static void timestamps_process (Timestamps * self, GstSample * sample);
G_DEFINE_TYPE (GstMediaSourceTrackBuffer, gst_media_source_track_buffer,
GST_TYPE_OBJECT);
static void
invalidate_cookie (GstMediaSourceTrackBuffer * self)
{
self->master_cookie++;
}
GstMediaSourceTrackBuffer *
gst_media_source_track_buffer_new (void)
{
return gst_object_ref_sink (g_object_new (GST_TYPE_MEDIA_SOURCE_TRACK_BUFFER,
NULL));
}
static void
gst_media_source_track_buffer_finalize (GObject * object)
{
GstMediaSourceTrackBuffer *self = GST_MEDIA_SOURCE_TRACK_BUFFER (object);
gst_object_unref (self->samples);
g_cond_clear (&self->new_data_cond);
g_mutex_clear (&self->new_data_mutex);
G_OBJECT_CLASS (gst_media_source_track_buffer_parent_class)->finalize
(object);
}
static void
gst_media_source_track_buffer_class_init (GstMediaSourceTrackBufferClass *
klass)
{
GObjectClass *oclass = G_OBJECT_CLASS (klass);
oclass->finalize = GST_DEBUG_FUNCPTR (gst_media_source_track_buffer_finalize);
}
static void
gst_media_source_track_buffer_init (GstMediaSourceTrackBuffer * self)
{
self->samples = gst_media_source_sample_map_new ();
self->eos = FALSE;
self->master_cookie = 0;
timestamps_init (&self->timestamps, FALSE);
g_cond_init (&self->new_data_cond);
g_mutex_init (&self->new_data_mutex);
}
void
gst_media_source_track_buffer_process_init_segment (GstMediaSourceTrackBuffer
* self, gboolean sequence_mode)
{
NEW_DATA_LOCK (self);
timestamps_init (&self->timestamps, sequence_mode);
NEW_DATA_UNLOCK (self);
}
void
gst_media_source_track_buffer_set_group_start (GstMediaSourceTrackBuffer
* self, GstClockTime group_start)
{
g_return_if_fail (GST_IS_MEDIA_SOURCE_TRACK_BUFFER (self));
if (self->timestamps.enabled) {
self->timestamps.group_start = group_start;
}
}
void
gst_media_source_track_buffer_add (GstMediaSourceTrackBuffer * self,
GstSample * sample)
{
g_return_if_fail (GST_IS_MEDIA_SOURCE_TRACK_BUFFER (self));
g_return_if_fail (GST_IS_SAMPLE (sample));
NEW_DATA_LOCK (self);
timestamps_process (&self->timestamps, sample);
gst_media_source_sample_map_add (self->samples, sample);
invalidate_cookie (self);
NEW_DATA_SIGNAL (self);
NEW_DATA_UNLOCK (self);
}
gsize
gst_media_source_track_buffer_remove_range (GstMediaSourceTrackBuffer * self,
GstClockTime earliest, GstClockTime latest)
{
NEW_DATA_LOCK (self);
gsize size = gst_media_source_sample_map_remove_range (self->samples,
earliest, latest);
invalidate_cookie (self);
NEW_DATA_SIGNAL (self);
NEW_DATA_UNLOCK (self);
return size;
}
static gboolean
is_eos (GstMediaSourceTrackBuffer * self)
{
return g_atomic_int_get (&self->eos);
}
void
gst_media_source_track_buffer_eos (GstMediaSourceTrackBuffer * self)
{
g_return_if_fail (GST_IS_MEDIA_SOURCE_TRACK_BUFFER (self));
NEW_DATA_LOCK (self);
g_atomic_int_set (&self->eos, TRUE);
NEW_DATA_SIGNAL (self);
NEW_DATA_UNLOCK (self);
}
gboolean
gst_media_source_track_buffer_is_eos (GstMediaSourceTrackBuffer * self)
{
g_return_val_if_fail (GST_IS_MEDIA_SOURCE_TRACK_BUFFER (self), FALSE);
return is_eos (self);
}
void
gst_media_source_track_buffer_await_new_data_until (GstMediaSourceTrackBuffer *
self, gint64 deadline)
{
NEW_DATA_LOCK (self);
NEW_DATA_WAIT_UNTIL (self, deadline);
NEW_DATA_UNLOCK (self);
}
gint
gst_media_source_track_buffer_get_size (GstMediaSourceTrackBuffer * self)
{
g_return_val_if_fail (GST_IS_MEDIA_SOURCE_TRACK_BUFFER (self), 0);
return gst_media_source_sample_map_get_size (self->samples);
}
GstClockTime
gst_media_source_track_buffer_get_highest_end_time (GstMediaSourceTrackBuffer
* self)
{
g_return_val_if_fail (GST_IS_MEDIA_SOURCE_TRACK_BUFFER (self),
GST_CLOCK_TIME_NONE);
return gst_media_source_sample_map_get_highest_end_time (self->samples);
}
typedef struct
{
GArray *ranges;
GstMediaSourceRange current_range;
GstClockTime max_duration;
} GetRangesAccumulator;
static gboolean
get_ranges_fold (const GValue * item, GetRangesAccumulator * acc,
gpointer user_data)
{
GstSample *sample = gst_value_get_sample (item);
GstBuffer *buffer = gst_sample_get_buffer (sample);
GstClockTime start = GST_BUFFER_PTS (buffer);
GstClockTime end = start + GST_BUFFER_DURATION (buffer);
if (GST_BUFFER_DURATION_IS_VALID (buffer)) {
acc->max_duration = MAX (acc->max_duration, GST_BUFFER_DURATION (buffer));
}
GstMediaSourceRange *range = &acc->current_range;
if (!GST_CLOCK_TIME_IS_VALID (acc->current_range.start)) {
acc->current_range.start = start;
}
if (!GST_CLOCK_TIME_IS_VALID (acc->current_range.end)) {
acc->current_range.end = end;
}
GstClockTime gap_tolerance = MAX (GST_SECOND / 10, acc->max_duration * 2);
GstClockTime gap = MAX (0, GST_CLOCK_DIFF (range->end, start));
if (range->end == 0 || gap <= gap_tolerance) {
range->end = end;
return TRUE;
}
g_array_append_val (acc->ranges, *range);
range->start = start;
range->end = end;
return TRUE;
}
GArray *
gst_media_source_track_buffer_get_ranges (GstMediaSourceTrackBuffer * self)
{
GetRangesAccumulator acc = {
.ranges = g_array_new_ranges (),
.max_duration = 0,
.current_range = {.start = GST_CLOCK_TIME_NONE,.end = GST_CLOCK_TIME_NONE},
};
/* *INDENT-OFF* */
GstIterator *iter = gst_media_source_sample_map_iter_samples_by_pts (
self->samples,
&self->new_data_mutex,
&self->master_cookie
);
/* *INDENT-ON* */
while (gst_iterator_fold (iter, (GstIteratorFoldFunction) get_ranges_fold,
(GValue *) & acc, NULL) == GST_ITERATOR_RESYNC) {
gst_iterator_resync (iter);
g_clear_pointer (&acc.ranges, g_array_unref);
acc.ranges = g_array_new_ranges ();
acc.max_duration = 0;
acc.current_range.start = GST_CLOCK_TIME_NONE;
acc.current_range.end = GST_CLOCK_TIME_NONE;
}
gst_iterator_free (iter);
if (!GST_CLOCK_TIME_IS_VALID (acc.current_range.start)) {
acc.current_range.start = 0;
}
if (!GST_CLOCK_TIME_IS_VALID (acc.current_range.end)) {
acc.current_range.end = 0;
}
if (acc.current_range.end > 0) {
g_array_append_val (acc.ranges, acc.current_range);
}
return acc.ranges;
}
static void
timestamps_init (Timestamps * self, gboolean enabled)
{
self->enabled = enabled;
self->group_start = GST_CLOCK_TIME_NONE;
self->group_end = GST_CLOCK_TIME_NONE;
self->offset = 0;
self->last_dts = 0;
self->last_duration = 0;
}
static void
timestamps_process (Timestamps * self, GstSample * sample)
{
if (!self->enabled) {
return;
}
GstBuffer *buffer = gst_sample_get_buffer (sample);
GstClockTime duration = GST_BUFFER_DURATION (buffer);
GstClockTime pts = 0;
GstClockTime dts = 0;
if (GST_CLOCK_TIME_IS_VALID (self->group_start)) {
self->offset = self->group_start - pts;
self->group_end = self->group_start;
self->group_start = GST_CLOCK_TIME_NONE;
}
if (self->offset != 0) {
pts += self->offset;
dts += self->offset;
}
GstClockTime end_pts = pts + duration;
self->last_dts = dts;
self->last_duration = duration;
if (GST_CLOCK_TIME_IS_VALID (self->group_end)) {
self->group_end = MAX (self->group_end, end_pts);
}
self->offset = end_pts;
GST_BUFFER_PTS (buffer) = pts;
GST_BUFFER_DTS (buffer) = dts;
}
gsize
gst_media_source_track_buffer_get_storage_size (GstMediaSourceTrackBuffer *
self)
{
g_return_val_if_fail (GST_IS_MEDIA_SOURCE_TRACK_BUFFER (self), 0);
return gst_media_source_sample_map_get_storage_size (self->samples);
}
GstIterator *
gst_media_source_track_buffer_iter_samples (GstMediaSourceTrackBuffer * self)
{
return gst_media_source_sample_map_iter_samples_by_dts (self->samples,
&self->new_data_mutex, &self->master_cookie);
}