gstreamer/subprojects/gst-plugins-bad/gst-libs/gst/mse/gstmediasourcesamplemap.c

552 lines
16 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 "gstmediasourcesamplemap-private.h"
#include "gstmselogging-private.h"
struct _GstMediaSourceSampleMap
{
GstObject parent_instance;
GHashTable *samples;
GSequence *samples_by_dts;
GSequence *samples_by_pts;
gsize storage_size;
};
G_DEFINE_TYPE (GstMediaSourceSampleMap, gst_media_source_sample_map,
GST_TYPE_OBJECT);
static gint compare_pts (GstSample * a, GstSample * b, gpointer user_data);
static GSequenceIter *find_sample_containing_dts (GstMediaSourceSampleMap *
self, GstClockTime dts);
static GSequenceIter *find_sample_containing_pts (GstMediaSourceSampleMap *
self, GstClockTime pts);
static GSequenceIter *find_sequentially (GSequence * sequence, gpointer item);
static inline GstClockTime
sample_dts (GstSample * sample)
{
return GST_BUFFER_DTS (gst_sample_get_buffer (sample));
}
static inline GstClockTime
sample_pts (GstSample * sample)
{
return GST_BUFFER_PTS (gst_sample_get_buffer (sample));
}
static gsize
sample_buffer_size (GstSample * sample)
{
return gst_buffer_get_size (gst_sample_get_buffer (sample));
}
static gboolean
iter_is_delta_unit (GSequenceIter * iter)
{
if (g_sequence_iter_is_end (iter)) {
return TRUE;
}
GstSample *sample = g_sequence_get (iter);
GstBuffer *buffer = gst_sample_get_buffer (sample);
return GST_BUFFER_FLAG_IS_SET (buffer, GST_BUFFER_FLAG_DELTA_UNIT);
}
static gint
compare_dts (GstSample * a, GstSample * b, gpointer user_data)
{
GstClockTime a_dts = sample_dts (a);
GstClockTime b_dts = sample_dts (b);
if (a_dts == b_dts) {
return compare_pts (a, b, user_data);
}
return a_dts > b_dts ? +1 : -1;
}
static gint
compare_pts (GstSample * a, GstSample * b, gpointer user_data)
{
GstClockTime a_pts = sample_pts (a);
GstClockTime b_pts = sample_pts (b);
if (a_pts == b_pts) {
return 0;
}
return a_pts > b_pts ? +1 : -1;
}
static inline void
remove_sample (GSequence * sequence, GCompareDataFunc compare,
GstSample * sample, GstMediaSourceSampleMap * self)
{
GSequenceIter *match = g_sequence_lookup (sequence, sample, compare, self);
if (match == NULL) {
return;
}
g_sequence_remove (match);
}
GstMediaSourceSampleMap *
gst_media_source_sample_map_new (void)
{
return gst_object_ref_sink (g_object_new (GST_TYPE_MEDIA_SOURCE_SAMPLE_MAP,
NULL));
}
static void
gst_media_source_sample_map_finalize (GObject * object)
{
GstMediaSourceSampleMap *self = GST_MEDIA_SOURCE_SAMPLE_MAP (object);
g_sequence_free (self->samples_by_dts);
g_sequence_free (self->samples_by_pts);
g_hash_table_unref (self->samples);
G_OBJECT_CLASS (gst_media_source_sample_map_parent_class)->finalize (object);
}
static void
gst_media_source_sample_map_class_init (GstMediaSourceSampleMapClass * klass)
{
GObjectClass *oclass = G_OBJECT_CLASS (klass);
oclass->finalize = GST_DEBUG_FUNCPTR (gst_media_source_sample_map_finalize);
}
static void
gst_media_source_sample_map_init (GstMediaSourceSampleMap * self)
{
self->samples = g_hash_table_new_full (g_direct_hash, g_direct_equal,
(GDestroyNotify) gst_sample_unref, NULL);
self->samples_by_dts = g_sequence_new ((GDestroyNotify) gst_sample_unref);
self->samples_by_pts = g_sequence_new ((GDestroyNotify) gst_sample_unref);
}
void
gst_media_source_sample_map_add (GstMediaSourceSampleMap * self,
GstSample * sample)
{
g_return_if_fail (GST_IS_MEDIA_SOURCE_SAMPLE_MAP (self));
g_return_if_fail (GST_IS_SAMPLE (sample));
GstBuffer *buffer = gst_sample_get_buffer (sample);
g_return_if_fail (GST_BUFFER_DTS_IS_VALID (buffer));
g_return_if_fail (GST_BUFFER_PTS_IS_VALID (buffer));
g_return_if_fail (GST_BUFFER_DURATION_IS_VALID (buffer));
if (g_hash_table_contains (self->samples, sample)) {
return;
}
g_hash_table_add (self->samples, gst_sample_ref (sample));
g_sequence_insert_sorted (self->samples_by_dts, gst_sample_ref (sample),
(GCompareDataFunc) compare_dts, self);
g_sequence_insert_sorted (self->samples_by_pts, gst_sample_ref (sample),
(GCompareDataFunc) compare_pts, self);
self->storage_size += gst_buffer_get_size (buffer);
GST_TRACE_OBJECT (self, "new storage size=%" G_GSIZE_FORMAT,
self->storage_size);
}
void
gst_media_source_sample_map_remove (GstMediaSourceSampleMap * self,
GstSample * sample)
{
g_return_if_fail (GST_IS_MEDIA_SOURCE_SAMPLE_MAP (self));
g_return_if_fail (GST_IS_SAMPLE (sample));
if (!g_hash_table_contains (self->samples, sample)) {
return;
}
gsize buffer_size = sample_buffer_size (sample);
remove_sample (self->samples_by_dts, (GCompareDataFunc) compare_dts, sample,
self);
remove_sample (self->samples_by_pts, (GCompareDataFunc) compare_pts, sample,
self);
g_hash_table_remove (self->samples, sample);
self->storage_size -= MIN (buffer_size, self->storage_size);
}
gboolean
gst_media_source_sample_map_contains (GstMediaSourceSampleMap * self,
GstSample * sample)
{
g_return_val_if_fail (GST_IS_MEDIA_SOURCE_SAMPLE_MAP (self), FALSE);
return g_hash_table_contains (self->samples, sample);
}
gsize
gst_media_source_sample_map_remove_range (GstMediaSourceSampleMap * self,
GstClockTime earliest, GstClockTime latest)
{
g_return_val_if_fail (GST_IS_MEDIA_SOURCE_SAMPLE_MAP (self), 0);
g_return_val_if_fail (earliest <= latest, 0);
GSequenceIter *start_by_dts = find_sample_containing_dts (self, earliest);
GSequenceIter *end_by_dts = find_sample_containing_dts (self, latest);
GstSample *start = g_sequence_get (start_by_dts);
GstSample *end = g_sequence_get (end_by_dts);
GstClockTime start_time =
start == NULL ? GST_CLOCK_TIME_NONE : sample_dts (start);
GstClockTime end_time = end == NULL ? start_time : sample_dts (end);
GST_TRACE_OBJECT (self, "remove range [%" GST_TIMEP_FORMAT ",%"
GST_TIMEP_FORMAT ")", &start_time, &end_time);
GList *to_remove = NULL;
while (g_sequence_iter_compare (start_by_dts, end_by_dts) < 1) {
GstSample *sample = g_sequence_get (start_by_dts);
to_remove = g_list_prepend (to_remove, sample);
start_by_dts = g_sequence_iter_next (start_by_dts);
}
gsize bytes_removed = 0;
for (GList * iter = to_remove; iter != NULL; iter = g_list_next (iter)) {
GstSample *sample = iter->data;
bytes_removed += sample_buffer_size (sample);
gst_media_source_sample_map_remove (self, sample);
}
g_list_free (to_remove);
GST_TRACE_OBJECT (self, "removed=%" G_GSIZE_FORMAT "B, latest=%"
GST_TIMEP_FORMAT, bytes_removed, &latest);
return bytes_removed;
}
gsize
gst_media_source_sample_map_remove_range_from_start (GstMediaSourceSampleMap
* self, GstClockTime latest_dts)
{
return gst_media_source_sample_map_remove_range (self, 0, latest_dts);
}
gsize
gst_media_source_sample_map_remove_range_from_end (GstMediaSourceSampleMap
* self, GstClockTime earliest_dts)
{
return gst_media_source_sample_map_remove_range (self, earliest_dts,
GST_CLOCK_TIME_NONE);
}
GstClockTime
gst_media_source_sample_map_get_highest_end_time (GstMediaSourceSampleMap *
self)
{
g_return_val_if_fail (GST_IS_MEDIA_SOURCE_SAMPLE_MAP (self),
GST_CLOCK_TIME_NONE);
GSequenceIter *iter = g_sequence_get_end_iter (self->samples_by_pts);
iter = g_sequence_iter_prev (iter);
if (g_sequence_iter_is_begin (iter)) {
return GST_CLOCK_TIME_NONE;
}
GstSample *sample = g_sequence_get (iter);
GstBuffer *buffer = gst_sample_get_buffer (sample);
g_return_val_if_fail (GST_BUFFER_PTS_IS_VALID (buffer), GST_CLOCK_TIME_NONE);
g_return_val_if_fail (GST_BUFFER_DURATION_IS_VALID (buffer),
GST_CLOCK_TIME_NONE);
return GST_BUFFER_PTS (buffer) + GST_BUFFER_DURATION (buffer);
}
guint
gst_media_source_sample_map_get_size (GstMediaSourceSampleMap * self)
{
g_return_val_if_fail (GST_IS_MEDIA_SOURCE_SAMPLE_MAP (self), 0);
return g_hash_table_size (self->samples);
}
gsize
gst_media_source_sample_map_get_storage_size (GstMediaSourceSampleMap * self)
{
g_return_val_if_fail (GST_IS_MEDIA_SOURCE_SAMPLE_MAP (self), 0);
return self->storage_size;
}
static inline gboolean
sample_contains_dts (GstSample * sample, GstClockTime dts)
{
GstBuffer *buffer = gst_sample_get_buffer (sample);
g_return_val_if_fail (GST_BUFFER_DURATION_IS_VALID (buffer), FALSE);
g_return_val_if_fail (GST_BUFFER_DTS_IS_VALID (buffer), FALSE);
g_return_val_if_fail (GST_CLOCK_TIME_IS_VALID (dts), FALSE);
GstClockTime end = GST_BUFFER_DTS (buffer) + GST_BUFFER_DURATION (buffer);
return dts <= end;
}
static inline gboolean
sample_contains_pts (GstSample * sample, GstClockTime pts)
{
GstBuffer *buffer = gst_sample_get_buffer (sample);
g_return_val_if_fail (GST_BUFFER_DURATION_IS_VALID (buffer), FALSE);
g_return_val_if_fail (GST_BUFFER_PTS_IS_VALID (buffer), FALSE);
g_return_val_if_fail (GST_CLOCK_TIME_IS_VALID (pts), FALSE);
GstClockTime end = GST_BUFFER_PTS (buffer) + GST_BUFFER_DURATION (buffer);
return pts <= end;
}
static GSequenceIter *
find_sequentially (GSequence * sequence, gpointer item)
{
GSequenceIter *iter = g_sequence_get_begin_iter (sequence);
for (; !g_sequence_iter_is_end (iter); iter = g_sequence_iter_next (iter)) {
gpointer current = g_sequence_get (iter);
if (current == item) {
return iter;
}
}
return iter;
}
static GSequenceIter *
find_sample_containing_dts (GstMediaSourceSampleMap * self, GstClockTime dts)
{
if (dts == 0) {
return g_sequence_get_begin_iter (self->samples_by_dts);
}
if (dts == GST_CLOCK_TIME_NONE) {
return g_sequence_get_end_iter (self->samples_by_dts);
}
GSequenceIter *iter = g_sequence_get_begin_iter (self->samples_by_dts);
while (!g_sequence_iter_is_end (iter)) {
GstSample *sample = g_sequence_get (iter);
if (sample_contains_dts (sample, dts)) {
return iter;
}
iter = g_sequence_iter_next (iter);
}
return iter;
}
static GSequenceIter *
find_sample_containing_pts (GstMediaSourceSampleMap * self, GstClockTime pts)
{
if (pts == 0) {
return g_sequence_get_begin_iter (self->samples_by_pts);
}
if (pts == GST_CLOCK_TIME_NONE) {
return g_sequence_get_end_iter (self->samples_by_pts);
}
GSequenceIter *iter = g_sequence_get_begin_iter (self->samples_by_pts);
while (!g_sequence_iter_is_end (iter)) {
GstSample *sample = g_sequence_get (iter);
if (sample_contains_pts (sample, pts)) {
return iter;
}
iter = g_sequence_iter_next (iter);
}
return iter;
}
static GSequenceIter *
find_previous_non_delta_unit (GstMediaSourceSampleMap * self,
GSequenceIter * iter)
{
while (!g_sequence_iter_is_begin (iter)) {
if (!iter_is_delta_unit (iter)) {
GST_TRACE_OBJECT (self, "found valid sample");
return iter;
}
iter = g_sequence_iter_prev (iter);
}
GST_TRACE_OBJECT (self, "rolled back to the first sample");
return iter;
}
static GSequenceIter *
gst_media_source_sample_map_iter_starting_dts (GstMediaSourceSampleMap * self,
GstClockTime start_dts)
{
g_return_val_if_fail (GST_IS_MEDIA_SOURCE_SAMPLE_MAP (self), NULL);
g_return_val_if_fail (GST_CLOCK_TIME_IS_VALID (start_dts), NULL);
GSequenceIter *iter = find_sample_containing_dts (self, start_dts);
return find_previous_non_delta_unit (self, iter);
}
static GSequenceIter *
gst_media_source_sample_map_iter_starting_pts (GstMediaSourceSampleMap
* self, GstClockTime start_pts)
{
g_return_val_if_fail (GST_IS_MEDIA_SOURCE_SAMPLE_MAP (self), NULL);
g_return_val_if_fail (GST_CLOCK_TIME_IS_VALID (start_pts), NULL);
GSequenceIter *iter = find_sample_containing_pts (self, start_pts);
return find_previous_non_delta_unit (self, iter);
}
typedef struct _SampleMapIterator SampleMapIterator;
struct _SampleMapIterator
{
GstIterator iterator;
GstMediaSourceSampleMap *map;
/* *INDENT-OFF* */
GstClockTime (*timestamp_func) (GstSample *);
GSequenceIter *(*resync_locator_func) (SampleMapIterator *);
/* *INDENT-ON* */
GstClockTime start_time;
GstClockTime current_time;
GSequenceIter *current_iter;
GstSample *current_sample;
};
static void
iter_copy (const SampleMapIterator * it, SampleMapIterator * copy)
{
copy->map = gst_object_ref (it->map);
copy->timestamp_func = it->timestamp_func;
copy->resync_locator_func = it->resync_locator_func;
copy->current_time = it->current_time;
copy->start_time = it->start_time;
copy->current_iter = it->current_iter;
copy->current_sample = gst_sample_ref (it->current_sample);
}
static GSequenceIter *
iter_find_resync_point_dts (SampleMapIterator * it)
{
if (it->current_sample) {
GSequenceIter *iter =
find_sequentially (it->map->samples_by_dts, it->current_sample);
if (!g_sequence_iter_is_end (iter)) {
return g_sequence_iter_next (iter);
}
}
return gst_media_source_sample_map_iter_starting_dts (it->map,
it->current_time);
}
static GSequenceIter *
iter_find_resync_point_pts (SampleMapIterator * it)
{
if (it->current_sample) {
GSequenceIter *iter =
find_sequentially (it->map->samples_by_pts, it->current_sample);
if (!g_sequence_iter_is_end (iter)) {
return g_sequence_iter_next (iter);
}
}
return gst_media_source_sample_map_iter_starting_pts (it->map,
it->current_time);
}
static GstIteratorResult
iter_next (SampleMapIterator * it, GValue * result)
{
if (g_sequence_iter_is_end (it->current_iter)) {
return GST_ITERATOR_DONE;
}
GstSample *sample = g_sequence_get (it->current_iter);
gst_clear_sample (&it->current_sample);
it->current_sample = gst_sample_ref (sample);
it->current_time = it->timestamp_func (sample);
it->current_iter = g_sequence_iter_next (it->current_iter);
gst_value_set_sample (result, sample);
return GST_ITERATOR_OK;
}
static void
iter_resync (SampleMapIterator * it)
{
GST_TRACE_OBJECT (it->map, "resync");
it->current_time = it->start_time;
it->current_iter = it->resync_locator_func (it);
}
static void
iter_free (SampleMapIterator * it)
{
gst_clear_object (&it->map);
gst_clear_sample (&it->current_sample);
}
GstIterator *
gst_media_source_sample_map_iter_samples_by_dts (GstMediaSourceSampleMap * map,
GMutex * lock, guint32 * master_cookie, GstClockTime start_dts,
GstSample * start_sample)
{
/* *INDENT-OFF* */
SampleMapIterator *it = (SampleMapIterator *) gst_iterator_new (
sizeof (SampleMapIterator),
GST_TYPE_SAMPLE,
lock,
master_cookie,
(GstIteratorCopyFunction) iter_copy,
(GstIteratorNextFunction) iter_next,
(GstIteratorItemFunction) NULL,
(GstIteratorResyncFunction) iter_resync,
(GstIteratorFreeFunction) iter_free
);
/* *INDENT-ON* */
it->map = gst_object_ref (map);
it->timestamp_func = sample_dts;
it->resync_locator_func = iter_find_resync_point_dts;
it->start_time = start_dts;
it->current_time = start_dts;
it->current_sample = start_sample ? gst_sample_ref (start_sample) : NULL;
it->current_iter = iter_find_resync_point_dts (it);
return GST_ITERATOR (it);
}
GstIterator *
gst_media_source_sample_map_iter_samples_by_pts (GstMediaSourceSampleMap * map,
GMutex * lock, guint32 * master_cookie, GstClockTime start_pts,
GstSample * start_sample)
{
/* *INDENT-OFF* */
SampleMapIterator *it = (SampleMapIterator *) gst_iterator_new (
sizeof (SampleMapIterator),
GST_TYPE_SAMPLE,
lock,
master_cookie,
(GstIteratorCopyFunction) iter_copy,
(GstIteratorNextFunction) iter_next,
(GstIteratorItemFunction) NULL,
(GstIteratorResyncFunction) iter_resync,
(GstIteratorFreeFunction) iter_free
);
/* *INDENT-ON* */
it->map = gst_object_ref (map);
it->timestamp_func = sample_pts;
it->resync_locator_func = iter_find_resync_point_pts;
it->start_time = start_pts;
it->current_time = start_pts;
it->current_sample = start_sample ? gst_sample_ref (start_sample) : NULL;
it->current_iter = iter_find_resync_point_pts (it);
return GST_ITERATOR (it);
}