mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-11-27 04:01:08 +00:00
552 lines
16 KiB
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)
|
|
{
|
|
gst_mse_init_logging ();
|
|
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);
|
|
}
|