mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-11-30 13:41:48 +00:00
378 lines
9.5 KiB
C
378 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);
|
||
|
}
|
||
|
|
||
|
void
|
||
|
gst_media_source_track_buffer_remove (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);
|
||
|
|
||
|
gst_media_source_sample_map_remove (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;
|
||
|
}
|
||
|
|
||
|
void
|
||
|
gst_media_source_track_buffer_clear (GstMediaSourceTrackBuffer * self)
|
||
|
{
|
||
|
g_return_if_fail (GST_IS_MEDIA_SOURCE_TRACK_BUFFER (self));
|
||
|
|
||
|
NEW_DATA_LOCK (self);
|
||
|
|
||
|
g_set_object (&self->samples, gst_media_source_sample_map_new ());
|
||
|
|
||
|
NEW_DATA_SIGNAL (self);
|
||
|
NEW_DATA_UNLOCK (self);
|
||
|
}
|
||
|
|
||
|
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);
|
||
|
}
|
||
|
|
||
|
gboolean
|
||
|
gst_media_source_track_buffer_await_eos_until (GstMediaSourceTrackBuffer * self,
|
||
|
gint64 deadline)
|
||
|
{
|
||
|
NEW_DATA_LOCK (self);
|
||
|
while (!is_eos (self) && NEW_DATA_WAIT_UNTIL (self, deadline)) {
|
||
|
/* wait */
|
||
|
}
|
||
|
NEW_DATA_UNLOCK (self);
|
||
|
return is_eos (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;
|
||
|
} 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);
|
||
|
|
||
|
GstMediaSourceRange *range = &acc->current_range;
|
||
|
|
||
|
if (range->end == 0 || start <= (range->end + (GST_SECOND / 100))) {
|
||
|
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 (),
|
||
|
.current_range = {.start = 0,.end = 0},
|
||
|
};
|
||
|
|
||
|
/* *INDENT-OFF* */
|
||
|
GstIterator *iter = gst_media_source_sample_map_iter_samples_by_pts (
|
||
|
self->samples,
|
||
|
&self->new_data_mutex,
|
||
|
&self->master_cookie,
|
||
|
0,
|
||
|
NULL
|
||
|
);
|
||
|
/* *INDENT-ON* */
|
||
|
while (gst_iterator_fold (iter, (GstIteratorFoldFunction) get_ranges_fold,
|
||
|
(GValue *) & acc, NULL) == GST_ITERATOR_RESYNC) {
|
||
|
gst_iterator_resync (iter);
|
||
|
}
|
||
|
gst_iterator_free (iter);
|
||
|
|
||
|
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,
|
||
|
GstClockTime start_dts, GstSample * start_sample)
|
||
|
{
|
||
|
/* *INDENT-OFF* */
|
||
|
return gst_media_source_sample_map_iter_samples_by_dts (
|
||
|
self->samples,
|
||
|
&self->new_data_mutex,
|
||
|
&self->master_cookie,
|
||
|
start_dts,
|
||
|
start_sample
|
||
|
);
|
||
|
/* *INDENT-ON* */
|
||
|
}
|