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

462 lines
12 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.
*/
/**
* SECTION:gstsourcebufferlist
* @title: GstSourceBufferList
* @short_description: Source Buffer List
* @include: mse/mse.h
* @symbols:
* - GstSourceBufferList
*
* The Source Buffer List is a list of #GstSourceBuffer<!-- -->s that can be
* indexed numerically and monitored for changes. The list itself cannot be
* modified through this interface, though the Source Buffers it holds can be
* modified after retrieval.
*
* It is used by #GstMediaSource to provide direct access to its child
* #GstSourceBuffer<!-- -->s through #GstMediaSource:source-buffers as well as
* informing clients which of the Source Buffers are active through
* #GstMediaSource:active-source-buffers.
*
* Since: 1.24
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "gstsourcebufferlist.h"
#include "gstsourcebufferlist-private.h"
#include "gstmseeventqueue-private.h"
typedef struct
{
gboolean frozen;
gboolean added;
gboolean removed;
} PendingNotifications;
/**
* GstSourceBufferList:
*
* Since: 1.24
*/
struct _GstSourceBufferList
{
GstObject parent_instance;
GPtrArray *buffers;
GstMseEventQueue *event_queue;
PendingNotifications pending_notifications;
};
G_DEFINE_TYPE (GstSourceBufferList, gst_source_buffer_list, GST_TYPE_OBJECT);
enum
{
PROP_0,
PROP_LENGTH,
N_PROPS,
};
typedef enum
{
ON_SOURCEBUFFER_ADDED,
ON_SOURCEBUFFER_REMOVED,
N_SIGNALS,
} SourceBufferListEvent;
typedef struct
{
GstDataQueueItem item;
SourceBufferListEvent event;
} SourceBufferListEventItem;
static GParamSpec *properties[N_PROPS];
static guint signals[N_SIGNALS] = { 0 };
static gboolean
is_frozen (GstSourceBufferList * self)
{
return g_atomic_int_get (&self->pending_notifications.frozen);
}
static void
set_frozen (GstSourceBufferList * self)
{
g_atomic_int_set (&self->pending_notifications.frozen, TRUE);
}
static void
clear_frozen (GstSourceBufferList * self)
{
g_atomic_int_set (&self->pending_notifications.frozen, FALSE);
}
static void
set_pending_added (GstSourceBufferList * self)
{
g_atomic_int_set (&self->pending_notifications.added, TRUE);
}
static void
set_pending_removed (GstSourceBufferList * self)
{
g_atomic_int_set (&self->pending_notifications.removed, TRUE);
}
static gboolean
clear_pending_added (GstSourceBufferList * self)
{
return g_atomic_int_and (&self->pending_notifications.added, FALSE);
}
static gboolean
clear_pending_removed (GstSourceBufferList * self)
{
return g_atomic_int_and (&self->pending_notifications.removed, FALSE);
}
static void
schedule_event (GstSourceBufferList * self, SourceBufferListEvent event)
{
SourceBufferListEventItem item = {
.item = {.destroy = g_free,.visible = TRUE,.size = 1,.object = NULL},
.event = event,
};
gst_mse_event_queue_push (self->event_queue, g_memdup2 (&item,
sizeof (SourceBufferListEventItem)));
}
static void
dispatch_event (SourceBufferListEventItem * item, GstSourceBufferList * self)
{
g_signal_emit (self, signals[item->event], 0);
}
static void
call_source_buffer_added (GstSourceBufferList * self)
{
if (is_frozen (self)) {
set_pending_added (self);
} else {
clear_pending_added (self);
schedule_event (self, ON_SOURCEBUFFER_ADDED);
}
}
static void
call_source_buffer_removed (GstSourceBufferList * self)
{
if (is_frozen (self)) {
set_pending_removed (self);
} else {
clear_pending_removed (self);
schedule_event (self, ON_SOURCEBUFFER_REMOVED);
}
}
GstSourceBufferList *
gst_source_buffer_list_new (void)
{
return gst_object_ref_sink (g_object_new (GST_TYPE_SOURCE_BUFFER_LIST, NULL));
}
static void
gst_source_buffer_list_dispose (GObject * object)
{
GstSourceBufferList *self = (GstSourceBufferList *) object;
gst_clear_object (&self->event_queue);
G_OBJECT_CLASS (gst_source_buffer_list_parent_class)->dispose (object);
}
static void
gst_source_buffer_list_finalize (GObject * object)
{
GstSourceBufferList *self = (GstSourceBufferList *) object;
g_clear_pointer (&self->buffers, g_ptr_array_unref);
G_OBJECT_CLASS (gst_source_buffer_list_parent_class)->finalize (object);
}
static void
gst_source_buffer_list_get_property (GObject * object, guint prop_id, GValue
* value, GParamSpec * pspec)
{
GstSourceBufferList *self = GST_SOURCE_BUFFER_LIST (object);
switch (prop_id) {
case PROP_LENGTH:
g_value_set_uint (value, gst_source_buffer_list_get_length (self));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
gst_source_buffer_list_class_init (GstSourceBufferListClass * klass)
{
GObjectClass *oclass = G_OBJECT_CLASS (klass);
oclass->dispose = GST_DEBUG_FUNCPTR (gst_source_buffer_list_dispose);
oclass->finalize = GST_DEBUG_FUNCPTR (gst_source_buffer_list_finalize);
oclass->get_property =
GST_DEBUG_FUNCPTR (gst_source_buffer_list_get_property);
/**
* GstSourceBufferList:length:
*
* The number of #GstSourceBuffer<!-- -->s contained by this structure
*
* [Specification](https://www.w3.org/TR/media-source-2/#dom-sourcebufferlist-length)
*
* Since: 1.24
*/
properties[PROP_LENGTH] = g_param_spec_ulong ("length",
"Length",
"The number of SourceBuffers contained by this structure",
0, G_MAXULONG, 0, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
g_object_class_install_properties (oclass, N_PROPS, properties);
/**
* GstSourceBufferList::on-sourcebuffer-added:
* @self: The #GstSourceBufferList that has just added a
* #GstSourceBuffer
*
* Emitted when a #GstSourceBuffer has been added to this list.
*
* [Specification](https://www.w3.org/TR/media-source-2/#dom-sourcebufferlist-onaddsourcebuffer)
*
* Since: 1.24
*/
signals[ON_SOURCEBUFFER_ADDED] = g_signal_new ("on-sourcebuffer-added",
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 0);
/**
* GstSourceBufferList::on-sourcebuffer-removed:
* @self: The #GstSourceBufferList that has just removed a
* #GstSourceBuffer
*
* Emitted when a #GstSourceBuffer has been removed from this list.
*
* [Specification](https://www.w3.org/TR/media-source-2/#dom-sourcebufferlist-onremovesourcebuffer)
*
* Since: 1.24
*/
signals[ON_SOURCEBUFFER_REMOVED] = g_signal_new ("on-sourcebuffer-removed",
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 0);
}
static void
gst_source_buffer_list_init (GstSourceBufferList * self)
{
set_frozen (self);
self->buffers = g_ptr_array_new_with_free_func (gst_object_unref);
self->event_queue =
gst_mse_event_queue_new ((GstMseEventQueueCallback) dispatch_event, self);
clear_pending_added (self);
clear_pending_removed (self);
clear_frozen (self);
}
/**
* gst_source_buffer_list_index:
* @self: #GstSourceBufferList instance
* @index: index of requested Source Buffer
*
* Retrieves the #GstSourceBuffer at @index from @self. If @index is greater than
* the highest index in the list, it will return `NULL`.
*
* [Specification](https://www.w3.org/TR/media-source-2/#dfn-sourcebufferlist-getter)
*
* Returns: (transfer full) (nullable): The requested #GstSourceBuffer or `NULL`
* Since: 1.24
*/
GstSourceBuffer *
gst_source_buffer_list_index (GstSourceBufferList * self, guint index)
{
g_return_val_if_fail (GST_IS_SOURCE_BUFFER_LIST (self), NULL);
if (index >= gst_source_buffer_list_get_length (self))
return NULL;
return gst_object_ref (g_ptr_array_index (self->buffers, index));
}
/**
* gst_source_buffer_list_get_length:
* @self: #GstSourceBufferList instance
*
* [Specification](https://www.w3.org/TR/media-source-2/#dom-sourcebufferlist-length)
*
* Returns: The number of #GstSourceBuffer objects in the list
* Since: 1.24
*/
guint
gst_source_buffer_list_get_length (GstSourceBufferList * self)
{
g_return_val_if_fail (GST_IS_SOURCE_BUFFER_LIST (self), 0);
return self->buffers->len;
}
gboolean
gst_source_buffer_list_contains (GstSourceBufferList * self,
GstSourceBuffer * buf)
{
g_return_val_if_fail (GST_IS_SOURCE_BUFFER_LIST (self), FALSE);
g_return_val_if_fail (GST_IS_SOURCE_BUFFER (buf), FALSE);
return g_ptr_array_find (self->buffers, buf, NULL);
}
void
gst_source_buffer_list_append (GstSourceBufferList * self,
GstSourceBuffer * buf)
{
g_return_if_fail (GST_IS_SOURCE_BUFFER_LIST (self));
g_ptr_array_add (self->buffers, gst_object_ref (buf));
call_source_buffer_added (self);
}
gboolean
gst_source_buffer_list_remove (GstSourceBufferList * self,
GstSourceBuffer * buf)
{
g_return_val_if_fail (GST_IS_SOURCE_BUFFER_LIST (self), FALSE);
gboolean removed = g_ptr_array_remove (self->buffers, buf);
if (removed) {
call_source_buffer_removed (self);
return TRUE;
} else {
return FALSE;
}
}
void
gst_source_buffer_list_remove_all (GstSourceBufferList * self)
{
g_return_if_fail (GST_IS_SOURCE_BUFFER_LIST (self));
if (self->buffers->len < 1) {
return;
}
g_ptr_array_set_size (self->buffers, 0);
call_source_buffer_removed (self);
}
/**
* gst_source_buffer_list_notify_freeze:
* @self: #GstSourceBufferList instance
*
* Prevents any notifications from being emitted by @self until the next call to
* gst_source_buffer_list_notify_thaw().
*
*/
void
gst_source_buffer_list_notify_freeze (GstSourceBufferList * self)
{
g_return_if_fail (GST_IS_SOURCE_BUFFER_LIST (self));
clear_pending_added (self);
clear_pending_removed (self);
set_frozen (self);
}
/**
* gst_source_buffer_list_notify_cancel:
* @self: #GstSourceBufferList instance
*
* Cancels any pending notifications that are waiting between calls to
* gst_source_buffer_list_notify_freeze() and
* gst_source_buffer_list_notify_thaw().
*
*/
void
gst_source_buffer_list_notify_cancel (GstSourceBufferList * self)
{
g_return_if_fail (GST_IS_SOURCE_BUFFER_LIST (self));
clear_pending_added (self);
clear_pending_removed (self);
}
/**
* gst_source_buffer_list_notify_added:
* @self: #GstSourceBufferList instance
*
* Explicitly notifies subscribers to the ::on-sourcebuffer-added signal that an
* item has been added to @self.
*
*/
void
gst_source_buffer_list_notify_added (GstSourceBufferList * self)
{
g_return_if_fail (GST_IS_SOURCE_BUFFER_LIST (self));
g_return_if_fail (!is_frozen (self));
call_source_buffer_added (self);
}
/**
* gst_source_buffer_list_notify_removed:
* @self: #GstSourceBufferList instance
*
* Explicitly notifies subscribers to the ::on-sourcebuffer-removed signal that
* an item has been removed from @self.
*
*/
void
gst_source_buffer_list_notify_removed (GstSourceBufferList * self)
{
g_return_if_fail (GST_IS_SOURCE_BUFFER_LIST (self));
g_return_if_fail (!is_frozen (self));
call_source_buffer_removed (self);
}
/**
* gst_source_buffer_list_notify_thaw:
* @self: #GstSourceBufferList instance
*
* Resumes notifications emitted from @self after a call to
* gst_source_buffer_list_notify_freeze(). If any notifications are pending,
* they will be emitted as a result of this call. To prevent pending
* notifications from being published, use
* gst_source_buffer_list_notify_cancel() before calling this method.
*
*/
void
gst_source_buffer_list_notify_thaw (GstSourceBufferList * self)
{
g_return_if_fail (GST_IS_SOURCE_BUFFER_LIST (self));
clear_frozen (self);
if (clear_pending_added (self)) {
g_signal_emit (self, signals[ON_SOURCEBUFFER_ADDED], 0);
}
if (clear_pending_removed (self)) {
g_signal_emit (self, signals[ON_SOURCEBUFFER_REMOVED], 0);
}
}