mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2025-02-06 06:22:29 +00:00
bad: Added W3C Media Source Extensions library
Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2992>
This commit is contained in:
parent
6c7956ab93
commit
66f51f642f
45 changed files with 11973 additions and 1 deletions
1135
girs/GstMse-1.0.gir
Normal file
1135
girs/GstMse-1.0.gir
Normal file
File diff suppressed because it is too large
Load diff
3
subprojects/gst-plugins-bad/docs/libs/mse/index.md
Normal file
3
subprojects/gst-plugins-bad/docs/libs/mse/index.md
Normal file
|
@ -0,0 +1,3 @@
|
|||
# W3C Media Source Extensions Library
|
||||
|
||||
> NOTE: This library API is considered *unstable*
|
1
subprojects/gst-plugins-bad/docs/libs/mse/sitemap.txt
Normal file
1
subprojects/gst-plugins-bad/docs/libs/mse/sitemap.txt
Normal file
|
@ -0,0 +1 @@
|
|||
gi-index
|
|
@ -134,6 +134,7 @@ if build_gir
|
|||
{'name': 'codecs', 'gir': codecs_gir, 'lib': gstcodecs_dep},
|
||||
{'name': 'cuda', 'gir': gst_cuda_gir, 'lib': gstcuda_dep, 'c_source_patterns': ['*.h', '*.cpp']},
|
||||
{'name': 'dxva', 'gir': dxva_gir, 'lib': gstdxva_dep, 'c_source_patterns': ['*.h', '*.cpp']},
|
||||
{'name': 'mse', 'gir': mse_gir, 'lib': gstmse_dep, 'suffix': 'lib'},
|
||||
]
|
||||
|
||||
if gstopencv_dep.found()
|
||||
|
|
|
@ -220390,6 +220390,126 @@
|
|||
"tracers": {},
|
||||
"url": "Unknown package origin"
|
||||
},
|
||||
"mse": {
|
||||
"description": "W3C Media Source Extensions Support",
|
||||
"elements": {
|
||||
"msesrc": {
|
||||
"author": "Collabora",
|
||||
"description": "Implements a GStreamer Source for the gstreamer-mse API",
|
||||
"hierarchy": [
|
||||
"GstMseSrc",
|
||||
"GstElement",
|
||||
"GstObject",
|
||||
"GInitiallyUnowned",
|
||||
"GObject"
|
||||
],
|
||||
"interfaces": [
|
||||
"GstURIHandler"
|
||||
],
|
||||
"klass": "Generic/Source",
|
||||
"pad-templates": {
|
||||
"src_%%s": {
|
||||
"caps": "ANY",
|
||||
"direction": "src",
|
||||
"presence": "sometimes",
|
||||
"type": "GstMseSrcPad"
|
||||
}
|
||||
},
|
||||
"properties": {
|
||||
"duration": {
|
||||
"blurb": "The duration of the stream as a GstClockTime",
|
||||
"conditionally-available": false,
|
||||
"construct": false,
|
||||
"construct-only": false,
|
||||
"controllable": false,
|
||||
"default": "0",
|
||||
"max": "18446744073709551615",
|
||||
"min": "0",
|
||||
"mutable": "null",
|
||||
"readable": true,
|
||||
"type": "guint64",
|
||||
"writable": true
|
||||
},
|
||||
"n-audio": {
|
||||
"blurb": "The number of audio tracks in the Media Source",
|
||||
"conditionally-available": false,
|
||||
"construct": false,
|
||||
"construct-only": false,
|
||||
"controllable": false,
|
||||
"default": "0",
|
||||
"max": "2147483647",
|
||||
"min": "0",
|
||||
"mutable": "null",
|
||||
"readable": true,
|
||||
"type": "guint",
|
||||
"writable": false
|
||||
},
|
||||
"n-text": {
|
||||
"blurb": "The number of text tracks in the Media Source",
|
||||
"conditionally-available": false,
|
||||
"construct": false,
|
||||
"construct-only": false,
|
||||
"controllable": false,
|
||||
"default": "0",
|
||||
"max": "2147483647",
|
||||
"min": "0",
|
||||
"mutable": "null",
|
||||
"readable": true,
|
||||
"type": "guint",
|
||||
"writable": false
|
||||
},
|
||||
"n-video": {
|
||||
"blurb": "The number of video tracks in the Media Source",
|
||||
"conditionally-available": false,
|
||||
"construct": false,
|
||||
"construct-only": false,
|
||||
"controllable": false,
|
||||
"default": "0",
|
||||
"max": "2147483647",
|
||||
"min": "0",
|
||||
"mutable": "null",
|
||||
"readable": true,
|
||||
"type": "guint",
|
||||
"writable": false
|
||||
},
|
||||
"position": {
|
||||
"blurb": "The playback position as a GstClockTime",
|
||||
"conditionally-available": false,
|
||||
"construct": false,
|
||||
"construct-only": false,
|
||||
"controllable": false,
|
||||
"default": "18446744073709551615",
|
||||
"max": "18446744073709551615",
|
||||
"min": "0",
|
||||
"mutable": "null",
|
||||
"readable": true,
|
||||
"type": "guint64",
|
||||
"writable": false
|
||||
},
|
||||
"ready-state": {
|
||||
"blurb": "The Ready State of this Element",
|
||||
"conditionally-available": false,
|
||||
"construct": false,
|
||||
"construct-only": false,
|
||||
"controllable": false,
|
||||
"default": "nothing (0)",
|
||||
"mutable": "null",
|
||||
"readable": true,
|
||||
"type": "GstMseSrcReadyState",
|
||||
"writable": false
|
||||
}
|
||||
},
|
||||
"rank": "none"
|
||||
}
|
||||
},
|
||||
"filename": "gstmse",
|
||||
"license": "LGPL",
|
||||
"other-types": {},
|
||||
"package": "GStreamer Bad Plug-ins",
|
||||
"source": "gst-plugins-bad",
|
||||
"tracers": {},
|
||||
"url": "Unknown package origin"
|
||||
},
|
||||
"musepack": {
|
||||
"description": "Musepack decoder",
|
||||
"elements": {
|
||||
|
|
|
@ -13,6 +13,7 @@ subdir('insertbin')
|
|||
subdir('interfaces')
|
||||
subdir('isoff')
|
||||
subdir('mpegts')
|
||||
subdir('mse')
|
||||
subdir('opencv')
|
||||
subdir('play')
|
||||
subdir('player')
|
||||
|
|
|
@ -0,0 +1,102 @@
|
|||
/* GStreamer
|
||||
*
|
||||
* SPDX-License-Identifier: LGPL-2.1
|
||||
*
|
||||
* Copyright (C) 2016, 2017 Metrological Group B.V.
|
||||
* Copyright (C) 2016, 2017 Igalia S.L
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <gst/gst.h>
|
||||
#include <glib-object.h>
|
||||
#include <gst/mse/mse-prelude.h>
|
||||
#include <gst/mse/gstmediasourcetrack-private.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GST_TYPE_APPEND_PIPELINE (gst_append_pipeline_get_type())
|
||||
|
||||
GST_MSE_PRIVATE
|
||||
G_DECLARE_FINAL_TYPE (GstAppendPipeline, gst_append_pipeline, GST,
|
||||
APPEND_PIPELINE, GstObject);
|
||||
|
||||
typedef struct
|
||||
{
|
||||
void (*received_init_segment) (GstAppendPipeline * self,
|
||||
gpointer user_data);
|
||||
void (*duration_changed) (GstAppendPipeline * self,
|
||||
gpointer user_data);
|
||||
void (*new_sample) (GstAppendPipeline * self,
|
||||
GstMediaSourceTrack * track,
|
||||
GstSample * sample,
|
||||
gpointer user_data);
|
||||
void (*eos) (GstAppendPipeline * self,
|
||||
GstMediaSourceTrack * track,
|
||||
gpointer user_data);
|
||||
void (*error) (GstAppendPipeline * self,
|
||||
gpointer user_data);
|
||||
} GstAppendPipelineCallbacks;
|
||||
|
||||
GST_MSE_PRIVATE
|
||||
GstAppendPipeline * gst_append_pipeline_new (
|
||||
GstAppendPipelineCallbacks * callbacks, gpointer user_data,
|
||||
GError ** error);
|
||||
|
||||
GST_MSE_PRIVATE
|
||||
GstFlowReturn gst_append_pipeline_append (GstAppendPipeline * self,
|
||||
GstBuffer * buffer);
|
||||
|
||||
GST_MSE_PRIVATE
|
||||
GstFlowReturn gst_append_pipeline_eos (GstAppendPipeline * self);
|
||||
|
||||
GST_MSE_PRIVATE
|
||||
gboolean gst_append_pipeline_stop (GstAppendPipeline * self);
|
||||
|
||||
GST_MSE_PRIVATE
|
||||
gboolean gst_append_pipeline_reset (GstAppendPipeline * self);
|
||||
|
||||
GST_MSE_PRIVATE
|
||||
gsize gst_append_pipeline_n_tracks (GstAppendPipeline * self);
|
||||
|
||||
GST_MSE_PRIVATE
|
||||
gboolean gst_append_pipeline_has_init_segment (GstAppendPipeline * self);
|
||||
|
||||
GST_MSE_PRIVATE
|
||||
GstClockTime gst_append_pipeline_get_duration (GstAppendPipeline * self);
|
||||
|
||||
GST_MSE_PRIVATE
|
||||
GPtrArray *gst_append_pipeline_get_audio_tracks (GstAppendPipeline * self);
|
||||
|
||||
GST_MSE_PRIVATE
|
||||
GPtrArray *gst_append_pipeline_get_text_tracks (GstAppendPipeline * self);
|
||||
|
||||
GST_MSE_PRIVATE
|
||||
GPtrArray *gst_append_pipeline_get_video_tracks (GstAppendPipeline * self);
|
||||
|
||||
GST_MSE_PRIVATE
|
||||
gboolean gst_append_pipeline_get_eos (GstAppendPipeline * self);
|
||||
|
||||
GST_MSE_PRIVATE
|
||||
void gst_append_pipeline_fail (GstAppendPipeline * self);
|
||||
|
||||
GST_MSE_PRIVATE
|
||||
gboolean gst_append_pipeline_get_failed (GstAppendPipeline * self);
|
||||
|
||||
G_END_DECLS
|
978
subprojects/gst-plugins-bad/gst-libs/gst/mse/gstappendpipeline.c
Normal file
978
subprojects/gst-plugins-bad/gst-libs/gst/mse/gstappendpipeline.c
Normal file
|
@ -0,0 +1,978 @@
|
|||
/* GStreamer
|
||||
*
|
||||
* SPDX-License-Identifier: LGPL-2.1
|
||||
*
|
||||
* Copyright (C) 2016, 2017 Metrological Group B.V.
|
||||
* Copyright (C) 2016, 2017 Igalia S.L
|
||||
* 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 "gstappendpipeline-private.h"
|
||||
|
||||
#include "mse.h"
|
||||
|
||||
#include "gstmselogging-private.h"
|
||||
#include "gstmsemediatype-private.h"
|
||||
#include "gstmediasourcetrack-private.h"
|
||||
|
||||
#include <gst/gst.h>
|
||||
#include <gst/app/gstappsrc.h>
|
||||
#include <gst/app/gstappsink.h>
|
||||
|
||||
typedef enum
|
||||
{
|
||||
PARSE_ERROR,
|
||||
IGNORED,
|
||||
ADDED,
|
||||
} AddTrackResult;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
GstAppendPipeline *pipeline;
|
||||
GstTask *task;
|
||||
GRecMutex mutex;
|
||||
GstBus *bus;
|
||||
} BackgroundTask;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
GstAppendPipelineCallbacks callbacks;
|
||||
gpointer user_data;
|
||||
} Callbacks;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
GstAppendPipeline *parent;
|
||||
GstPad *src_pad;
|
||||
GstAppSink *sink;
|
||||
GstMediaSourceTrack *mse_track;
|
||||
GstStream *stream;
|
||||
GstClockTime previous_pts;
|
||||
} Track;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
GstClockTime duration;
|
||||
GPtrArray *video_tracks;
|
||||
GPtrArray *audio_tracks;
|
||||
GPtrArray *text_tracks;
|
||||
} InitSegment;
|
||||
|
||||
struct _GstAppendPipeline
|
||||
{
|
||||
GstObject parent_instance;
|
||||
|
||||
GstPipeline *pipeline;
|
||||
GstAppSrc *src;
|
||||
GstElement *parsebin;
|
||||
GstBus *bus;
|
||||
|
||||
GstStreamCollection *streams;
|
||||
GArray *tracks;
|
||||
|
||||
gboolean received_init_segment;
|
||||
gboolean have_outstanding_samples;
|
||||
InitSegment init_segment;
|
||||
|
||||
gboolean encountered_error;
|
||||
|
||||
BackgroundTask *task;
|
||||
|
||||
Callbacks callbacks;
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE (GstAppendPipeline, gst_append_pipeline, GST_TYPE_OBJECT);
|
||||
|
||||
#define END_OF_APPEND "end-of-append"
|
||||
#define ABORT "abort"
|
||||
#define SHUTDOWN "shutdown"
|
||||
|
||||
static void process_init_segment (GstAppendPipeline *);
|
||||
|
||||
static gboolean
|
||||
send_abort (GstAppendPipeline * self)
|
||||
{
|
||||
return gst_bus_post (self->bus, gst_message_new_application (NULL,
|
||||
gst_structure_new_empty (ABORT)));
|
||||
}
|
||||
|
||||
static gboolean
|
||||
send_shutdown (GstAppendPipeline * self)
|
||||
{
|
||||
return gst_bus_post (self->bus, gst_message_new_application (NULL,
|
||||
gst_structure_new_empty (SHUTDOWN)));
|
||||
}
|
||||
|
||||
static GstEvent *
|
||||
new_end_of_append_event (void)
|
||||
{
|
||||
GstStructure *structure = gst_structure_new_empty (END_OF_APPEND);
|
||||
return gst_event_new_custom (GST_EVENT_CUSTOM_DOWNSTREAM, structure);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
is_end_of_append_event (GstEvent * event)
|
||||
{
|
||||
return GST_EVENT_TYPE (event) == GST_EVENT_CUSTOM_DOWNSTREAM
|
||||
&& gst_event_has_name (event, END_OF_APPEND);
|
||||
}
|
||||
|
||||
static inline guint
|
||||
n_tracks (GstAppendPipeline * self)
|
||||
{
|
||||
return self->tracks->len;
|
||||
}
|
||||
|
||||
static inline Track *
|
||||
index_track (GstAppendPipeline * self, guint i)
|
||||
{
|
||||
return &g_array_index (self->tracks, Track, i);
|
||||
}
|
||||
|
||||
static inline void
|
||||
call_parse_error (GstAppendPipeline * self)
|
||||
{
|
||||
GstAppendPipelineCallbacks *callbacks = &self->callbacks.callbacks;
|
||||
gpointer user_data = self->callbacks.user_data;
|
||||
|
||||
if (callbacks->error) {
|
||||
callbacks->error (self, user_data);
|
||||
GST_TRACE_OBJECT (self, "done");
|
||||
} else {
|
||||
GST_TRACE_OBJECT (self, "dropping");
|
||||
}
|
||||
}
|
||||
|
||||
static inline void
|
||||
call_received_init_segment (GstAppendPipeline * self)
|
||||
{
|
||||
GstAppendPipelineCallbacks *callbacks = &self->callbacks.callbacks;
|
||||
gpointer user_data = self->callbacks.user_data;
|
||||
|
||||
if (callbacks->received_init_segment) {
|
||||
callbacks->received_init_segment (self, user_data);
|
||||
GST_TRACE_OBJECT (self, "done");
|
||||
} else {
|
||||
GST_TRACE_OBJECT (self, "dropping");
|
||||
}
|
||||
}
|
||||
|
||||
static inline void
|
||||
call_new_sample (GstAppendPipeline * self, GstMediaSourceTrack * track,
|
||||
GstSample * sample)
|
||||
{
|
||||
GstAppendPipelineCallbacks *callbacks = &self->callbacks.callbacks;
|
||||
gpointer user_data = self->callbacks.user_data;
|
||||
|
||||
if (callbacks->new_sample) {
|
||||
callbacks->new_sample (self, track, sample, user_data);
|
||||
GST_TRACE_OBJECT (self, "done");
|
||||
} else {
|
||||
GST_TRACE_OBJECT (self, "dropping");
|
||||
}
|
||||
}
|
||||
|
||||
static inline void
|
||||
call_duration_changed (GstAppendPipeline * self)
|
||||
{
|
||||
GstAppendPipelineCallbacks *callbacks = &self->callbacks.callbacks;
|
||||
gpointer user_data = self->callbacks.user_data;
|
||||
|
||||
if (callbacks->duration_changed) {
|
||||
callbacks->duration_changed (self, user_data);
|
||||
GST_TRACE_OBJECT (self, "done");
|
||||
} else {
|
||||
GST_TRACE_OBJECT (self, "dropping");
|
||||
}
|
||||
}
|
||||
|
||||
static inline void
|
||||
call_eos (GstAppendPipeline * self, GstMediaSourceTrack * track)
|
||||
{
|
||||
GstAppendPipelineCallbacks *callbacks = &self->callbacks.callbacks;
|
||||
gpointer user_data = self->callbacks.user_data;
|
||||
|
||||
if (callbacks->eos) {
|
||||
callbacks->eos (self, track, user_data);
|
||||
GST_TRACE_OBJECT (self, "done");
|
||||
} else {
|
||||
GST_TRACE_OBJECT (self, "dropping");
|
||||
}
|
||||
}
|
||||
|
||||
static inline GstSample *
|
||||
patch_missing_duration (GstAppendPipeline * self, GstSample * sample)
|
||||
{
|
||||
GstBuffer *buffer = gst_sample_get_buffer (sample);
|
||||
if (!GST_BUFFER_DURATION_IS_VALID (buffer)) {
|
||||
GST_BUFFER_DURATION (buffer) = GST_SECOND / 60;
|
||||
GST_TRACE_OBJECT (self, "sample is missing duration, patched to %"
|
||||
GST_TIMEP_FORMAT, &buffer->duration);
|
||||
}
|
||||
return sample;
|
||||
}
|
||||
|
||||
static inline GstSample *
|
||||
patch_missing_pts (GstAppendPipeline * self, GstSample * sample, GstClockTime
|
||||
fallback)
|
||||
{
|
||||
GstBuffer *buffer = gst_sample_get_buffer (sample);
|
||||
if (!GST_BUFFER_PTS_IS_VALID (buffer) && GST_CLOCK_TIME_IS_VALID (fallback)) {
|
||||
GST_TRACE_OBJECT (self, "sample is missing pts, patching with %"
|
||||
GST_TIMEP_FORMAT, &fallback);
|
||||
GST_BUFFER_PTS (buffer) = fallback;
|
||||
}
|
||||
return sample;
|
||||
}
|
||||
|
||||
static inline GstSample *
|
||||
patch_missing_dts (GstAppendPipeline * self, GstSample * sample)
|
||||
{
|
||||
GstBuffer *buffer = gst_sample_get_buffer (sample);
|
||||
if (!GST_BUFFER_DTS_IS_VALID (buffer) && GST_BUFFER_PTS_IS_VALID (buffer)) {
|
||||
GST_TRACE_OBJECT (self, "sample is missing dts, patching with pts %"
|
||||
GST_TIMEP_FORMAT, &buffer->pts);
|
||||
GST_BUFFER_DTS (buffer) = GST_BUFFER_PTS (buffer);
|
||||
}
|
||||
return sample;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
consume_sample_from_track (GstAppendPipeline * self, Track * track)
|
||||
{
|
||||
GstSample *sample = gst_app_sink_try_pull_sample (track->sink, 0);
|
||||
if (sample == NULL) {
|
||||
return FALSE;
|
||||
}
|
||||
GstBuffer *buffer = gst_sample_get_buffer (sample);
|
||||
if (!GST_IS_BUFFER (buffer)) {
|
||||
GST_WARNING_OBJECT (self, "got null buffer in sample");
|
||||
goto done;
|
||||
}
|
||||
sample = patch_missing_pts (self, sample, track->previous_pts);
|
||||
sample = patch_missing_duration (self, sample);
|
||||
sample = patch_missing_dts (self, sample);
|
||||
track->previous_pts = GST_BUFFER_PTS (buffer);
|
||||
call_new_sample (self, track->mse_track, sample);
|
||||
|
||||
done:
|
||||
gst_clear_sample (&sample);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
consume_all_samples (GstAppendPipeline * self)
|
||||
{
|
||||
if (!self->received_init_segment) {
|
||||
GST_DEBUG_OBJECT (self, "not all tracks are available, delaying");
|
||||
self->have_outstanding_samples = TRUE;
|
||||
return;
|
||||
}
|
||||
guint track_count = n_tracks (self);
|
||||
while (TRUE) {
|
||||
gboolean sample_consumed = FALSE;
|
||||
for (guint i = 0; i < track_count; i++) {
|
||||
Track *track = index_track (self, i);
|
||||
sample_consumed |= consume_sample_from_track (self, track);
|
||||
}
|
||||
if (!sample_consumed) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
call_duration_changed (self);
|
||||
self->have_outstanding_samples = FALSE;
|
||||
}
|
||||
|
||||
static void
|
||||
handle_shutdown (BackgroundTask * task)
|
||||
{
|
||||
gst_task_stop (task->task);
|
||||
GstAppendPipeline *self = task->pipeline;
|
||||
guint track_count = n_tracks (self);
|
||||
for (guint i = 0; i < track_count; i++) {
|
||||
Track *track = index_track (self, i);
|
||||
call_eos (self, track->mse_track);
|
||||
}
|
||||
call_eos (self, NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
handle_abort (BackgroundTask * task)
|
||||
{
|
||||
gst_task_stop (task->task);
|
||||
}
|
||||
|
||||
static void
|
||||
task_function (gpointer user_data)
|
||||
{
|
||||
BackgroundTask *task = (BackgroundTask *) user_data;
|
||||
GstAppendPipeline *self = task->pipeline;
|
||||
GstMessage *message = gst_bus_timed_pop (task->bus, GST_CLOCK_TIME_NONE);
|
||||
switch (GST_MESSAGE_TYPE (message)) {
|
||||
case GST_MESSAGE_APPLICATION:{
|
||||
if (gst_message_has_name (message, END_OF_APPEND)) {
|
||||
GST_TRACE_OBJECT (self, "end of append");
|
||||
consume_all_samples (self);
|
||||
goto done;
|
||||
}
|
||||
if (gst_message_has_name (message, SHUTDOWN)) {
|
||||
GST_DEBUG_OBJECT (self, "shutdown");
|
||||
handle_shutdown (task);
|
||||
goto done;
|
||||
}
|
||||
if (gst_message_has_name (message, ABORT)) {
|
||||
GST_DEBUG_OBJECT (self, "abort");
|
||||
handle_abort (task);
|
||||
goto done;
|
||||
}
|
||||
g_error ("received unsupported application message");
|
||||
}
|
||||
case GST_MESSAGE_STREAM_COLLECTION:{
|
||||
GST_DEBUG_OBJECT (self, "stream collection");
|
||||
GstStreamCollection *streams;
|
||||
gst_message_parse_stream_collection (message, &streams);
|
||||
gst_clear_object (&self->streams);
|
||||
self->streams = streams;
|
||||
process_init_segment (self);
|
||||
goto done;
|
||||
}
|
||||
case GST_MESSAGE_EOS:
|
||||
GST_DEBUG_OBJECT (self, "end of stream");
|
||||
if (self->have_outstanding_samples) {
|
||||
GST_DEBUG_OBJECT (self, "consuming remaining samples before EOS");
|
||||
consume_all_samples (self);
|
||||
}
|
||||
handle_shutdown (task);
|
||||
goto done;
|
||||
case GST_MESSAGE_ERROR:
|
||||
GST_DEBUG_OBJECT (self, "error: %" GST_PTR_FORMAT, message);
|
||||
self->encountered_error = TRUE;
|
||||
call_parse_error (self);
|
||||
handle_shutdown (task);
|
||||
goto done;
|
||||
default:
|
||||
GST_TRACE_OBJECT (self, "ignoring message %" GST_PTR_FORMAT, message);
|
||||
goto done;
|
||||
}
|
||||
done:
|
||||
gst_message_unref (message);
|
||||
}
|
||||
|
||||
static inline GstAppSink *
|
||||
new_appsink (GstAppendPipeline * self, GstStreamType type)
|
||||
{
|
||||
const gchar *type_name = gst_stream_type_get_name (type);
|
||||
gchar *name = g_strdup_printf ("%s-%u", type_name, n_tracks (self));
|
||||
GstAppSink *appsink =
|
||||
GST_APP_SINK (gst_element_factory_make ("appsink", name));
|
||||
gst_base_sink_set_sync (GST_BASE_SINK (appsink), FALSE);
|
||||
gst_base_sink_set_async_enabled (GST_BASE_SINK (appsink), FALSE);
|
||||
gst_base_sink_set_drop_out_of_segment (GST_BASE_SINK (appsink), FALSE);
|
||||
gst_base_sink_set_last_sample_enabled (GST_BASE_SINK (appsink), FALSE);
|
||||
g_free (name);
|
||||
return appsink;
|
||||
}
|
||||
|
||||
static GstPadProbeReturn
|
||||
black_hole_probe (GstPad * pad, GstPadProbeInfo * info, gpointer user_data)
|
||||
{
|
||||
g_return_val_if_fail (GST_PAD_PROBE_INFO_TYPE (info) &
|
||||
GST_PAD_PROBE_TYPE_BUFFER, GST_PAD_PROBE_DROP);
|
||||
return GST_PAD_PROBE_DROP;
|
||||
}
|
||||
|
||||
static GstPadProbeReturn
|
||||
event_probe (GstPad * pad, GstPadProbeInfo * info, gpointer user_data)
|
||||
{
|
||||
GstAppendPipeline *self = GST_APPEND_PIPELINE (user_data);
|
||||
GstEvent *event = GST_PAD_PROBE_INFO_EVENT (info);
|
||||
|
||||
if (is_end_of_append_event (event)) {
|
||||
GST_TRACE_OBJECT (self, "end of append event");
|
||||
if (gst_bus_post (self->bus, gst_message_new_application (NULL,
|
||||
gst_structure_new_empty (END_OF_APPEND)))) {
|
||||
return GST_PAD_PROBE_DROP;
|
||||
} else {
|
||||
GST_ERROR_OBJECT (self, "failed to post end of append");
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
if (GST_EVENT_TYPE (event) == GST_EVENT_EOS) {
|
||||
GST_DEBUG_OBJECT (self, "eos event");
|
||||
if (send_shutdown (self)) {
|
||||
return GST_PAD_PROBE_OK;
|
||||
} else {
|
||||
GST_ERROR_OBJECT (self, "failed to post shutdown");
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
return GST_PAD_PROBE_OK;
|
||||
|
||||
error:
|
||||
GST_PAD_PROBE_INFO_FLOW_RETURN (info) = GST_FLOW_ERROR;
|
||||
gst_event_unref (event);
|
||||
return GST_PAD_PROBE_HANDLED;
|
||||
}
|
||||
|
||||
static AddTrackResult
|
||||
add_track (GstAppendPipeline * self, GstPad * pad, GstStream * stream,
|
||||
GstCaps * caps, Track * added_track)
|
||||
{
|
||||
GstStreamType type = gst_stream_get_stream_type (stream);
|
||||
GstMediaSourceTrackType track_type =
|
||||
gst_media_source_track_type_from_stream_type (type);
|
||||
|
||||
switch (type) {
|
||||
case GST_STREAM_TYPE_AUDIO:
|
||||
case GST_STREAM_TYPE_TEXT:
|
||||
case GST_STREAM_TYPE_VIDEO:
|
||||
break;
|
||||
default:{
|
||||
GST_DEBUG_OBJECT (self, "unexpected caps %" GST_PTR_FORMAT
|
||||
", using black hole probe", caps);
|
||||
gst_pad_add_probe (pad, GST_PAD_PROBE_TYPE_BUFFER, black_hole_probe, self,
|
||||
NULL);
|
||||
return IGNORED;
|
||||
}
|
||||
}
|
||||
|
||||
if (type != GST_STREAM_TYPE_TEXT &&
|
||||
!gst_media_source_media_type_is_caps_supported (caps)) {
|
||||
GST_ERROR_OBJECT (self, "unsupported caps: %" GST_PTR_FORMAT, caps);
|
||||
return PARSE_ERROR;
|
||||
}
|
||||
|
||||
GstAppSink *appsink = new_appsink (self, type);
|
||||
gst_bin_add (GST_BIN (self->pipeline), GST_ELEMENT (appsink));
|
||||
gst_element_sync_state_with_parent (GST_ELEMENT (appsink));
|
||||
|
||||
GstPad *appsink_pad =
|
||||
gst_element_get_static_pad (GST_ELEMENT (appsink), "sink");
|
||||
GstPadLinkReturn link_result = gst_pad_link (pad, appsink_pad);
|
||||
gst_clear_object (&appsink_pad);
|
||||
if (GST_PAD_LINK_FAILED (link_result)) {
|
||||
g_error ("failed to link parser to appsink: %s",
|
||||
gst_pad_link_get_name (link_result));
|
||||
}
|
||||
|
||||
Track track_template = {
|
||||
.parent = self,
|
||||
.sink = gst_object_ref (appsink),
|
||||
.src_pad = gst_object_ref (pad),
|
||||
.stream = gst_object_ref (stream),
|
||||
.mse_track = gst_media_source_track_new_with_initial_caps (track_type,
|
||||
GST_OBJECT_NAME (appsink), caps),
|
||||
.previous_pts = GST_CLOCK_TIME_NONE,
|
||||
};
|
||||
g_array_append_val (self->tracks, track_template);
|
||||
|
||||
GST_TRACE_OBJECT (self, "added appsink %s to pad %s",
|
||||
GST_OBJECT_NAME (appsink), GST_OBJECT_NAME (pad));
|
||||
|
||||
*added_track = track_template;
|
||||
|
||||
return ADDED;
|
||||
}
|
||||
|
||||
static void
|
||||
clear_track (Track * track)
|
||||
{
|
||||
gst_clear_object (&track->sink);
|
||||
gst_clear_object (&track->src_pad);
|
||||
gst_clear_object (&track->mse_track);
|
||||
gst_clear_object (&track->stream);
|
||||
}
|
||||
|
||||
static inline GPtrArray *
|
||||
init_segment_tracks_for (InitSegment * self, GstStreamType type)
|
||||
{
|
||||
switch (type) {
|
||||
case GST_STREAM_TYPE_AUDIO:
|
||||
return self->audio_tracks;
|
||||
case GST_STREAM_TYPE_TEXT:
|
||||
return self->text_tracks;
|
||||
case GST_STREAM_TYPE_VIDEO:
|
||||
return self->video_tracks;
|
||||
default:
|
||||
g_assert_not_reached ();
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
process_init_segment_track (GstPad * pad, GstAppendPipeline * self)
|
||||
{
|
||||
GST_OBJECT_LOCK (self);
|
||||
InitSegment *init_segment = &self->init_segment;
|
||||
GstStream *stream = gst_pad_get_stream (pad);
|
||||
GstCaps *caps = gst_stream_get_caps (stream);
|
||||
GST_DEBUG_OBJECT (self, "%" GST_PTR_FORMAT " got caps %" GST_PTR_FORMAT, pad,
|
||||
caps);
|
||||
|
||||
if (gst_pad_is_linked (pad)) {
|
||||
GST_TRACE_OBJECT (self, "%" GST_PTR_FORMAT " is already linked, skipping",
|
||||
pad);
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (!GST_IS_CAPS (caps)) {
|
||||
GST_ERROR_OBJECT (self, "no caps on %" GST_PTR_FORMAT
|
||||
" after stream collection", pad);
|
||||
call_parse_error (self);
|
||||
goto done;
|
||||
}
|
||||
|
||||
Track track;
|
||||
AddTrackResult result = add_track (self, pad, stream, caps, &track);
|
||||
GstMediaSourceTrack *mse_track = track.mse_track;
|
||||
switch (result) {
|
||||
case ADDED:{
|
||||
GstStreamType type = gst_stream_get_stream_type (stream);
|
||||
GPtrArray *tracks = init_segment_tracks_for (init_segment, type);
|
||||
if (tracks->len < 1) {
|
||||
gst_media_source_track_set_active (mse_track, TRUE);
|
||||
}
|
||||
g_ptr_array_add (tracks, gst_object_ref (mse_track));
|
||||
break;
|
||||
}
|
||||
case IGNORED:
|
||||
break;
|
||||
case PARSE_ERROR:
|
||||
call_parse_error (self);
|
||||
break;
|
||||
}
|
||||
|
||||
done:
|
||||
gst_clear_object (&stream);
|
||||
gst_clear_caps (&caps);
|
||||
GST_OBJECT_UNLOCK (self);
|
||||
}
|
||||
|
||||
static void
|
||||
on_pad_added (GstElement * parsebin, GstPad * pad, gpointer user_data)
|
||||
{
|
||||
GstAppendPipeline *self = GST_APPEND_PIPELINE (user_data);
|
||||
process_init_segment_track (pad, self);
|
||||
process_init_segment (self);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
has_track_for_stream (GstAppendPipeline * self, GstStream * stream)
|
||||
{
|
||||
guint track_count = n_tracks (self);
|
||||
for (guint i = 0; i < track_count; i++) {
|
||||
Track *track = index_track (self, i);
|
||||
if (track->stream == stream) {
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
has_all_tracks (GstAppendPipeline * self)
|
||||
{
|
||||
if (!GST_IS_STREAM_COLLECTION (self->streams)) {
|
||||
return FALSE;
|
||||
}
|
||||
for (guint i = 0; i < gst_stream_collection_get_size (self->streams); i++) {
|
||||
GstStream *stream = gst_stream_collection_get_stream (self->streams, i);
|
||||
switch (gst_stream_get_stream_type (stream)) {
|
||||
case GST_STREAM_TYPE_AUDIO:
|
||||
case GST_STREAM_TYPE_VIDEO:
|
||||
case GST_STREAM_TYPE_TEXT:
|
||||
break;
|
||||
default:
|
||||
continue;
|
||||
}
|
||||
if (!has_track_for_stream (self, stream)) {
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
process_init_segment (GstAppendPipeline * self)
|
||||
{
|
||||
gint64 duration;
|
||||
InitSegment *init_segment = &self->init_segment;
|
||||
|
||||
GST_OBJECT_LOCK (self);
|
||||
|
||||
if (!has_all_tracks (self)) {
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (gst_element_query_duration (self->parsebin, GST_FORMAT_TIME, &duration)) {
|
||||
init_segment->duration = MAX (0, duration);
|
||||
} else {
|
||||
init_segment->duration = GST_CLOCK_TIME_NONE;
|
||||
}
|
||||
|
||||
GST_DEBUG_OBJECT (self, "init segment says duration=%" GST_TIME_FORMAT,
|
||||
GST_TIME_ARGS ((GstClockTime) duration));
|
||||
|
||||
self->received_init_segment = TRUE;
|
||||
|
||||
call_received_init_segment (self);
|
||||
|
||||
done:
|
||||
GST_OBJECT_UNLOCK (self);
|
||||
}
|
||||
|
||||
static inline void
|
||||
init_segment_init (InitSegment * self)
|
||||
{
|
||||
self->audio_tracks = g_ptr_array_new_with_free_func (gst_object_unref);
|
||||
self->text_tracks = g_ptr_array_new_with_free_func (gst_object_unref);
|
||||
self->video_tracks = g_ptr_array_new_with_free_func (gst_object_unref);
|
||||
self->duration = GST_CLOCK_TIME_NONE;
|
||||
}
|
||||
|
||||
static inline void
|
||||
init_segment_finalize (InitSegment * self)
|
||||
{
|
||||
g_ptr_array_free (self->audio_tracks, TRUE);
|
||||
g_ptr_array_free (self->text_tracks, TRUE);
|
||||
g_ptr_array_free (self->video_tracks, TRUE);
|
||||
}
|
||||
|
||||
static GArray *
|
||||
new_tracks_array (void)
|
||||
{
|
||||
GArray *tracks = g_array_new (TRUE, TRUE, sizeof (Track));
|
||||
g_array_set_clear_func (tracks, (GDestroyNotify) clear_track);
|
||||
return tracks;
|
||||
}
|
||||
|
||||
static BackgroundTask *
|
||||
background_task_new (GstAppendPipeline * pipeline)
|
||||
{
|
||||
BackgroundTask *task = g_new0 (BackgroundTask, 1);
|
||||
g_rec_mutex_init (&task->mutex);
|
||||
task->task = gst_task_new (task_function, task, NULL);
|
||||
task->pipeline = pipeline;
|
||||
task->bus = gst_object_ref (pipeline->bus);
|
||||
gst_task_set_lock (task->task, &task->mutex);
|
||||
return task;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
background_task_start (BackgroundTask * task)
|
||||
{
|
||||
gst_bus_set_flushing (task->bus, FALSE);
|
||||
return gst_task_start (task->task);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
background_task_stop (BackgroundTask * task)
|
||||
{
|
||||
send_abort (task->pipeline);
|
||||
gst_task_join (task->task);
|
||||
gst_bus_set_flushing (task->bus, TRUE);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
background_task_cleanup (gpointer ptr)
|
||||
{
|
||||
BackgroundTask *task = (BackgroundTask *) ptr;
|
||||
background_task_stop (task);
|
||||
task->pipeline = NULL;
|
||||
gst_clear_object (&task->task);
|
||||
gst_clear_object (&task->bus);
|
||||
g_rec_mutex_clear (&task->mutex);
|
||||
g_free (task);
|
||||
}
|
||||
|
||||
static void
|
||||
gst_append_pipeline_init (GstAppendPipeline * self)
|
||||
{
|
||||
GstElement *appsrc = GST_ELEMENT (gst_element_factory_make ("appsrc", "src"));
|
||||
GstElement *parsebin =
|
||||
GST_ELEMENT (gst_element_factory_make ("parsebin", "parse"));
|
||||
GstElement *pipeline = gst_pipeline_new ("append-pipeline");
|
||||
|
||||
GstPad *appsrc_pad = GST_PAD (gst_element_get_static_pad (appsrc, "src"));
|
||||
gst_pad_add_probe (appsrc_pad, GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM,
|
||||
event_probe, self, NULL);
|
||||
gst_object_unref (appsrc_pad);
|
||||
|
||||
gst_bin_add_many (GST_BIN (pipeline), appsrc, parsebin, NULL);
|
||||
if (!gst_element_link (appsrc, parsebin)) {
|
||||
g_error ("failed to link appsrc to parsebin");
|
||||
}
|
||||
|
||||
self->bus = gst_pipeline_get_bus (GST_PIPELINE (pipeline));
|
||||
self->pipeline = gst_object_ref_sink (GST_PIPELINE (pipeline));
|
||||
self->src = GST_APP_SRC (appsrc);
|
||||
self->parsebin = parsebin;
|
||||
|
||||
self->task = background_task_new (self);
|
||||
|
||||
g_signal_connect_object (parsebin, "pad-added", G_CALLBACK (on_pad_added),
|
||||
self, 0);
|
||||
|
||||
self->received_init_segment = FALSE;
|
||||
self->encountered_error = FALSE;
|
||||
self->tracks = new_tracks_array ();
|
||||
init_segment_init (&self->init_segment);
|
||||
}
|
||||
|
||||
static void
|
||||
gst_append_pipeline_dispose (GObject * object)
|
||||
{
|
||||
GstAppendPipeline *self = (GstAppendPipeline *) object;
|
||||
|
||||
send_shutdown (self);
|
||||
g_clear_pointer (&self->task, background_task_cleanup);
|
||||
|
||||
gst_element_set_state (GST_ELEMENT (self->pipeline), GST_STATE_NULL);
|
||||
|
||||
G_OBJECT_CLASS (gst_append_pipeline_parent_class)->dispose (object);
|
||||
}
|
||||
|
||||
static void
|
||||
gst_append_pipeline_finalize (GObject * object)
|
||||
{
|
||||
GstAppendPipeline *self = (GstAppendPipeline *) object;
|
||||
|
||||
gst_clear_object (&self->pipeline);
|
||||
gst_clear_object (&self->bus);
|
||||
|
||||
init_segment_finalize (&self->init_segment);
|
||||
|
||||
g_array_free (self->tracks, TRUE);
|
||||
gst_clear_object (&self->streams);
|
||||
|
||||
G_OBJECT_CLASS (gst_append_pipeline_parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
static void
|
||||
gst_append_pipeline_class_init (GstAppendPipelineClass * klass)
|
||||
{
|
||||
GObjectClass *oclass = G_OBJECT_CLASS (klass);
|
||||
|
||||
oclass->dispose = GST_DEBUG_FUNCPTR (gst_append_pipeline_dispose);
|
||||
oclass->finalize = GST_DEBUG_FUNCPTR (gst_append_pipeline_finalize);
|
||||
}
|
||||
|
||||
GstAppendPipeline *
|
||||
gst_append_pipeline_new (GstAppendPipelineCallbacks * callbacks,
|
||||
gpointer user_data, GError ** error)
|
||||
{
|
||||
gst_mse_init_logging ();
|
||||
GstAppendPipeline *self = g_object_new (GST_TYPE_APPEND_PIPELINE, NULL);
|
||||
GstStateChangeReturn started =
|
||||
gst_element_set_state (GST_ELEMENT (self->pipeline), GST_STATE_PLAYING);
|
||||
if (started != GST_STATE_CHANGE_SUCCESS) {
|
||||
GST_ERROR_OBJECT (self, "failed to start: %s",
|
||||
gst_element_state_change_return_get_name (started));
|
||||
g_set_error (error,
|
||||
GST_MEDIA_SOURCE_ERROR, GST_MEDIA_SOURCE_ERROR_INVALID_STATE,
|
||||
"failed to start append pipeline");
|
||||
goto error;
|
||||
}
|
||||
if (callbacks) {
|
||||
self->callbacks.callbacks = *callbacks;
|
||||
self->callbacks.user_data = user_data;
|
||||
}
|
||||
if (!background_task_start (self->task)) {
|
||||
GST_ERROR_OBJECT (self, "failed to start background task");
|
||||
g_set_error (error,
|
||||
GST_MEDIA_SOURCE_ERROR, GST_MEDIA_SOURCE_ERROR_INVALID_STATE,
|
||||
"failed to start append pipeline's background task");
|
||||
goto error;
|
||||
}
|
||||
return gst_object_ref_sink (self);
|
||||
error:
|
||||
gst_clear_object (&self);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
GstFlowReturn
|
||||
gst_append_pipeline_append (GstAppendPipeline * self, GstBuffer * buffer)
|
||||
{
|
||||
g_return_val_if_fail (GST_IS_APPEND_PIPELINE (self), GST_FLOW_ERROR);
|
||||
GstFlowReturn push_result = gst_app_src_push_buffer (self->src, buffer);
|
||||
if (push_result != GST_FLOW_OK)
|
||||
return push_result;
|
||||
|
||||
if (!gst_element_send_event (GST_ELEMENT_CAST (self->src),
|
||||
new_end_of_append_event ())) {
|
||||
GST_ERROR_OBJECT (self, "failed to push end-of-append event");
|
||||
return GST_FLOW_ERROR;
|
||||
}
|
||||
return GST_FLOW_OK;
|
||||
}
|
||||
|
||||
GstFlowReturn
|
||||
gst_append_pipeline_eos (GstAppendPipeline * self)
|
||||
{
|
||||
g_return_val_if_fail (GST_IS_APPEND_PIPELINE (self), GST_FLOW_ERROR);
|
||||
return gst_app_src_end_of_stream (self->src);
|
||||
}
|
||||
|
||||
gboolean
|
||||
gst_append_pipeline_stop (GstAppendPipeline * self)
|
||||
{
|
||||
g_return_val_if_fail (GST_IS_APPEND_PIPELINE (self), FALSE);
|
||||
|
||||
GstElement *pipeline = GST_ELEMENT (self->pipeline);
|
||||
|
||||
GstStateChangeReturn stopped =
|
||||
gst_element_set_state (pipeline, GST_STATE_NULL);
|
||||
if (stopped != GST_STATE_CHANGE_SUCCESS) {
|
||||
GST_ERROR_OBJECT (self, "failed to stop: %s",
|
||||
gst_element_state_change_return_get_name (stopped));
|
||||
return FALSE;
|
||||
}
|
||||
self->received_init_segment = FALSE;
|
||||
self->encountered_error = FALSE;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
gboolean
|
||||
gst_append_pipeline_reset (GstAppendPipeline * self)
|
||||
{
|
||||
g_return_val_if_fail (GST_IS_APPEND_PIPELINE (self), FALSE);
|
||||
|
||||
GstElement *pipeline = GST_ELEMENT (self->pipeline);
|
||||
|
||||
GstStateChangeReturn stopped =
|
||||
gst_element_set_state (pipeline, GST_STATE_READY);
|
||||
if (stopped != GST_STATE_CHANGE_SUCCESS) {
|
||||
GST_ERROR_OBJECT (self, "failed to stop: %s",
|
||||
gst_element_state_change_return_get_name (stopped));
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
background_task_stop (self->task);
|
||||
|
||||
init_segment_finalize (&self->init_segment);
|
||||
gst_clear_object (&self->streams);
|
||||
g_array_free (self->tracks, TRUE);
|
||||
|
||||
self->received_init_segment = FALSE;
|
||||
self->have_outstanding_samples = FALSE;
|
||||
self->encountered_error = FALSE;
|
||||
self->tracks = new_tracks_array ();
|
||||
init_segment_init (&self->init_segment);
|
||||
|
||||
if (!background_task_start (self->task)) {
|
||||
GST_ERROR_OBJECT (self, "failed to start background task");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
GstStateChangeReturn started =
|
||||
gst_element_set_state (pipeline, GST_STATE_PLAYING);
|
||||
if (started == GST_STATE_CHANGE_SUCCESS) {
|
||||
return TRUE;
|
||||
} else {
|
||||
GST_ERROR_OBJECT (self, "failed to start: %s",
|
||||
gst_element_state_change_return_get_name (started));
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
gsize
|
||||
gst_append_pipeline_n_tracks (GstAppendPipeline * self)
|
||||
{
|
||||
g_return_val_if_fail (GST_IS_APPEND_PIPELINE (self), 0);
|
||||
return n_tracks (self);
|
||||
}
|
||||
|
||||
gboolean
|
||||
gst_append_pipeline_has_init_segment (GstAppendPipeline * self)
|
||||
{
|
||||
g_return_val_if_fail (GST_IS_APPEND_PIPELINE (self), FALSE);
|
||||
return self->received_init_segment;
|
||||
}
|
||||
|
||||
GstClockTime
|
||||
gst_append_pipeline_get_duration (GstAppendPipeline * self)
|
||||
{
|
||||
g_return_val_if_fail (GST_IS_APPEND_PIPELINE (self), GST_CLOCK_TIME_NONE);
|
||||
if (self->received_init_segment) {
|
||||
return self->init_segment.duration;
|
||||
} else {
|
||||
return GST_CLOCK_TIME_NONE;
|
||||
}
|
||||
}
|
||||
|
||||
GPtrArray *
|
||||
gst_append_pipeline_get_audio_tracks (GstAppendPipeline * self)
|
||||
{
|
||||
g_return_val_if_fail (GST_IS_APPEND_PIPELINE (self), NULL);
|
||||
if (self->received_init_segment) {
|
||||
return self->init_segment.audio_tracks;
|
||||
} else {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
GPtrArray *
|
||||
gst_append_pipeline_get_text_tracks (GstAppendPipeline * self)
|
||||
{
|
||||
g_return_val_if_fail (GST_IS_APPEND_PIPELINE (self), NULL);
|
||||
if (self->received_init_segment) {
|
||||
return self->init_segment.text_tracks;
|
||||
} else {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
GPtrArray *
|
||||
gst_append_pipeline_get_video_tracks (GstAppendPipeline * self)
|
||||
{
|
||||
g_return_val_if_fail (GST_IS_APPEND_PIPELINE (self), NULL);
|
||||
if (self->received_init_segment) {
|
||||
return self->init_segment.video_tracks;
|
||||
} else {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
gboolean
|
||||
gst_append_pipeline_get_eos (GstAppendPipeline * self)
|
||||
{
|
||||
g_return_val_if_fail (GST_IS_APPEND_PIPELINE (self), FALSE);
|
||||
return gst_task_get_state (self->task->task) != GST_TASK_STARTED;
|
||||
}
|
||||
|
||||
void
|
||||
gst_append_pipeline_fail (GstAppendPipeline * self)
|
||||
{
|
||||
g_return_if_fail (GST_IS_APPEND_PIPELINE (self));
|
||||
gst_bus_post (self->bus, gst_message_new_error (NULL, NULL, NULL));
|
||||
}
|
||||
|
||||
gboolean
|
||||
gst_append_pipeline_get_failed (GstAppendPipeline * self)
|
||||
{
|
||||
g_return_val_if_fail (GST_IS_APPEND_PIPELINE (self), FALSE);
|
||||
return self->encountered_error;
|
||||
}
|
|
@ -0,0 +1,58 @@
|
|||
/* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "gstmediasource.h"
|
||||
#include "gstmseeventqueue-private.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
GST_MSE_PRIVATE
|
||||
gboolean gst_media_source_is_attached (GstMediaSource * self);
|
||||
|
||||
GST_MSE_PRIVATE
|
||||
void gst_media_source_open (GstMediaSource * self);
|
||||
|
||||
GST_MSE_PRIVATE
|
||||
GstMseSrc * gst_media_source_get_source_element (GstMediaSource * self);
|
||||
|
||||
GST_MSE_PRIVATE
|
||||
void gst_media_source_seek (GstMediaSource * self, GstClockTime time);
|
||||
|
||||
struct _GstMediaSource
|
||||
{
|
||||
GstObject parent_instance;
|
||||
|
||||
GstMseSrc *element;
|
||||
GstMseEventQueue *event_queue;
|
||||
|
||||
GstSourceBufferList *buffers;
|
||||
GstSourceBufferList *active_buffers;
|
||||
|
||||
GstMediaSourceRange live_seekable_range;
|
||||
|
||||
GstClockTime duration;
|
||||
GstMediaSourceReadyState ready_state;
|
||||
};
|
||||
|
||||
G_END_DECLS
|
1063
subprojects/gst-plugins-bad/gst-libs/gst/mse/gstmediasource.c
Normal file
1063
subprojects/gst-plugins-bad/gst-libs/gst/mse/gstmediasource.c
Normal file
File diff suppressed because it is too large
Load diff
203
subprojects/gst-plugins-bad/gst-libs/gst/mse/gstmediasource.h
Normal file
203
subprojects/gst-plugins-bad/gst-libs/gst/mse/gstmediasource.h
Normal file
|
@ -0,0 +1,203 @@
|
|||
/* GStreamer
|
||||
*
|
||||
* SPDX-License-Identifier: LGPL-2.1
|
||||
*
|
||||
* Copyright (C) 2013 Google Inc. All rights reserved.
|
||||
* Copyright (C) 2013 Orange
|
||||
* Copyright (C) 2013-2017 Apple Inc. All rights reserved.
|
||||
* Copyright (C) 2014 Sebastian Dröge <sebastian@centricular.com>
|
||||
* Copyright (C) 2015, 2016 Igalia, S.L
|
||||
* Copyright (C) 2015, 2016 Metrological Group B.V.
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <gst/gst.h>
|
||||
#include <gst/mse/mse-prelude.h>
|
||||
#include <gst/mse/gstmsesrc.h>
|
||||
|
||||
#include "gstsourcebufferlist.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
/**
|
||||
* GstMediaSourceReadyState:
|
||||
* @GST_MEDIA_SOURCE_READY_STATE_CLOSED: The #GstMediaSource is not connected to
|
||||
* any playback element.
|
||||
* @GST_MEDIA_SOURCE_READY_STATE_OPEN: The #GstMediaSource is connected to a
|
||||
* playback element and ready to append data to its #GstSourceBuffer (s).
|
||||
* @GST_MEDIA_SOURCE_READY_STATE_ENDED: gst_media_source_end_of_stream() has
|
||||
* been called on the current #GstMediaSource
|
||||
*
|
||||
* Describes the possible states of the Media Source.
|
||||
*
|
||||
* [Specification](https://www.w3.org/TR/media-source-2/#dom-readystate)
|
||||
*
|
||||
* Since: 1.24
|
||||
*/
|
||||
typedef enum
|
||||
{
|
||||
GST_MEDIA_SOURCE_READY_STATE_CLOSED,
|
||||
GST_MEDIA_SOURCE_READY_STATE_OPEN,
|
||||
GST_MEDIA_SOURCE_READY_STATE_ENDED,
|
||||
} GstMediaSourceReadyState;
|
||||
|
||||
/**
|
||||
* GstMediaSourceError:
|
||||
* @GST_MEDIA_SOURCE_ERROR_INVALID_STATE:
|
||||
* @GST_MEDIA_SOURCE_ERROR_TYPE:
|
||||
* @GST_MEDIA_SOURCE_ERROR_NOT_SUPPORTED:
|
||||
* @GST_MEDIA_SOURCE_ERROR_NOT_FOUND:
|
||||
* @GST_MEDIA_SOURCE_ERROR_QUOTA_EXCEEDED:
|
||||
*
|
||||
* Any error that can occur within #GstMediaSource or #GstSourceBuffer APIs.
|
||||
* These values correspond directly to those in the Web IDL specification.
|
||||
*
|
||||
* [Specification](https://webidl.spec.whatwg.org/#idl-DOMException-error-names)
|
||||
*
|
||||
* Since: 1.24
|
||||
*/
|
||||
typedef enum
|
||||
{
|
||||
GST_MEDIA_SOURCE_ERROR_INVALID_STATE,
|
||||
GST_MEDIA_SOURCE_ERROR_TYPE,
|
||||
GST_MEDIA_SOURCE_ERROR_NOT_SUPPORTED,
|
||||
GST_MEDIA_SOURCE_ERROR_NOT_FOUND,
|
||||
GST_MEDIA_SOURCE_ERROR_QUOTA_EXCEEDED,
|
||||
} GstMediaSourceError;
|
||||
|
||||
/**
|
||||
* GstMediaSourceEOSError:
|
||||
* @GST_MEDIA_SOURCE_EOS_ERROR_NONE: End the stream successfully
|
||||
* @GST_MEDIA_SOURCE_EOS_ERROR_NETWORK: End the stream due to a networking error
|
||||
* @GST_MEDIA_SOURCE_EOS_ERROR_DECODE: End the stream due to a decoding error
|
||||
*
|
||||
* Reasons for ending a #GstMediaSource using gst_media_source_end_of_stream().
|
||||
*
|
||||
* [Specification](https://www.w3.org/TR/media-source-2/#dom-endofstreamerror)
|
||||
*
|
||||
* Since: 1.24
|
||||
*/
|
||||
typedef enum
|
||||
{
|
||||
GST_MEDIA_SOURCE_EOS_ERROR_NONE,
|
||||
GST_MEDIA_SOURCE_EOS_ERROR_NETWORK,
|
||||
GST_MEDIA_SOURCE_EOS_ERROR_DECODE,
|
||||
} GstMediaSourceEOSError;
|
||||
|
||||
/**
|
||||
* GstMediaSourceRange:
|
||||
* @start: The start of this range.
|
||||
* @end: The end of this range.
|
||||
*
|
||||
* A structure describing a simplified version of the TimeRanges concept in the
|
||||
* HTML specification, only representing a single @start and @end time.
|
||||
*
|
||||
* [Specification](https://html.spec.whatwg.org/multipage/media.html#timeranges)
|
||||
*
|
||||
* Since: 1.24
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
GstClockTime start;
|
||||
GstClockTime end;
|
||||
} GstMediaSourceRange;
|
||||
|
||||
GST_MSE_API
|
||||
gboolean gst_media_source_is_type_supported (const gchar * type);
|
||||
|
||||
#define GST_TYPE_MEDIA_SOURCE (gst_media_source_get_type())
|
||||
#define GST_MEDIA_SOURCE_ERROR (gst_media_source_error_quark())
|
||||
|
||||
/**
|
||||
* gst_media_source_error_quark:
|
||||
*
|
||||
* Any error type that can be reported by the Media Source API.
|
||||
*
|
||||
* Since: 1.24
|
||||
*/
|
||||
GST_MSE_API
|
||||
GQuark gst_media_source_error_quark (void);
|
||||
|
||||
GST_MSE_API
|
||||
G_DECLARE_FINAL_TYPE (GstMediaSource, gst_media_source, GST, MEDIA_SOURCE,
|
||||
GstObject);
|
||||
|
||||
GST_MSE_API
|
||||
GstMediaSource *gst_media_source_new (void);
|
||||
|
||||
GST_MSE_API
|
||||
void gst_media_source_attach (GstMediaSource * self, GstMseSrc * element);
|
||||
|
||||
GST_MSE_API
|
||||
void gst_media_source_detach (GstMediaSource * self);
|
||||
|
||||
GST_MSE_API
|
||||
GstSourceBufferList * gst_media_source_get_source_buffers (
|
||||
GstMediaSource * self);
|
||||
|
||||
GST_MSE_API
|
||||
GstSourceBufferList * gst_media_source_get_active_source_buffers (
|
||||
GstMediaSource * self);
|
||||
|
||||
GST_MSE_API
|
||||
GstMediaSourceReadyState gst_media_source_get_ready_state (
|
||||
GstMediaSource * self);
|
||||
|
||||
GST_MSE_API
|
||||
GstClockTime gst_media_source_get_position (GstMediaSource * self);
|
||||
|
||||
GST_MSE_API
|
||||
GstClockTime gst_media_source_get_duration (GstMediaSource * self);
|
||||
|
||||
GST_MSE_API
|
||||
gboolean gst_media_source_set_duration (GstMediaSource * self,
|
||||
GstClockTime duration,
|
||||
GError ** error);
|
||||
|
||||
GST_MSE_API
|
||||
GstSourceBuffer * gst_media_source_add_source_buffer (GstMediaSource * self,
|
||||
const gchar * type,
|
||||
GError ** error);
|
||||
|
||||
GST_MSE_API
|
||||
gboolean gst_media_source_remove_source_buffer (GstMediaSource * self,
|
||||
GstSourceBuffer * buffer,
|
||||
GError ** error);
|
||||
|
||||
GST_MSE_API
|
||||
gboolean gst_media_source_end_of_stream (GstMediaSource * self,
|
||||
GstMediaSourceEOSError eos_error,
|
||||
GError ** error);
|
||||
|
||||
GST_MSE_API
|
||||
gboolean gst_media_source_set_live_seekable_range (GstMediaSource * self,
|
||||
GstClockTime start,
|
||||
GstClockTime end,
|
||||
GError ** error);
|
||||
|
||||
GST_MSE_API
|
||||
gboolean gst_media_source_clear_live_seekable_range (GstMediaSource * self,
|
||||
GError ** error);
|
||||
|
||||
GST_MSE_API
|
||||
void gst_media_source_get_live_seekable_range (GstMediaSource * self,
|
||||
GstMediaSourceRange * range);
|
||||
|
||||
G_END_DECLS
|
|
@ -0,0 +1,94 @@
|
|||
/* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <gst/gst.h>
|
||||
#include <glib-object.h>
|
||||
#include <gst/mse/mse-prelude.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GST_TYPE_MEDIA_SOURCE_SAMPLE_MAP \
|
||||
(gst_media_source_sample_map_get_type())
|
||||
|
||||
GST_MSE_PRIVATE
|
||||
G_DECLARE_FINAL_TYPE (GstMediaSourceSampleMap, gst_media_source_sample_map, GST,
|
||||
MEDIA_SOURCE_SAMPLE_MAP, GstObject);
|
||||
|
||||
GST_MSE_PRIVATE
|
||||
GstMediaSourceSampleMap *gst_media_source_sample_map_new (void);
|
||||
|
||||
GST_MSE_PRIVATE
|
||||
void gst_media_source_sample_map_add (GstMediaSourceSampleMap * self,
|
||||
GstSample * sample);
|
||||
|
||||
GST_MSE_PRIVATE
|
||||
void gst_media_source_sample_map_remove (GstMediaSourceSampleMap * self,
|
||||
GstSample * sample);
|
||||
|
||||
GST_MSE_PRIVATE
|
||||
gsize gst_media_source_sample_map_remove_range (GstMediaSourceSampleMap * self,
|
||||
GstClockTime earliest, GstClockTime latest);
|
||||
|
||||
GST_MSE_PRIVATE
|
||||
gsize
|
||||
gst_media_source_sample_map_remove_range_from_start (GstMediaSourceSampleMap
|
||||
* self, GstClockTime latest_dts);
|
||||
|
||||
GST_MSE_PRIVATE
|
||||
gsize
|
||||
gst_media_source_sample_map_remove_range_from_end (GstMediaSourceSampleMap
|
||||
* self, GstClockTime earliest_dts);
|
||||
|
||||
GST_MSE_PRIVATE
|
||||
gboolean gst_media_source_sample_map_contains (GstMediaSourceSampleMap * self,
|
||||
GstSample * sample);
|
||||
|
||||
GST_MSE_PRIVATE
|
||||
GSequenceIter * gst_media_source_sample_map_get_begin_iter_by_pts (
|
||||
GstMediaSourceSampleMap * self);
|
||||
|
||||
GST_MSE_PRIVATE
|
||||
GstClockTime gst_media_source_sample_map_get_highest_end_time (
|
||||
GstMediaSourceSampleMap * self);
|
||||
|
||||
GST_MSE_PRIVATE
|
||||
guint gst_media_source_sample_map_get_size (GstMediaSourceSampleMap * self);
|
||||
|
||||
GST_MSE_PRIVATE
|
||||
gsize gst_media_source_sample_map_get_storage_size (
|
||||
GstMediaSourceSampleMap * self);
|
||||
|
||||
GST_MSE_PRIVATE
|
||||
GstIterator *
|
||||
gst_media_source_sample_map_iter_samples_by_dts (GstMediaSourceSampleMap * map,
|
||||
GMutex * lock, guint32 * master_cookie, GstClockTime start_dts,
|
||||
GstSample * start_sample);
|
||||
|
||||
GST_MSE_PRIVATE
|
||||
GstIterator *
|
||||
gst_media_source_sample_map_iter_samples_by_pts (GstMediaSourceSampleMap * map,
|
||||
GMutex * lock, guint32 * master_cookie, GstClockTime start_pts,
|
||||
GstSample *start_sample);
|
||||
|
||||
G_END_DECLS
|
|
@ -0,0 +1,551 @@
|
|||
/* 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);
|
||||
}
|
|
@ -0,0 +1,110 @@
|
|||
/* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <gst/gst.h>
|
||||
#include <gst/mse/mse-prelude.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
typedef enum
|
||||
{
|
||||
GST_MEDIA_SOURCE_TRACK_TYPE_AUDIO,
|
||||
GST_MEDIA_SOURCE_TRACK_TYPE_VIDEO,
|
||||
GST_MEDIA_SOURCE_TRACK_TYPE_TEXT,
|
||||
GST_MEDIA_SOURCE_TRACK_TYPE_OTHER,
|
||||
} GstMediaSourceTrackType;
|
||||
|
||||
#define GST_TYPE_MEDIA_SOURCE_TRACK (gst_media_source_track_get_type())
|
||||
|
||||
GST_MSE_PRIVATE
|
||||
G_DECLARE_FINAL_TYPE (GstMediaSourceTrack, gst_media_source_track, GST,
|
||||
MEDIA_SOURCE_TRACK, GstObject);
|
||||
|
||||
GST_MSE_PRIVATE
|
||||
GstMediaSourceTrackType gst_media_source_track_get_track_type (
|
||||
GstMediaSourceTrack * self);
|
||||
|
||||
GST_MSE_PRIVATE
|
||||
const gchar *
|
||||
gst_media_source_track_get_id (GstMediaSourceTrack * self);
|
||||
|
||||
GST_MSE_PRIVATE
|
||||
gboolean gst_media_source_track_get_active (GstMediaSourceTrack * self);
|
||||
|
||||
GST_MSE_PRIVATE
|
||||
GstMediaSourceTrack *gst_media_source_track_new (GstMediaSourceTrackType type,
|
||||
const gchar * track_id);
|
||||
|
||||
GST_MSE_PRIVATE
|
||||
GstMediaSourceTrack *
|
||||
gst_media_source_track_new_with_initial_caps (GstMediaSourceTrackType type,
|
||||
const gchar * track_id, GstCaps * initial_caps);
|
||||
|
||||
GST_MSE_PRIVATE
|
||||
GstMediaSourceTrack * gst_media_source_track_new_with_size (
|
||||
GstMediaSourceTrackType type, const gchar * track_id, gsize size);
|
||||
|
||||
GST_MSE_PRIVATE
|
||||
GstCaps *gst_media_source_track_get_initial_caps (GstMediaSourceTrack * self);
|
||||
|
||||
GST_MSE_PRIVATE
|
||||
GstStreamType
|
||||
gst_media_source_track_get_stream_type (GstMediaSourceTrack * self);
|
||||
|
||||
GST_MSE_API
|
||||
void gst_media_source_track_set_active (GstMediaSourceTrack * self,
|
||||
gboolean active);
|
||||
|
||||
GST_MSE_PRIVATE
|
||||
GstMiniObject *gst_media_source_track_pop (GstMediaSourceTrack * self);
|
||||
|
||||
GST_MSE_PRIVATE
|
||||
gboolean gst_media_source_track_push (GstMediaSourceTrack * self,
|
||||
GstSample * sample);
|
||||
|
||||
GST_MSE_PRIVATE
|
||||
gboolean gst_media_source_track_try_push (GstMediaSourceTrack * self,
|
||||
GstSample * sample);
|
||||
|
||||
GST_MSE_PRIVATE
|
||||
gboolean gst_media_source_track_push_eos (GstMediaSourceTrack * self);
|
||||
|
||||
GST_MSE_PRIVATE
|
||||
void gst_media_source_track_flush (GstMediaSourceTrack * self);
|
||||
|
||||
GST_MSE_PRIVATE
|
||||
void gst_media_source_track_resume (GstMediaSourceTrack * self);
|
||||
|
||||
GST_MSE_PRIVATE
|
||||
gboolean gst_media_source_track_is_empty (GstMediaSourceTrack * self);
|
||||
|
||||
GST_MSE_PRIVATE
|
||||
GstStreamType gst_media_source_track_type_to_stream_type (
|
||||
GstMediaSourceTrackType type);
|
||||
|
||||
GST_MSE_PRIVATE
|
||||
GstMediaSourceTrackType gst_media_source_track_type_from_stream_type (
|
||||
GstStreamType type);
|
||||
|
||||
G_END_DECLS
|
|
@ -0,0 +1,458 @@
|
|||
/* 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 <gst/mse/mse-enumtypes.h>
|
||||
#include <gst/mse/mse-enumtypes-private.h>
|
||||
#include "gstmediasourcetrack-private.h"
|
||||
|
||||
#include "gstmselogging-private.h"
|
||||
|
||||
#include <gst/base/gstdataqueue.h>
|
||||
|
||||
struct _GstMediaSourceTrack
|
||||
{
|
||||
GstObject parent_instance;
|
||||
|
||||
GstMediaSourceTrackType track_type;
|
||||
gchar *track_id;
|
||||
gboolean active;
|
||||
GstCaps *initial_caps;
|
||||
|
||||
gsize queue_size;
|
||||
GstDataQueue *samples;
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE (GstMediaSourceTrack, gst_media_source_track, GST_TYPE_OBJECT);
|
||||
|
||||
#define DEFAULT_QUEUE_SIZE (1 << 10)
|
||||
|
||||
enum
|
||||
{
|
||||
PROP_0,
|
||||
|
||||
PROP_TRACK_TYPE,
|
||||
PROP_TRACK_ID,
|
||||
PROP_ACTIVE,
|
||||
PROP_INITIAL_CAPS,
|
||||
PROP_QUEUE_SIZE,
|
||||
|
||||
N_PROPS,
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
ON_NOT_EMPTY,
|
||||
|
||||
N_SIGNALS,
|
||||
};
|
||||
|
||||
static GParamSpec *properties[N_PROPS];
|
||||
static guint signals[N_SIGNALS];
|
||||
|
||||
static GstMediaSourceTrack *
|
||||
_gst_media_source_track_new_full (GstMediaSourceTrackType type,
|
||||
const gchar * track_id, gsize size, GstCaps * initial_caps)
|
||||
{
|
||||
g_return_val_if_fail (type >= 0, NULL);
|
||||
g_return_val_if_fail (type <= GST_MEDIA_SOURCE_TRACK_TYPE_OTHER, NULL);
|
||||
|
||||
gst_mse_init_logging ();
|
||||
|
||||
GstMediaSourceTrack *self = g_object_new (GST_TYPE_MEDIA_SOURCE_TRACK,
|
||||
"track-type", type, "track-id", track_id, "queue-size", size,
|
||||
"initial-caps", initial_caps, NULL);
|
||||
|
||||
return gst_object_ref_sink (self);
|
||||
}
|
||||
|
||||
GstMediaSourceTrack *
|
||||
gst_media_source_track_new_with_size (GstMediaSourceTrackType type,
|
||||
const gchar * track_id, gsize size)
|
||||
{
|
||||
return _gst_media_source_track_new_full (type, track_id, size, NULL);
|
||||
}
|
||||
|
||||
GstMediaSourceTrack *
|
||||
gst_media_source_track_new (GstMediaSourceTrackType type,
|
||||
const gchar * track_id)
|
||||
{
|
||||
return _gst_media_source_track_new_full (type, track_id, DEFAULT_QUEUE_SIZE,
|
||||
NULL);
|
||||
}
|
||||
|
||||
GstMediaSourceTrack *
|
||||
gst_media_source_track_new_with_initial_caps (GstMediaSourceTrackType type,
|
||||
const gchar * track_id, GstCaps * initial_caps)
|
||||
{
|
||||
g_return_val_if_fail (GST_IS_CAPS (initial_caps), NULL);
|
||||
return _gst_media_source_track_new_full (type, track_id, DEFAULT_QUEUE_SIZE,
|
||||
initial_caps);
|
||||
}
|
||||
|
||||
static void
|
||||
gst_media_source_track_finalize (GObject * object)
|
||||
{
|
||||
GstMediaSourceTrack *self = (GstMediaSourceTrack *) object;
|
||||
|
||||
g_free (self->track_id);
|
||||
gst_clear_caps (&self->initial_caps);
|
||||
g_object_unref (self->samples);
|
||||
|
||||
G_OBJECT_CLASS (gst_media_source_track_parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
static void
|
||||
gst_media_source_track_get_property (GObject * object, guint prop_id,
|
||||
GValue * value, GParamSpec * pspec)
|
||||
{
|
||||
GstMediaSourceTrack *self = GST_MEDIA_SOURCE_TRACK (object);
|
||||
|
||||
switch (prop_id) {
|
||||
case PROP_TRACK_TYPE:
|
||||
g_value_set_enum (value, gst_media_source_track_get_track_type (self));
|
||||
break;
|
||||
case PROP_TRACK_ID:
|
||||
g_value_set_static_string (value, gst_media_source_track_get_id (self));
|
||||
break;
|
||||
case PROP_ACTIVE:
|
||||
g_value_set_boolean (value, gst_media_source_track_get_active (self));
|
||||
break;
|
||||
case PROP_INITIAL_CAPS:
|
||||
g_value_take_boxed (value,
|
||||
gst_media_source_track_get_initial_caps (self));
|
||||
break;
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gst_media_source_track_set_property (GObject * object, guint prop_id,
|
||||
const GValue * value, GParamSpec * pspec)
|
||||
{
|
||||
GstMediaSourceTrack *self = GST_MEDIA_SOURCE_TRACK (object);
|
||||
|
||||
switch (prop_id) {
|
||||
case PROP_TRACK_TYPE:
|
||||
self->track_type = g_value_get_enum (value);
|
||||
break;
|
||||
case PROP_TRACK_ID:
|
||||
self->track_id = g_value_dup_string (value);
|
||||
break;
|
||||
case PROP_ACTIVE:
|
||||
gst_media_source_track_set_active (self, g_value_get_boolean (value));
|
||||
break;
|
||||
case PROP_INITIAL_CAPS:{
|
||||
const GstCaps *caps = gst_value_get_caps (value);
|
||||
self->initial_caps = caps == NULL ? NULL : gst_caps_copy (caps);
|
||||
break;
|
||||
}
|
||||
case PROP_QUEUE_SIZE:{
|
||||
self->queue_size = g_value_get_ulong (value);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gst_media_source_track_class_init (GstMediaSourceTrackClass * klass)
|
||||
{
|
||||
GObjectClass *oclass = G_OBJECT_CLASS (klass);
|
||||
|
||||
oclass->finalize = GST_DEBUG_FUNCPTR (gst_media_source_track_finalize);
|
||||
oclass->get_property =
|
||||
GST_DEBUG_FUNCPTR (gst_media_source_track_get_property);
|
||||
oclass->set_property =
|
||||
GST_DEBUG_FUNCPTR (gst_media_source_track_set_property);
|
||||
|
||||
signals[ON_NOT_EMPTY] = g_signal_new ("on-not-empty",
|
||||
G_TYPE_FROM_CLASS (klass),
|
||||
G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 0);
|
||||
|
||||
properties[PROP_TRACK_TYPE] =
|
||||
g_param_spec_enum ("track-type", "Track Type",
|
||||
"Type of media in this Track, either Audio, Video, Text, or Other.",
|
||||
GST_TYPE_MEDIA_SOURCE_TRACK_TYPE, GST_MEDIA_SOURCE_TRACK_TYPE_OTHER,
|
||||
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
|
||||
|
||||
properties[PROP_TRACK_ID] =
|
||||
g_param_spec_string ("track-id", "Track ID",
|
||||
"Identifier for this Track that must be unique within a Source Buffer.",
|
||||
"", G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
|
||||
|
||||
properties[PROP_ACTIVE] =
|
||||
g_param_spec_boolean ("active", "Active",
|
||||
"Whether this Track requires its parent Source Buffer to be in its "
|
||||
"parent Media Source's Active Source Buffers list", FALSE,
|
||||
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
|
||||
|
||||
properties[PROP_INITIAL_CAPS] =
|
||||
g_param_spec_boxed ("initial-caps", "Initial Caps",
|
||||
"GstCaps discovered in the first Initialization Segment",
|
||||
GST_TYPE_CAPS, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
|
||||
G_PARAM_STATIC_STRINGS);
|
||||
|
||||
properties[PROP_QUEUE_SIZE] =
|
||||
g_param_spec_ulong ("queue-size", "Queue Size",
|
||||
"Maximum Track Queue size",
|
||||
0, G_MAXULONG, DEFAULT_QUEUE_SIZE,
|
||||
G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
|
||||
|
||||
g_object_class_install_properties (oclass, N_PROPS, properties);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
check_queue_depth (GstDataQueue * queue, guint visible, guint bytes,
|
||||
guint64 time, gpointer checkdata)
|
||||
{
|
||||
GstMediaSourceTrack *self = GST_MEDIA_SOURCE_TRACK (checkdata);
|
||||
return visible >= self->queue_size;
|
||||
}
|
||||
|
||||
static void
|
||||
gst_media_source_track_init (GstMediaSourceTrack * self)
|
||||
{
|
||||
self->samples = gst_data_queue_new (check_queue_depth, NULL, NULL, self);
|
||||
}
|
||||
|
||||
GstMediaSourceTrackType
|
||||
gst_media_source_track_get_track_type (GstMediaSourceTrack * self)
|
||||
{
|
||||
g_return_val_if_fail (GST_IS_MEDIA_SOURCE_TRACK (self),
|
||||
GST_MEDIA_SOURCE_TRACK_TYPE_OTHER);
|
||||
return self->track_type;
|
||||
}
|
||||
|
||||
GstStreamType
|
||||
gst_media_source_track_get_stream_type (GstMediaSourceTrack * self)
|
||||
{
|
||||
g_return_val_if_fail (GST_IS_MEDIA_SOURCE_TRACK (self),
|
||||
GST_STREAM_TYPE_UNKNOWN);
|
||||
return gst_media_source_track_type_to_stream_type (self->track_type);
|
||||
}
|
||||
|
||||
const gchar *
|
||||
gst_media_source_track_get_id (GstMediaSourceTrack * self)
|
||||
{
|
||||
g_return_val_if_fail (GST_IS_MEDIA_SOURCE_TRACK (self), NULL);
|
||||
return self->track_id;
|
||||
}
|
||||
|
||||
GstCaps *
|
||||
gst_media_source_track_get_initial_caps (GstMediaSourceTrack * self)
|
||||
{
|
||||
g_return_val_if_fail (GST_IS_MEDIA_SOURCE_TRACK (self), NULL);
|
||||
if (GST_IS_CAPS (self->initial_caps)) {
|
||||
return self->initial_caps;
|
||||
} else {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
gboolean
|
||||
gst_media_source_track_get_active (GstMediaSourceTrack * self)
|
||||
{
|
||||
g_return_val_if_fail (GST_IS_MEDIA_SOURCE_TRACK (self), FALSE);
|
||||
return self->active;
|
||||
}
|
||||
|
||||
void
|
||||
gst_media_source_track_set_active (GstMediaSourceTrack * self, gboolean active)
|
||||
{
|
||||
g_return_if_fail (GST_IS_MEDIA_SOURCE_TRACK (self));
|
||||
self->active = active;
|
||||
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_ACTIVE]);
|
||||
}
|
||||
|
||||
GstMiniObject *
|
||||
gst_media_source_track_pop (GstMediaSourceTrack * self)
|
||||
{
|
||||
g_return_val_if_fail (GST_IS_MEDIA_SOURCE_TRACK (self), NULL);
|
||||
GstDataQueueItem *item = NULL;
|
||||
if (!gst_data_queue_pop (self->samples, &item)) {
|
||||
return NULL;
|
||||
}
|
||||
GstMiniObject *object = item->object;
|
||||
item->object = NULL;
|
||||
item->destroy (item);
|
||||
return object;
|
||||
}
|
||||
|
||||
static inline gsize
|
||||
sample_size (GstSample * sample)
|
||||
{
|
||||
GstBuffer *buffer = gst_sample_get_buffer (sample);
|
||||
return gst_buffer_get_size (buffer);
|
||||
}
|
||||
|
||||
static inline GstClockTime
|
||||
sample_duration (GstSample * sample)
|
||||
{
|
||||
GstBuffer *buffer = gst_sample_get_buffer (sample);
|
||||
return GST_BUFFER_DURATION (buffer);
|
||||
}
|
||||
|
||||
static void
|
||||
destroy_item (GstDataQueueItem * item)
|
||||
{
|
||||
gst_clear_mini_object (&item->object);
|
||||
g_free (item);
|
||||
}
|
||||
|
||||
static inline GstDataQueueItem *
|
||||
wrap_sample (GstSample * sample)
|
||||
{
|
||||
GstDataQueueItem item = {
|
||||
.object = GST_MINI_OBJECT (sample),
|
||||
.size = sample_size (sample),
|
||||
.duration = sample_duration (sample),
|
||||
.destroy = (GDestroyNotify) destroy_item,
|
||||
.visible = TRUE,
|
||||
};
|
||||
return g_memdup2 (&item, sizeof (GstDataQueueItem));
|
||||
}
|
||||
|
||||
gboolean
|
||||
gst_media_source_track_push (GstMediaSourceTrack * self, GstSample * sample)
|
||||
{
|
||||
g_return_val_if_fail (GST_IS_MEDIA_SOURCE_TRACK (self), FALSE);
|
||||
g_return_val_if_fail (GST_IS_SAMPLE (sample), FALSE);
|
||||
|
||||
gboolean was_empty = gst_media_source_track_is_empty (self);
|
||||
|
||||
GstDataQueueItem *item = wrap_sample (sample);
|
||||
|
||||
gboolean result = gst_data_queue_push (self->samples, item);
|
||||
|
||||
if (result) {
|
||||
if (was_empty) {
|
||||
g_signal_emit (self, signals[ON_NOT_EMPTY], 0);
|
||||
}
|
||||
return TRUE;
|
||||
} else {
|
||||
item->destroy (item);
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
static inline GstDataQueueItem *
|
||||
wrap_eos (void)
|
||||
{
|
||||
GstEvent *event = gst_event_ref (gst_event_new_eos ());
|
||||
|
||||
GstDataQueueItem item = {
|
||||
.object = GST_MINI_OBJECT (event),
|
||||
.size = 0,
|
||||
.duration = 0,
|
||||
.destroy = (GDestroyNotify) destroy_item,
|
||||
.visible = TRUE,
|
||||
};
|
||||
return g_memdup2 (&item, sizeof (GstDataQueueItem));
|
||||
}
|
||||
|
||||
gboolean
|
||||
gst_media_source_track_push_eos (GstMediaSourceTrack * self)
|
||||
{
|
||||
g_return_val_if_fail (GST_IS_MEDIA_SOURCE_TRACK (self), FALSE);
|
||||
|
||||
GstDataQueueItem *item = wrap_eos ();
|
||||
|
||||
gboolean result = gst_data_queue_push (self->samples, item);
|
||||
|
||||
if (result) {
|
||||
return TRUE;
|
||||
} else {
|
||||
item->destroy (item);
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
gst_media_source_track_flush (GstMediaSourceTrack * self)
|
||||
{
|
||||
g_return_if_fail (GST_IS_MEDIA_SOURCE_TRACK (self));
|
||||
gst_data_queue_set_flushing (self->samples, TRUE);
|
||||
gst_data_queue_flush (self->samples);
|
||||
}
|
||||
|
||||
void
|
||||
gst_media_source_track_resume (GstMediaSourceTrack * self)
|
||||
{
|
||||
g_return_if_fail (GST_IS_MEDIA_SOURCE_TRACK (self));
|
||||
gst_data_queue_set_flushing (self->samples, FALSE);
|
||||
}
|
||||
|
||||
gboolean
|
||||
gst_media_source_track_try_push (GstMediaSourceTrack * self, GstSample * sample)
|
||||
{
|
||||
g_return_val_if_fail (GST_IS_MEDIA_SOURCE_TRACK (self), FALSE);
|
||||
g_return_val_if_fail (GST_IS_SAMPLE (sample), FALSE);
|
||||
|
||||
if (gst_data_queue_is_full (self->samples)) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return gst_media_source_track_push (self, sample);
|
||||
}
|
||||
|
||||
gboolean
|
||||
gst_media_source_track_is_empty (GstMediaSourceTrack * self)
|
||||
{
|
||||
g_return_val_if_fail (GST_IS_MEDIA_SOURCE_TRACK (self), FALSE);
|
||||
return gst_data_queue_is_empty (self->samples);
|
||||
}
|
||||
|
||||
GstStreamType
|
||||
gst_media_source_track_type_to_stream_type (GstMediaSourceTrackType type)
|
||||
{
|
||||
switch (type) {
|
||||
case GST_MEDIA_SOURCE_TRACK_TYPE_AUDIO:
|
||||
return GST_STREAM_TYPE_AUDIO;
|
||||
case GST_MEDIA_SOURCE_TRACK_TYPE_TEXT:
|
||||
return GST_STREAM_TYPE_TEXT;
|
||||
case GST_MEDIA_SOURCE_TRACK_TYPE_VIDEO:
|
||||
return GST_STREAM_TYPE_VIDEO;
|
||||
default:
|
||||
return GST_STREAM_TYPE_UNKNOWN;
|
||||
}
|
||||
}
|
||||
|
||||
GstMediaSourceTrackType
|
||||
gst_media_source_track_type_from_stream_type (GstStreamType type)
|
||||
{
|
||||
switch (type) {
|
||||
case GST_STREAM_TYPE_AUDIO:
|
||||
return GST_MEDIA_SOURCE_TRACK_TYPE_AUDIO;
|
||||
case GST_STREAM_TYPE_TEXT:
|
||||
return GST_MEDIA_SOURCE_TRACK_TYPE_TEXT;
|
||||
case GST_STREAM_TYPE_VIDEO:
|
||||
return GST_MEDIA_SOURCE_TRACK_TYPE_VIDEO;
|
||||
default:
|
||||
return GST_MEDIA_SOURCE_TRACK_TYPE_OTHER;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,100 @@
|
|||
/* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <gst/gst.h>
|
||||
#include <glib-object.h>
|
||||
#include <gst/mse/mse-prelude.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GST_TYPE_MEDIA_SOURCE_TRACK_BUFFER \
|
||||
(gst_media_source_track_buffer_get_type())
|
||||
|
||||
GST_MSE_PRIVATE
|
||||
G_DECLARE_FINAL_TYPE (GstMediaSourceTrackBuffer, gst_media_source_track_buffer,
|
||||
GST, MEDIA_SOURCE_TRACK_BUFFER, GstObject);
|
||||
|
||||
GST_MSE_PRIVATE
|
||||
GstMediaSourceTrackBuffer *gst_media_source_track_buffer_new (void);
|
||||
|
||||
GST_MSE_PRIVATE
|
||||
gboolean gst_media_source_track_buffer_is_empty (
|
||||
GstMediaSourceTrackBuffer * self);
|
||||
|
||||
GST_MSE_PRIVATE
|
||||
gint gst_media_source_track_buffer_get_size (GstMediaSourceTrackBuffer * self);
|
||||
|
||||
GST_MSE_PRIVATE
|
||||
GstClockTime gst_media_source_track_buffer_get_highest_end_time (
|
||||
GstMediaSourceTrackBuffer * self);
|
||||
|
||||
GST_MSE_PRIVATE
|
||||
GArray * gst_media_source_track_buffer_get_ranges (
|
||||
GstMediaSourceTrackBuffer * self);
|
||||
|
||||
GST_MSE_PRIVATE
|
||||
void gst_media_source_track_buffer_set_group_start (GstMediaSourceTrackBuffer
|
||||
* self, GstClockTime group_start);
|
||||
|
||||
GST_MSE_PRIVATE
|
||||
void gst_media_source_track_buffer_process_init_segment (
|
||||
GstMediaSourceTrackBuffer * self, gboolean sequence_mode);
|
||||
|
||||
GST_MSE_PRIVATE
|
||||
void gst_media_source_track_buffer_add (GstMediaSourceTrackBuffer * self,
|
||||
GstSample * sample);
|
||||
|
||||
GST_MSE_PRIVATE
|
||||
void gst_media_source_track_buffer_remove (GstMediaSourceTrackBuffer * self,
|
||||
GstSample * sample);
|
||||
|
||||
GST_MSE_PRIVATE
|
||||
gsize
|
||||
gst_media_source_track_buffer_remove_range (GstMediaSourceTrackBuffer * self,
|
||||
GstClockTime start, GstClockTime end);
|
||||
|
||||
GST_MSE_PRIVATE
|
||||
void gst_media_source_track_buffer_clear (GstMediaSourceTrackBuffer * self);
|
||||
|
||||
GST_MSE_PRIVATE
|
||||
void gst_media_source_track_buffer_eos (GstMediaSourceTrackBuffer * self);
|
||||
|
||||
GST_MSE_PRIVATE
|
||||
gboolean gst_media_source_track_buffer_is_eos (
|
||||
GstMediaSourceTrackBuffer * self);
|
||||
|
||||
GST_MSE_PRIVATE
|
||||
gboolean gst_media_source_track_buffer_await_eos_until (
|
||||
GstMediaSourceTrackBuffer * self, gint64 deadline);
|
||||
|
||||
GST_MSE_PRIVATE
|
||||
gsize gst_media_source_track_buffer_get_storage_size (
|
||||
GstMediaSourceTrackBuffer * self);
|
||||
|
||||
GST_MSE_PRIVATE
|
||||
GstIterator * gst_media_source_track_buffer_iter_samples (
|
||||
GstMediaSourceTrackBuffer * buffer, GstClockTime start_dts,
|
||||
GstSample * start_sample);
|
||||
|
||||
G_END_DECLS
|
|
@ -0,0 +1,377 @@
|
|||
/* 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* */
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
/* GStreamer
|
||||
*
|
||||
* SPDX-License-Identifier: LGPL-2.1
|
||||
*
|
||||
* Copyright (C) 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <gst/gst.h>
|
||||
#include <gst/base/gstdataqueue.h>
|
||||
#include <gst/mse/mse-prelude.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
typedef void (*GstMseEventQueueCallback) (GstDataQueueItem *, gpointer);
|
||||
|
||||
#define GST_TYPE_MSE_EVENT_QUEUE (gst_mse_event_queue_get_type())
|
||||
|
||||
G_DECLARE_FINAL_TYPE (GstMseEventQueue, gst_mse_event_queue, GST,
|
||||
MSE_EVENT_QUEUE, GstObject);
|
||||
|
||||
GST_MSE_PRIVATE
|
||||
GstMseEventQueue * gst_mse_event_queue_new (GstMseEventQueueCallback callback,
|
||||
gpointer user_data);
|
||||
|
||||
GST_MSE_PRIVATE
|
||||
gboolean gst_mse_event_queue_push (GstMseEventQueue * self,
|
||||
GstDataQueueItem * item);
|
||||
|
||||
G_END_DECLS
|
133
subprojects/gst-plugins-bad/gst-libs/gst/mse/gstmseeventqueue.c
Normal file
133
subprojects/gst-plugins-bad/gst-libs/gst/mse/gstmseeventqueue.c
Normal file
|
@ -0,0 +1,133 @@
|
|||
/* GStreamer
|
||||
*
|
||||
* SPDX-License-Identifier: LGPL-2.1
|
||||
*
|
||||
* Copyright (C) 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 <gst/base/gstdataqueue.h>
|
||||
#include <gst/mse/mse-prelude.h>
|
||||
|
||||
#include "gstmseeventqueue-private.h"
|
||||
|
||||
struct _GstMseEventQueue
|
||||
{
|
||||
GstObject parent_instance;
|
||||
|
||||
GstMseEventQueueCallback callback;
|
||||
gpointer user_data;
|
||||
|
||||
GstTask *task;
|
||||
GRecMutex lock;
|
||||
GstDataQueue *queue;
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE (GstMseEventQueue, gst_mse_event_queue, GST_TYPE_OBJECT);
|
||||
|
||||
GstMseEventQueue *
|
||||
gst_mse_event_queue_new (GstMseEventQueueCallback callback, gpointer user_data)
|
||||
{
|
||||
g_return_val_if_fail (callback != NULL, NULL);
|
||||
|
||||
GstMseEventQueue *self = g_object_new (GST_TYPE_MSE_EVENT_QUEUE, NULL);
|
||||
|
||||
self->callback = callback;
|
||||
self->user_data = user_data;
|
||||
|
||||
gst_task_start (self->task);
|
||||
|
||||
return g_object_ref_sink (self);
|
||||
}
|
||||
|
||||
static void
|
||||
gst_mse_background_event_queue_dispose (GObject * obj)
|
||||
{
|
||||
GstMseEventQueue *self = GST_MSE_EVENT_QUEUE (obj);
|
||||
|
||||
gst_data_queue_set_flushing (self->queue, TRUE);
|
||||
gst_data_queue_flush (self->queue);
|
||||
|
||||
G_OBJECT_CLASS (gst_mse_event_queue_parent_class)->dispose (obj);
|
||||
}
|
||||
|
||||
static void
|
||||
gst_mse_background_event_queue_finalize (GObject * obj)
|
||||
{
|
||||
GstMseEventQueue *self = GST_MSE_EVENT_QUEUE (obj);
|
||||
|
||||
gst_task_join (self->task);
|
||||
g_rec_mutex_clear (&self->lock);
|
||||
gst_clear_object (&self->task);
|
||||
gst_clear_object (&self->queue);
|
||||
|
||||
G_OBJECT_CLASS (gst_mse_event_queue_parent_class)->finalize (obj);
|
||||
}
|
||||
|
||||
void
|
||||
gst_mse_event_queue_class_init (GstMseEventQueueClass * klass)
|
||||
{
|
||||
GObjectClass *oclass = G_OBJECT_CLASS (klass);
|
||||
|
||||
oclass->dispose = GST_DEBUG_FUNCPTR (gst_mse_background_event_queue_dispose);
|
||||
oclass->finalize =
|
||||
GST_DEBUG_FUNCPTR (gst_mse_background_event_queue_finalize);
|
||||
}
|
||||
|
||||
static void
|
||||
task_func (GstMseEventQueue * self)
|
||||
{
|
||||
GstTask *task = self->task;
|
||||
GstDataQueue *queue = self->queue;
|
||||
GstDataQueueItem *item = NULL;
|
||||
if (!gst_data_queue_pop (queue, &item)) {
|
||||
gst_task_stop (task);
|
||||
return;
|
||||
}
|
||||
self->callback (item, self->user_data);
|
||||
if (item->destroy) {
|
||||
item->destroy (item);
|
||||
}
|
||||
}
|
||||
|
||||
static gboolean
|
||||
never_full (GstDataQueue * queue, guint visible, guint bytes, guint64 time,
|
||||
gpointer user_data)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
void
|
||||
gst_mse_event_queue_init (GstMseEventQueue * self)
|
||||
{
|
||||
self->queue = gst_data_queue_new (never_full, NULL, NULL, NULL);
|
||||
self->task = gst_task_new ((GstTaskFunction) task_func, self, NULL);
|
||||
g_rec_mutex_init (&self->lock);
|
||||
gst_task_set_lock (self->task, &self->lock);
|
||||
}
|
||||
|
||||
gboolean
|
||||
gst_mse_event_queue_push (GstMseEventQueue * self, GstDataQueueItem * item)
|
||||
{
|
||||
g_return_val_if_fail (GST_IS_MSE_EVENT_QUEUE (self), FALSE);
|
||||
g_return_val_if_fail (item != NULL, FALSE);
|
||||
return gst_data_queue_push (self->queue, item);
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
/* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <gst/gst.h>
|
||||
|
||||
#define GST_CAT_DEFAULT gst_mse_debug
|
||||
|
||||
G_GNUC_INTERNAL
|
||||
GST_DEBUG_CATEGORY_EXTERN (gst_mse_debug);
|
||||
|
||||
G_GNUC_INTERNAL
|
||||
void gst_mse_init_logging (void);
|
35
subprojects/gst-plugins-bad/gst-libs/gst/mse/gstmselogging.c
Normal file
35
subprojects/gst-plugins-bad/gst-libs/gst/mse/gstmselogging.c
Normal file
|
@ -0,0 +1,35 @@
|
|||
/* 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 "gstmselogging-private.h"
|
||||
|
||||
GST_DEBUG_CATEGORY (gst_mse_debug);
|
||||
|
||||
void
|
||||
gst_mse_init_logging (void)
|
||||
{
|
||||
GST_DEBUG_CATEGORY_INIT (gst_mse_debug, "gst-mse", 0, "GstMse");
|
||||
}
|
|
@ -0,0 +1,60 @@
|
|||
/* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <glib.h>
|
||||
#include <gst/gst.h>
|
||||
#include <gst/mse/mse-prelude.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
typedef struct
|
||||
{
|
||||
gchar *mime_type;
|
||||
GStrv codecs;
|
||||
} GstMediaSourceMediaType;
|
||||
|
||||
#define GST_MEDIA_SOURCE_MEDIA_TYPE_INIT { 0 }
|
||||
|
||||
GST_MSE_PRIVATE
|
||||
gboolean gst_media_source_media_type_parse (GstMediaSourceMediaType * self,
|
||||
const gchar * type);
|
||||
|
||||
GST_MSE_PRIVATE
|
||||
gboolean gst_media_source_media_type_is_caps_supported (const GstCaps * caps);
|
||||
|
||||
GST_MSE_PRIVATE
|
||||
gboolean gst_media_source_media_type_is_supported (
|
||||
GstMediaSourceMediaType * self);
|
||||
|
||||
GST_MSE_PRIVATE
|
||||
gboolean gst_media_source_media_type_generates_timestamp (
|
||||
GstMediaSourceMediaType * self);
|
||||
|
||||
GST_MSE_PRIVATE
|
||||
void gst_media_source_media_type_reset (GstMediaSourceMediaType * self);
|
||||
|
||||
GST_MSE_PRIVATE
|
||||
void gst_media_source_media_type_free (GstMediaSourceMediaType * self);
|
||||
|
||||
G_END_DECLS
|
544
subprojects/gst-plugins-bad/gst-libs/gst/mse/gstmsemediatype.c
Normal file
544
subprojects/gst-plugins-bad/gst-libs/gst/mse/gstmsemediatype.c
Normal file
|
@ -0,0 +1,544 @@
|
|||
/* GStreamer
|
||||
*
|
||||
* SPDX-License-Identifier: LGPL-2.1
|
||||
*
|
||||
* Copyright (C) 2019 Igalia S.L.
|
||||
* 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 <glib.h>
|
||||
#include <gst/gst.h>
|
||||
|
||||
#include "gstmsemediatype-private.h"
|
||||
|
||||
static const gchar tspecials[] = {
|
||||
'(', ')', '<', '>', '@', ',', ';', ':',
|
||||
'\\', '"', '/', '[', ']', '?', '=',
|
||||
0,
|
||||
};
|
||||
|
||||
static gboolean
|
||||
is_tspecial (const gchar c)
|
||||
{
|
||||
for (const gchar * tspecial = tspecials; *tspecial != 0; tspecial++) {
|
||||
if (*tspecial == c) {
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
is_token_character (const gchar c)
|
||||
{
|
||||
if (g_ascii_iscntrl (c) || is_tspecial (c)) {
|
||||
return FALSE;
|
||||
}
|
||||
return g_ascii_isgraph (c);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
is_ascii (const gchar c)
|
||||
{
|
||||
return c > 0 && c <= G_MAXINT8;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
is_eos (const gchar c)
|
||||
{
|
||||
return c == '\0';
|
||||
}
|
||||
|
||||
static void
|
||||
ignore_whitespace (const gchar ** input)
|
||||
{
|
||||
g_return_if_fail (input != NULL);
|
||||
|
||||
const gchar *output;
|
||||
for (output = *input; g_ascii_isspace (*output); output++) {
|
||||
}
|
||||
*input = output;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
any_of (const gchar ** input, const gchar ** choices, gchar ** value)
|
||||
{
|
||||
g_return_val_if_fail (input != NULL, FALSE);
|
||||
g_return_val_if_fail (choices != NULL, FALSE);
|
||||
g_return_val_if_fail (value != NULL, FALSE);
|
||||
|
||||
for (const gchar ** choice = choices; *choice != NULL; choice++) {
|
||||
gulong prefix_length = strlen (*choice);
|
||||
if (g_str_has_prefix (*input, *choice)) {
|
||||
*value = g_strndup (*input, prefix_length);
|
||||
*input += prefix_length;
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
literal (const gchar ** input, const gchar * value)
|
||||
{
|
||||
g_return_val_if_fail (input != NULL, FALSE);
|
||||
g_return_val_if_fail (value != NULL, FALSE);
|
||||
|
||||
if (!g_str_has_prefix (*input, value)) {
|
||||
return FALSE;
|
||||
}
|
||||
*input += strlen (value);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
token (const gchar ** input, gchar ** value)
|
||||
{
|
||||
g_return_val_if_fail (input != NULL, FALSE);
|
||||
g_return_val_if_fail (value != NULL, FALSE);
|
||||
|
||||
const gchar *unparsed = *input;
|
||||
for (; is_token_character (unparsed[0]); unparsed++) {
|
||||
}
|
||||
gsize length = unparsed - *input;
|
||||
if (length < 1) {
|
||||
return FALSE;
|
||||
}
|
||||
*value = g_strndup (*input, length);
|
||||
*input = unparsed;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
quoted_string_char (const gchar ** input, gchar * value)
|
||||
{
|
||||
g_return_val_if_fail (input != NULL, FALSE);
|
||||
g_return_val_if_fail (value != NULL, FALSE);
|
||||
|
||||
const gchar *unparsed = *input;
|
||||
char c = unparsed[0];
|
||||
if (!is_ascii (c)) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (c == '"' || c == '\\' || c == '\r') {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
*value = c;
|
||||
(*input)++;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
escaped_ascii_char (const gchar ** input, gchar * value)
|
||||
{
|
||||
g_return_val_if_fail (input != NULL, FALSE);
|
||||
g_return_val_if_fail (value != NULL, FALSE);
|
||||
|
||||
if (!literal (input, "\\")) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
gchar c = (*input)[0];
|
||||
if (!is_ascii (c)) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
*value = c;
|
||||
(*input)++;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
quoted_string (const gchar ** input, gchar ** value)
|
||||
{
|
||||
g_return_val_if_fail (input != NULL, FALSE);
|
||||
g_return_val_if_fail (value != NULL, FALSE);
|
||||
|
||||
const gchar *unparsed = *input;
|
||||
if (!literal (&unparsed, "\"")) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
GString *string_value = g_string_new (NULL);
|
||||
for (;;) {
|
||||
if (literal (&unparsed, "\"")) {
|
||||
*input = unparsed;
|
||||
*value = g_string_free (string_value, FALSE);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
if (is_eos (unparsed[0])) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
gchar c;
|
||||
|
||||
if (quoted_string_char (&unparsed, &c)) {
|
||||
g_string_append_c (string_value, c);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (escaped_ascii_char (&unparsed, &c)) {
|
||||
g_string_append_c (string_value, c);
|
||||
continue;
|
||||
}
|
||||
|
||||
goto error;
|
||||
}
|
||||
error:
|
||||
g_string_free (string_value, TRUE);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static const gchar *discrete_media_types[] = {
|
||||
"text",
|
||||
"image",
|
||||
"audio",
|
||||
"video",
|
||||
"application",
|
||||
NULL,
|
||||
};
|
||||
|
||||
static gboolean
|
||||
discrete_media_type (const gchar ** input, gchar ** value)
|
||||
{
|
||||
return any_of (input, discrete_media_types, value);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
composite_media_type (const gchar ** input, gchar ** value)
|
||||
{
|
||||
return token (input, value);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
media_type_parameter (const gchar ** input, gchar ** param_name,
|
||||
gchar ** param_value)
|
||||
{
|
||||
g_return_val_if_fail (input != NULL, FALSE);
|
||||
g_return_val_if_fail (param_name != NULL, FALSE);
|
||||
g_return_val_if_fail (param_value != NULL, FALSE);
|
||||
|
||||
ignore_whitespace (input);
|
||||
if (!token (input, param_name)) {
|
||||
return FALSE;
|
||||
}
|
||||
if (!literal (input, "=")) {
|
||||
return FALSE;
|
||||
}
|
||||
if (!token (input, param_value)) {
|
||||
if (!quoted_string (input, param_value)) {
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
ignore_whitespace (input);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
media_type (const gchar ** input, gchar ** value)
|
||||
{
|
||||
g_return_val_if_fail (input != NULL, FALSE);
|
||||
g_return_val_if_fail (value != NULL, FALSE);
|
||||
|
||||
gchar *discrete = NULL;
|
||||
gchar *composite = NULL;
|
||||
|
||||
if (!discrete_media_type (input, &discrete)) {
|
||||
goto error;
|
||||
}
|
||||
if (!literal (input, "/")) {
|
||||
goto error;
|
||||
}
|
||||
if (!composite_media_type (input, &composite)) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
*value = g_strdup_printf ("%s/%s", discrete, composite);
|
||||
g_clear_pointer (&discrete, g_free);
|
||||
g_clear_pointer (&composite, g_free);
|
||||
return TRUE;
|
||||
|
||||
error:
|
||||
g_clear_pointer (&discrete, g_free);
|
||||
g_clear_pointer (&composite, g_free);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
media_type_codecs (const gchar ** input, gchar *** codecs)
|
||||
{
|
||||
g_return_val_if_fail (input != NULL, FALSE);
|
||||
g_return_val_if_fail (codecs != NULL, FALSE);
|
||||
|
||||
gchar *param_name = NULL;
|
||||
gchar *codecs_value = NULL;
|
||||
while (!is_eos (*input[0])
|
||||
&& media_type_parameter (input, ¶m_name, &codecs_value)) {
|
||||
gboolean is_codecs = g_strcmp0 (param_name, "codecs") == 0;
|
||||
g_clear_pointer (¶m_name, g_free);
|
||||
if (is_codecs) {
|
||||
break;
|
||||
}
|
||||
g_clear_pointer (&codecs_value, g_free);
|
||||
}
|
||||
if (codecs_value == NULL) {
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
gchar **codec_names = g_strsplit (codecs_value, ",", 0);
|
||||
guint n_codecs = g_strv_length (codec_names);
|
||||
GPtrArray *codecs_array = g_ptr_array_new_full (n_codecs, NULL);
|
||||
for (guint i = 0; i < n_codecs; i++) {
|
||||
gchar *codec = g_strstrip (g_strdup (codec_names[i]));
|
||||
if (g_strcmp0 (codec, "") != 0) {
|
||||
g_ptr_array_add (codecs_array, g_strdup (codec));
|
||||
}
|
||||
g_clear_pointer (&codec, g_free);
|
||||
}
|
||||
g_ptr_array_add (codecs_array, NULL);
|
||||
*codecs = (gchar **) g_ptr_array_free (codecs_array, FALSE);
|
||||
|
||||
g_clear_pointer (&codecs_value, g_free);
|
||||
g_strfreev (codec_names);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
gboolean
|
||||
gst_media_source_media_type_parse (GstMediaSourceMediaType * self,
|
||||
const gchar * type)
|
||||
{
|
||||
g_return_val_if_fail (self != NULL, FALSE);
|
||||
g_return_val_if_fail (type != NULL, FALSE);
|
||||
|
||||
gchar *lowercase = g_ascii_strdown (type, -1);
|
||||
const gchar *input = lowercase;
|
||||
|
||||
ignore_whitespace (&input);
|
||||
if (!media_type (&input, &self->mime_type)) {
|
||||
g_free (lowercase);
|
||||
return FALSE;
|
||||
}
|
||||
self->codecs = NULL;
|
||||
ignore_whitespace (&input);
|
||||
if (is_eos (input[0])) {
|
||||
g_free (lowercase);
|
||||
return TRUE;
|
||||
}
|
||||
if (!literal (&input, ";")) {
|
||||
g_free (lowercase);
|
||||
return TRUE;
|
||||
}
|
||||
ignore_whitespace (&input);
|
||||
media_type_codecs (&input, &self->codecs);
|
||||
g_free (lowercase);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static inline gboolean
|
||||
has_any_prefix (const gchar * str, const gchar * prefix, ...)
|
||||
{
|
||||
if (g_str_has_prefix (str, prefix)) {
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
va_list varargs;
|
||||
|
||||
va_start (varargs, prefix);
|
||||
for (const gchar * arg = va_arg (varargs, const gchar *); arg != NULL;
|
||||
arg = va_arg (varargs, const gchar *))
|
||||
{
|
||||
if (g_str_has_prefix (str, arg)) {
|
||||
va_end (varargs);
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
va_end (varargs);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static const gchar *
|
||||
patch_media_type (const gchar * media_type)
|
||||
{
|
||||
if (g_strcmp0 (media_type, "video/mp4") == 0) {
|
||||
return "video/quicktime";
|
||||
}
|
||||
if (g_strcmp0 (media_type, "audio/mp4") == 0 ||
|
||||
g_strcmp0 (media_type, "audio/aac") == 0) {
|
||||
return "audio/x-m4a";
|
||||
}
|
||||
if (!has_any_prefix (media_type, "audio/", "video/", NULL)) {
|
||||
return NULL;
|
||||
}
|
||||
return media_type;
|
||||
}
|
||||
|
||||
static GstStaticCaps h264_caps = GST_STATIC_CAPS ("video/x-h264");
|
||||
static GstStaticCaps h265_caps = GST_STATIC_CAPS ("video/x-h265");
|
||||
static GstStaticCaps av1_caps = GST_STATIC_CAPS ("video/x-av1");
|
||||
static GstStaticCaps vp8_caps = GST_STATIC_CAPS ("video/x-vp8");
|
||||
static GstStaticCaps vp9_caps = GST_STATIC_CAPS ("video/x-vp9");
|
||||
|
||||
static GstStaticCaps vorbis_caps = GST_STATIC_CAPS ("audio/x-vorbis");
|
||||
static GstStaticCaps opus_caps = GST_STATIC_CAPS ("audio/x-opus");
|
||||
static GstStaticCaps flac_caps = GST_STATIC_CAPS ("audio/x-flac");
|
||||
static GstStaticCaps mp3_caps =
|
||||
GST_STATIC_CAPS ("audio/mpeg, mpegversion=(int)1, layer=(int)3");
|
||||
static GstStaticCaps aac_caps =
|
||||
GST_STATIC_CAPS ("audio/mpeg, mpegversion=(int)4");
|
||||
|
||||
static inline GstStaticCaps *
|
||||
mse_codec_id_to_static_caps (const gchar * codec_id)
|
||||
{
|
||||
if (has_any_prefix (codec_id, "avc", "x-h264", "mp4v", NULL)) {
|
||||
return &h264_caps;
|
||||
}
|
||||
if (has_any_prefix (codec_id, "hvc1", "hev1", "x-h265", NULL)) {
|
||||
return &h265_caps;
|
||||
}
|
||||
if (has_any_prefix (codec_id, "av01", "av1", "x-av1", NULL)) {
|
||||
return &av1_caps;
|
||||
}
|
||||
if (has_any_prefix (codec_id, "vp8", "x-vp8", NULL)) {
|
||||
return &vp8_caps;
|
||||
}
|
||||
if (has_any_prefix (codec_id, "vp9", "vp09", "x-vp9", NULL)) {
|
||||
return &vp9_caps;
|
||||
}
|
||||
|
||||
if (has_any_prefix (codec_id, "mpeg", "mp4a", NULL)) {
|
||||
return &aac_caps;
|
||||
}
|
||||
if (has_any_prefix (codec_id, "vorbis", "x-vorbis", NULL)) {
|
||||
return &vorbis_caps;
|
||||
}
|
||||
if (has_any_prefix (codec_id, "opus", "x-opus", NULL)) {
|
||||
return &opus_caps;
|
||||
}
|
||||
if (has_any_prefix (codec_id, "flac", "x-flac", NULL)) {
|
||||
return &flac_caps;
|
||||
}
|
||||
if (has_any_prefix (codec_id, "mp3", "audio/mp3", NULL)) {
|
||||
return &mp3_caps;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
is_supported (const GstCaps * caps, GList * elements)
|
||||
{
|
||||
GList *supporting_elements = gst_element_factory_list_filter (elements, caps,
|
||||
GST_PAD_SINK, FALSE);
|
||||
gboolean supported = supporting_elements != NULL;
|
||||
gst_plugin_feature_list_free (supporting_elements);
|
||||
return supported;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
is_codec_supported (const gchar * codec_id, GList * elements)
|
||||
{
|
||||
GstStaticCaps *static_caps = mse_codec_id_to_static_caps (codec_id);
|
||||
if (static_caps == NULL) {
|
||||
return FALSE;
|
||||
}
|
||||
GstCaps *codec_caps = gst_static_caps_get (static_caps);
|
||||
gboolean supported = is_supported (codec_caps, elements);
|
||||
gst_clear_caps (&codec_caps);
|
||||
return supported;
|
||||
}
|
||||
|
||||
static inline GList *
|
||||
get_srcpad_elements (void)
|
||||
{
|
||||
return gst_element_factory_list_get_elements (GST_ELEMENT_FACTORY_TYPE_DEMUXER
|
||||
| GST_ELEMENT_FACTORY_TYPE_DECODER | GST_ELEMENT_FACTORY_TYPE_DECRYPTOR |
|
||||
GST_ELEMENT_FACTORY_TYPE_DEPAYLOADER | GST_ELEMENT_FACTORY_TYPE_SINK |
|
||||
GST_ELEMENT_FACTORY_TYPE_PARSER, GST_RANK_MARGINAL);
|
||||
}
|
||||
|
||||
gboolean
|
||||
gst_media_source_media_type_is_caps_supported (const GstCaps * caps)
|
||||
{
|
||||
GList *elements = get_srcpad_elements ();
|
||||
gboolean supported = is_supported (caps, elements);
|
||||
gst_plugin_feature_list_free (elements);
|
||||
return supported;
|
||||
}
|
||||
|
||||
gboolean
|
||||
gst_media_source_media_type_is_supported (GstMediaSourceMediaType * self)
|
||||
{
|
||||
g_return_val_if_fail (self != NULL, FALSE);
|
||||
g_return_val_if_fail (self->mime_type != NULL, FALSE);
|
||||
|
||||
const gchar *mime_type = patch_media_type (self->mime_type);
|
||||
if (mime_type == NULL) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
GList *elements = get_srcpad_elements ();
|
||||
GstCaps *caps = gst_caps_from_string (mime_type);
|
||||
|
||||
gboolean supported = is_supported (caps, elements);
|
||||
for (gchar ** codec_id = self->codecs;
|
||||
supported && codec_id && *codec_id; codec_id++) {
|
||||
supported &= is_codec_supported (*codec_id, elements);
|
||||
}
|
||||
|
||||
gst_plugin_feature_list_free (elements);
|
||||
gst_clear_caps (&caps);
|
||||
|
||||
return supported;
|
||||
}
|
||||
|
||||
static const gchar *generate_timestamps_formats[] = {
|
||||
"audio/mpeg",
|
||||
"audio/aac",
|
||||
NULL,
|
||||
};
|
||||
|
||||
gboolean
|
||||
gst_media_source_media_type_generates_timestamp (GstMediaSourceMediaType * self)
|
||||
{
|
||||
g_return_val_if_fail (self != NULL, FALSE);
|
||||
g_return_val_if_fail (self->mime_type != NULL, FALSE);
|
||||
return g_strv_contains (generate_timestamps_formats, self->mime_type);
|
||||
}
|
||||
|
||||
void
|
||||
gst_media_source_media_type_reset (GstMediaSourceMediaType * self)
|
||||
{
|
||||
g_return_if_fail (self != NULL);
|
||||
g_clear_pointer (&self->mime_type, g_free);
|
||||
g_clear_pointer (&self->codecs, g_strfreev);
|
||||
}
|
||||
|
||||
void
|
||||
gst_media_source_media_type_free (GstMediaSourceMediaType * self)
|
||||
{
|
||||
gst_media_source_media_type_reset (self);
|
||||
g_free (self);
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
/* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <glib.h>
|
||||
#include <gst/gst.h>
|
||||
#include "gstmsesrc.h"
|
||||
#include "gstmediasource.h"
|
||||
#include "gstmediasourcetrack-private.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
GST_MSE_PRIVATE
|
||||
void gst_mse_src_set_duration (GstMseSrc * self, GstClockTime duration);
|
||||
|
||||
GST_MSE_PRIVATE
|
||||
void gst_mse_src_network_error (GstMseSrc * self);
|
||||
|
||||
GST_MSE_PRIVATE
|
||||
void gst_mse_src_decode_error (GstMseSrc * self);
|
||||
|
||||
GST_MSE_PRIVATE
|
||||
void gst_mse_src_emit_streams (GstMseSrc * self, GstMediaSourceTrack ** tracks,
|
||||
gsize n_tracks);
|
||||
|
||||
GST_MSE_PRIVATE
|
||||
void gst_mse_src_update_ready_state (GstMseSrc * self);
|
||||
|
||||
GST_MSE_PRIVATE
|
||||
void gst_mse_src_attach (GstMseSrc * self, GstMediaSource * media_source);
|
||||
|
||||
GST_MSE_PRIVATE
|
||||
void gst_mse_src_detach (GstMseSrc * self);
|
||||
|
||||
G_END_DECLS
|
1385
subprojects/gst-plugins-bad/gst-libs/gst/mse/gstmsesrc.c
Normal file
1385
subprojects/gst-plugins-bad/gst-libs/gst/mse/gstmsesrc.c
Normal file
File diff suppressed because it is too large
Load diff
94
subprojects/gst-plugins-bad/gst-libs/gst/mse/gstmsesrc.h
Normal file
94
subprojects/gst-plugins-bad/gst-libs/gst/mse/gstmsesrc.h
Normal file
|
@ -0,0 +1,94 @@
|
|||
/* GStreamer
|
||||
*
|
||||
* SPDX-License-Identifier: LGPL-2.1
|
||||
*
|
||||
* Copyright (C) 2009, 2010 Sebastian Dröge <sebastian.droege@collabora.co.uk>
|
||||
* Copyright (C) 2013, 2022, 2023 Collabora Ltd.
|
||||
* Copyright (C) 2013 Orange
|
||||
* Copyright (C) 2014, 2015 Sebastian Dröge <sebastian@centricular.com>
|
||||
* Copyright (C) 2015, 2016, 2018, 2019, 2020, 2021 Igalia, S.L
|
||||
* Copyright (C) 2015, 2016, 2018, 2019, 2020, 2021 Metrological Group B.V.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <gst/gst.h>
|
||||
#include <gst/mse/mse-prelude.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
/**
|
||||
* GstMseSrcReadyState:
|
||||
* @GST_MSE_SRC_READY_STATE_HAVE_NOTHING: No information is available about the
|
||||
* stream
|
||||
* @GST_MSE_SRC_READY_STATE_HAVE_METADATA: The duration is available and video
|
||||
* dimensions are available if the stream contains video
|
||||
* @GST_MSE_SRC_READY_STATE_HAVE_CURRENT_DATA: The current playback position can
|
||||
* be presented but future information is not available
|
||||
* @GST_MSE_SRC_READY_STATE_HAVE_FUTURE_DATA: There is data for the current
|
||||
* position and some amount in the future and any text tracks are ready.
|
||||
* @GST_MSE_SRC_READY_STATE_HAVE_ENOUGH_DATA: Either there is enough data to
|
||||
* play the stream through at the current playback and input rate or the input
|
||||
* buffer is full.
|
||||
*
|
||||
* Describes how much information a #GstMseSrc has about the media it is playing
|
||||
* back at the current playback #GstMseSrc:position. This type corresponds
|
||||
* directly to the ready state of a HTML Media Element and is a separate concept
|
||||
* from #GstMediaSourceReadyState.
|
||||
*
|
||||
* [Specification](https://html.spec.whatwg.org/multipage/media.html#ready-states)
|
||||
*
|
||||
* Since: 1.24
|
||||
*/
|
||||
typedef enum
|
||||
{
|
||||
GST_MSE_SRC_READY_STATE_HAVE_NOTHING,
|
||||
GST_MSE_SRC_READY_STATE_HAVE_METADATA,
|
||||
GST_MSE_SRC_READY_STATE_HAVE_CURRENT_DATA,
|
||||
GST_MSE_SRC_READY_STATE_HAVE_FUTURE_DATA,
|
||||
GST_MSE_SRC_READY_STATE_HAVE_ENOUGH_DATA,
|
||||
} GstMseSrcReadyState;
|
||||
|
||||
#define GST_TYPE_MSE_SRC (gst_mse_src_get_type())
|
||||
#define GST_TYPE_MSE_SRC_PAD (gst_mse_src_pad_get_type())
|
||||
|
||||
GST_MSE_API
|
||||
G_DECLARE_FINAL_TYPE (GstMseSrc, gst_mse_src, GST, MSE_SRC, GstElement);
|
||||
|
||||
GST_MSE_API
|
||||
G_DECLARE_FINAL_TYPE (GstMseSrcPad, gst_mse_src_pad, GST, MSE_SRC_PAD, GstPad);
|
||||
|
||||
GST_MSE_API
|
||||
GstClockTime gst_mse_src_get_position (GstMseSrc * self);
|
||||
|
||||
GST_MSE_API
|
||||
GstClockTime gst_mse_src_get_duration (GstMseSrc * self);
|
||||
|
||||
GST_MSE_API
|
||||
GstMseSrcReadyState gst_mse_src_get_ready_state (GstMseSrc * self);
|
||||
|
||||
GST_MSE_API
|
||||
guint gst_mse_src_get_n_audio (GstMseSrc * self);
|
||||
|
||||
GST_MSE_API
|
||||
guint gst_mse_src_get_n_text (GstMseSrc * self);
|
||||
|
||||
GST_MSE_API
|
||||
guint gst_mse_src_get_n_video (GstMseSrc * self);
|
||||
|
||||
G_END_DECLS
|
|
@ -0,0 +1,72 @@
|
|||
/* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <glib.h>
|
||||
#include "gstsourcebuffer.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
typedef struct
|
||||
{
|
||||
void (*duration_changed) (GstSourceBuffer * self, gpointer user_data);
|
||||
void (*received_init_segment) (GstSourceBuffer * self, gpointer user_data);
|
||||
void (*active_state_changed) (GstSourceBuffer * self, gpointer user_data);
|
||||
} GstSourceBufferCallbacks;
|
||||
|
||||
GST_MSE_PRIVATE
|
||||
GstSourceBuffer * gst_source_buffer_new (const gchar * content_type,
|
||||
GstObject * parent, GError ** error);
|
||||
|
||||
GST_MSE_PRIVATE
|
||||
GstSourceBuffer * gst_source_buffer_new_with_callbacks (
|
||||
const gchar * content_type, GstObject * parent,
|
||||
GstSourceBufferCallbacks *callbacks, gpointer user_data, GError ** error);
|
||||
|
||||
GST_MSE_PRIVATE
|
||||
void gst_source_buffer_teardown (GstSourceBuffer * self);
|
||||
|
||||
GST_MSE_PRIVATE
|
||||
gboolean gst_source_buffer_has_init_segment (GstSourceBuffer * self);
|
||||
|
||||
GST_MSE_PRIVATE
|
||||
gboolean gst_source_buffer_is_buffered (GstSourceBuffer * self, GstClockTime
|
||||
time);
|
||||
|
||||
GST_MSE_PRIVATE
|
||||
gboolean gst_source_buffer_is_range_buffered (GstSourceBuffer * self,
|
||||
GstClockTime start, GstClockTime end);
|
||||
|
||||
GST_MSE_PRIVATE
|
||||
GstClockTime gst_source_buffer_get_duration (GstSourceBuffer * self);
|
||||
|
||||
GST_MSE_PRIVATE
|
||||
GPtrArray * gst_source_buffer_get_all_tracks (GstSourceBuffer * self);
|
||||
|
||||
GST_MSE_PRIVATE
|
||||
void gst_source_buffer_seek (GstSourceBuffer * self, GstClockTime time);
|
||||
|
||||
GST_MSE_PRIVATE
|
||||
gboolean gst_source_buffer_get_active (GstSourceBuffer * self);
|
||||
|
||||
G_END_DECLS
|
2019
subprojects/gst-plugins-bad/gst-libs/gst/mse/gstsourcebuffer.c
Normal file
2019
subprojects/gst-plugins-bad/gst-libs/gst/mse/gstsourcebuffer.c
Normal file
File diff suppressed because it is too large
Load diff
127
subprojects/gst-plugins-bad/gst-libs/gst/mse/gstsourcebuffer.h
Normal file
127
subprojects/gst-plugins-bad/gst-libs/gst/mse/gstsourcebuffer.h
Normal file
|
@ -0,0 +1,127 @@
|
|||
/* GStreamer
|
||||
*
|
||||
* SPDX-License-Identifier: LGPL-2.1
|
||||
*
|
||||
* Copyright (C) 2013 Google Inc. All rights reserved.
|
||||
* Copyright (C) 2013 Orange
|
||||
* Copyright (C) 2013-2020 Apple Inc. All rights reserved.
|
||||
* Copyright (C) 2014 Sebastian Dröge <sebastian@centricular.com>
|
||||
* Copyright (C) 2015, 2016, 2017 Igalia, S.L
|
||||
* Copyright (C) 2015, 2016, 2017 Metrological Group B.V.
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <gst/gst.h>
|
||||
#include <glib-object.h>
|
||||
#include <gst/mse/mse-prelude.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
/**
|
||||
* GstSourceBufferAppendMode:
|
||||
* @GST_SOURCE_BUFFER_APPEND_MODE_SEGMENTS:
|
||||
* @GST_SOURCE_BUFFER_APPEND_MODE_SEQUENCE:
|
||||
*
|
||||
* [Specification](https://www.w3.org/TR/media-source-2/#dom-appendmode)
|
||||
*
|
||||
* Since: 1.24
|
||||
*/
|
||||
typedef enum
|
||||
{
|
||||
GST_SOURCE_BUFFER_APPEND_MODE_SEGMENTS,
|
||||
GST_SOURCE_BUFFER_APPEND_MODE_SEQUENCE,
|
||||
} GstSourceBufferAppendMode;
|
||||
|
||||
/**
|
||||
* GstSourceBufferInterval:
|
||||
*
|
||||
* Since: 1.24
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
GstClockTime start;
|
||||
GstClockTime end;
|
||||
} GstSourceBufferInterval;
|
||||
|
||||
#define GST_TYPE_SOURCE_BUFFER (gst_source_buffer_get_type())
|
||||
|
||||
GST_MSE_API
|
||||
G_DECLARE_FINAL_TYPE (GstSourceBuffer, gst_source_buffer, GST, SOURCE_BUFFER,
|
||||
GstObject);
|
||||
|
||||
GST_MSE_API
|
||||
GstSourceBufferAppendMode gst_source_buffer_get_append_mode (
|
||||
GstSourceBuffer * self);
|
||||
|
||||
GST_MSE_API
|
||||
gboolean gst_source_buffer_set_append_mode (GstSourceBuffer * self,
|
||||
GstSourceBufferAppendMode mode, GError ** error);
|
||||
|
||||
GST_MSE_API
|
||||
gchar *gst_source_buffer_get_content_type (GstSourceBuffer * self);
|
||||
|
||||
GST_MSE_API
|
||||
gboolean gst_source_buffer_get_updating (GstSourceBuffer * self);
|
||||
|
||||
GST_MSE_API
|
||||
GArray * gst_source_buffer_get_buffered (GstSourceBuffer * self,
|
||||
GError ** error);
|
||||
|
||||
GST_MSE_API
|
||||
gboolean gst_source_buffer_set_timestamp_offset (GstSourceBuffer * self,
|
||||
GstClockTime offset,
|
||||
GError ** error);
|
||||
|
||||
GST_MSE_API
|
||||
GstClockTime gst_source_buffer_get_timestamp_offset (GstSourceBuffer * self);
|
||||
|
||||
GST_MSE_API
|
||||
gboolean gst_source_buffer_set_append_window_start (GstSourceBuffer * self,
|
||||
GstClockTime start,
|
||||
GError ** error);
|
||||
|
||||
GST_MSE_API
|
||||
GstClockTime gst_source_buffer_get_append_window_start (GstSourceBuffer * self);
|
||||
|
||||
GST_MSE_API
|
||||
gboolean gst_source_buffer_set_append_window_end (GstSourceBuffer * self,
|
||||
GstClockTime end,
|
||||
GError ** error);
|
||||
|
||||
GST_MSE_API
|
||||
GstClockTime gst_source_buffer_get_append_window_end (GstSourceBuffer * self);
|
||||
|
||||
GST_MSE_API
|
||||
gboolean gst_source_buffer_append_buffer (GstSourceBuffer * self,
|
||||
GstBuffer * buf, GError ** error);
|
||||
|
||||
GST_MSE_API
|
||||
gboolean gst_source_buffer_abort (GstSourceBuffer * self, GError ** error);
|
||||
|
||||
GST_MSE_API
|
||||
gboolean gst_source_buffer_change_content_type (GstSourceBuffer * self,
|
||||
const gchar * type,
|
||||
GError ** error);
|
||||
|
||||
GST_MSE_API
|
||||
gboolean gst_source_buffer_remove (GstSourceBuffer * self, GstClockTime start,
|
||||
GstClockTime end, GError ** error);
|
||||
|
||||
G_END_DECLS
|
|
@ -0,0 +1,64 @@
|
|||
/* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <glib.h>
|
||||
#include "gstsourcebufferlist.h"
|
||||
#include "gstsourcebuffer.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
GST_MSE_PRIVATE
|
||||
GstSourceBufferList *gst_source_buffer_list_new (void);
|
||||
|
||||
GST_MSE_PRIVATE
|
||||
gboolean gst_source_buffer_list_contains (GstSourceBufferList * self,
|
||||
GstSourceBuffer * buf);
|
||||
|
||||
GST_MSE_PRIVATE
|
||||
void gst_source_buffer_list_append (GstSourceBufferList * self,
|
||||
GstSourceBuffer * buf);
|
||||
|
||||
GST_MSE_PRIVATE
|
||||
gboolean gst_source_buffer_list_remove (GstSourceBufferList * self,
|
||||
GstSourceBuffer * buf);
|
||||
|
||||
GST_MSE_PRIVATE
|
||||
void gst_source_buffer_list_remove_all (GstSourceBufferList * self);
|
||||
|
||||
GST_MSE_PRIVATE
|
||||
void gst_source_buffer_list_notify_freeze (GstSourceBufferList * self);
|
||||
|
||||
GST_MSE_PRIVATE
|
||||
void gst_source_buffer_list_notify_cancel (GstSourceBufferList * self);
|
||||
|
||||
GST_MSE_PRIVATE
|
||||
void gst_source_buffer_list_notify_added (GstSourceBufferList * self);
|
||||
|
||||
GST_MSE_PRIVATE
|
||||
void gst_source_buffer_list_notify_removed (GstSourceBufferList * self);
|
||||
|
||||
GST_MSE_PRIVATE
|
||||
void gst_source_buffer_list_notify_thaw (GstSourceBufferList * self);
|
||||
|
||||
G_END_DECLS
|
|
@ -0,0 +1,461 @@
|
|||
/* 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);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
/* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <gst/gst.h>
|
||||
#include <gst/mse/mse-prelude.h>
|
||||
|
||||
#include "gstsourcebuffer.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GST_TYPE_SOURCE_BUFFER_LIST (gst_source_buffer_list_get_type())
|
||||
|
||||
GST_MSE_API
|
||||
G_DECLARE_FINAL_TYPE (GstSourceBufferList, gst_source_buffer_list, GST,
|
||||
SOURCE_BUFFER_LIST, GstObject);
|
||||
|
||||
GST_MSE_API
|
||||
GstSourceBuffer *gst_source_buffer_list_index (GstSourceBufferList * self,
|
||||
guint index);
|
||||
GST_MSE_API
|
||||
guint gst_source_buffer_list_get_length (GstSourceBufferList * self);
|
||||
|
||||
G_END_DECLS
|
149
subprojects/gst-plugins-bad/gst-libs/gst/mse/meson.build
Normal file
149
subprojects/gst-plugins-bad/gst-libs/gst/mse/meson.build
Normal file
|
@ -0,0 +1,149 @@
|
|||
gstmse_headers_private = files(
|
||||
'gstappendpipeline-private.h',
|
||||
'gstmediasourcesamplemap-private.h',
|
||||
'gstmediasourcetrackbuffer-private.h',
|
||||
'gstmediasourcetrack-private.h',
|
||||
'gstmseeventqueue-private.h',
|
||||
'gstmselogging-private.h',
|
||||
'gstmsemediatype-private.h',
|
||||
)
|
||||
|
||||
gstmse_sources_private = files(
|
||||
'gstappendpipeline.c',
|
||||
'gstmediasourcesamplemap.c',
|
||||
'gstmediasourcetrackbuffer.c',
|
||||
'gstmediasourcetrack.c',
|
||||
'gstmseeventqueue.c',
|
||||
'gstmselogging.c',
|
||||
'gstmsemediatype.c',
|
||||
)
|
||||
|
||||
gstmse_headers_public = files(
|
||||
'gstmediasource.h',
|
||||
'gstsourcebuffer.h',
|
||||
'gstsourcebufferlist.h',
|
||||
'gstmsesrc.h',
|
||||
'mse.h',
|
||||
'mse-prelude.h',
|
||||
)
|
||||
|
||||
gstmse_sources_public = files(
|
||||
'gstmediasource.c',
|
||||
'gstmsesrc.c',
|
||||
'gstsourcebuffer.c',
|
||||
'gstsourcebufferlist.c',
|
||||
)
|
||||
|
||||
gstmse_header_dir = 'gstreamer-' + api_version + '/gst/mse/'
|
||||
|
||||
gstmse_enums_private = gnome.mkenums_simple('mse-enumtypes-private',
|
||||
sources : gstmse_headers_private,
|
||||
body_prefix : '#ifdef HAVE_CONFIG_H\n#include "config.h"\n#endif',
|
||||
header_prefix : '#include <gst/mse/mse-prelude.h>',
|
||||
decorator: 'GST_MSE_PRIVATE',
|
||||
install_header: false,
|
||||
)
|
||||
|
||||
gstmse_enums = gnome.mkenums_simple('mse-enumtypes',
|
||||
sources : gstmse_headers_public,
|
||||
body_prefix : '#ifdef HAVE_CONFIG_H\n#include "config.h"\n#endif',
|
||||
header_prefix : '#include <gst/mse/mse-prelude.h>',
|
||||
decorator: 'GST_MSE_API',
|
||||
install_header: true,
|
||||
install_dir : join_paths(get_option('includedir'), gstmse_header_dir),
|
||||
)
|
||||
|
||||
gstmse_deps = [gstbase_dep, gstapp_dep]
|
||||
|
||||
gstmse_enums_private_c = gstmse_enums_private[0]
|
||||
gstmse_enums_private_h = gstmse_enums_private[1]
|
||||
|
||||
gstmse_enums_c = gstmse_enums[0]
|
||||
gstmse_enums_h = gstmse_enums[1]
|
||||
|
||||
gstmse_c_args = gst_plugins_bad_args + [
|
||||
'-DBUILDING_GST_MSE',
|
||||
'-DGST_USE_UNSTABLE_API',
|
||||
'-DG_LOG_DOMAIN="GStreamer-MSE"',
|
||||
]
|
||||
|
||||
gstmse_sources_all = [
|
||||
gstmse_sources_private,
|
||||
gstmse_sources_public,
|
||||
gstmse_enums_c,
|
||||
gstmse_enums_h,
|
||||
gstmse_enums_private_c,
|
||||
gstmse_enums_private_h,
|
||||
]
|
||||
|
||||
gstmse_private_test = library('gstmse-private-test',
|
||||
gstmse_sources_all,
|
||||
c_args : [gstmse_c_args, '-DBUILDING_GST_MSE_TEST'],
|
||||
include_directories : [configinc, libsinc],
|
||||
dependencies : gstmse_deps,
|
||||
)
|
||||
|
||||
gstmse_private_test_dep = declare_dependency(
|
||||
compile_args : [gstmse_c_args, '-DBUILDING_GST_MSE_TEST'],
|
||||
link_with : [gstmse_private_test],
|
||||
include_directories : [libsinc],
|
||||
)
|
||||
|
||||
gstmse = library('gstmse-' + api_version,
|
||||
gstmse_sources_all,
|
||||
c_args : gstmse_c_args,
|
||||
include_directories : [configinc, libsinc],
|
||||
version : libversion,
|
||||
soversion : soversion,
|
||||
darwin_versions : osxversion,
|
||||
install : true,
|
||||
dependencies : gstmse_deps,
|
||||
)
|
||||
|
||||
pkg_name = 'gstreamer-mse-' + api_version
|
||||
gst_libraries += [[pkg_name, {'lib': gstmse}]]
|
||||
pkgconfig.generate(gstmse,
|
||||
libraries : [gst_dep],
|
||||
variables : pkgconfig_variables,
|
||||
subdirs : pkgconfig_subdirs,
|
||||
name : pkg_name,
|
||||
description : 'GStreamer Support for W3C Media Source Extensions',
|
||||
)
|
||||
|
||||
library_def = {'lib': gstmse}
|
||||
gen_sources = []
|
||||
if build_gir
|
||||
gir = {
|
||||
'sources' : [
|
||||
gstmse_sources_public,
|
||||
gstmse_headers_public,
|
||||
gstmse_enums_h,
|
||||
gstmse_enums_c,
|
||||
],
|
||||
'namespace' : 'GstMse',
|
||||
'nsversion' : api_version,
|
||||
'identifier_prefix' : 'Gst',
|
||||
'symbol_prefix' : 'gst',
|
||||
'export_packages' : pkg_name,
|
||||
'includes' : ['Gst-1.0'],
|
||||
'install' : true,
|
||||
'extra_args' : gir_init_section + ['-DGST_USE_UNSTABLE_API'] + ['--c-include=gst/mse/mse.h'],
|
||||
'dependencies' : gstmse_deps,
|
||||
}
|
||||
library_def += {'gir': [gir]}
|
||||
if not static_build
|
||||
mse_gir = gnome.generate_gir(gstmse, kwargs: gir)
|
||||
library_def += {'gir_targets': library_def.get('gir_targets', []) + [mse_gir]}
|
||||
gen_sources += mse_gir
|
||||
endif
|
||||
endif
|
||||
gst_libraries += [[pkg_name, library_def]]
|
||||
|
||||
gstmse_dep = declare_dependency(
|
||||
link_with : [gstmse],
|
||||
include_directories : [libsinc],
|
||||
dependencies : gstmse_deps,
|
||||
sources: [gstmse_enums_h],
|
||||
)
|
||||
install_headers(gstmse_headers_public, subdir: gstmse_header_dir)
|
||||
meson.override_dependency(pkg_name, gstmse_dep)
|
48
subprojects/gst-plugins-bad/gst-libs/gst/mse/mse-prelude.h
Normal file
48
subprojects/gst-plugins-bad/gst-libs/gst/mse/mse-prelude.h
Normal file
|
@ -0,0 +1,48 @@
|
|||
/* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <gst/gst.h>
|
||||
|
||||
#ifndef GST_MSE_API
|
||||
# if defined(BUILDING_GST_MSE) && defined(GST_API_EXPORT)
|
||||
# define GST_MSE_API GST_API_EXPORT
|
||||
# elif defined(GST_API_IMPORT)
|
||||
# define GST_MSE_API GST_API_IMPORT
|
||||
# else
|
||||
# define GST_MSE_API
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#ifndef GST_MSE_PRIVATE
|
||||
# if defined(BUILDING_GST_MSE_TEST)
|
||||
# define GST_MSE_PRIVATE GST_MSE_API
|
||||
# else
|
||||
# define GST_MSE_PRIVATE G_GNUC_INTERNAL
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#ifndef GST_USE_UNSTABLE_API
|
||||
#warning "The MSE library from gst-plugins-bad is an unstable API and may change in future."
|
||||
#warning "You can define GST_USE_UNSTABLE_API to avoid this warning."
|
||||
#endif
|
32
subprojects/gst-plugins-bad/gst-libs/gst/mse/mse.h
Normal file
32
subprojects/gst-plugins-bad/gst-libs/gst/mse/mse.h
Normal file
|
@ -0,0 +1,32 @@
|
|||
/* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <gst/gst.h>
|
||||
|
||||
#include "mse-prelude.h"
|
||||
#include <gst/mse/mse-enumtypes.h>
|
||||
#include "gstmsesrc.h"
|
||||
#include "gstmediasource.h"
|
||||
#include "gstsourcebufferlist.h"
|
||||
#include "gstsourcebuffer.h"
|
|
@ -8,7 +8,7 @@ foreach plugin : ['accurip', 'adpcmdec', 'adpcmenc', 'aiff', 'asfmux',
|
|||
'geometrictransform', 'id3tag', 'insertbin', 'inter', 'interlace',
|
||||
'ivfparse', 'ivtc', 'jp2kdecimator', 'jpegformat', 'librfb',
|
||||
'midi', 'mpegdemux', 'mpegpsmux', 'mpegtsdemux', 'mpegtsmux',
|
||||
'mxf', 'netsim', 'onvif', 'pcapparse', 'pnm', 'proxy',
|
||||
'mse', 'mxf', 'netsim', 'onvif', 'pcapparse', 'pnm', 'proxy',
|
||||
'rawparse', 'removesilence', 'rist', 'rtmp2', 'rtp', 'sdp',
|
||||
'segmentclip', 'siren', 'smooth', 'speed', 'subenc', 'switchbin',
|
||||
'timecode', 'transcode', 'unixfd', 'videofilters',
|
||||
|
|
49
subprojects/gst-plugins-bad/gst/mse/gstmse.c
Normal file
49
subprojects/gst-plugins-bad/gst/mse/gstmse.c
Normal file
|
@ -0,0 +1,49 @@
|
|||
/* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* plugin-mse:
|
||||
*
|
||||
* Since: 1.24
|
||||
*/
|
||||
|
||||
#include "gstmse.h"
|
||||
|
||||
/**
|
||||
* SECTION:element-msesrc
|
||||
* @title: msesrc
|
||||
*
|
||||
* Since: 1.24
|
||||
*/
|
||||
GST_ELEMENT_REGISTER_DEFINE (msesrc, "msesrc", GST_RANK_NONE, GST_TYPE_MSE_SRC);
|
||||
|
||||
static gboolean
|
||||
plugin_init (GstPlugin * plugin)
|
||||
{
|
||||
return GST_ELEMENT_REGISTER (msesrc, plugin);
|
||||
}
|
||||
|
||||
GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
|
||||
GST_VERSION_MINOR,
|
||||
mse,
|
||||
"W3C Media Source Extensions Support",
|
||||
plugin_init, VERSION, GST_LICENSE, GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)
|
33
subprojects/gst-plugins-bad/gst/mse/gstmse.h
Normal file
33
subprojects/gst-plugins-bad/gst/mse/gstmse.h
Normal file
|
@ -0,0 +1,33 @@
|
|||
/* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include <gst/gst.h>
|
||||
#include <gst/mse/mse.h>
|
||||
#include <gst/mse/gstmsesrc.h>
|
||||
|
||||
GST_ELEMENT_REGISTER_DECLARE (msesrc);
|
12
subprojects/gst-plugins-bad/gst/mse/meson.build
Normal file
12
subprojects/gst-plugins-bad/gst/mse/meson.build
Normal file
|
@ -0,0 +1,12 @@
|
|||
mse_sources = ['gstmse.c']
|
||||
|
||||
gstmse_plugin = library('gstmse',
|
||||
mse_sources,
|
||||
c_args: gst_plugins_bad_args + ['-DGST_USE_UNSTABLE_API'],
|
||||
include_directories: [configinc],
|
||||
dependencies : [gstbase_dep, gstmse_dep],
|
||||
install : true,
|
||||
install_dir : plugins_install_dir,
|
||||
)
|
||||
|
||||
plugins += [gstmse_plugin]
|
|
@ -45,6 +45,7 @@ option('mpegdemux', type : 'feature', value : 'auto')
|
|||
option('mpegpsmux', type : 'feature', value : 'auto')
|
||||
option('mpegtsdemux', type : 'feature', value : 'auto')
|
||||
option('mpegtsmux', type : 'feature', value : 'auto')
|
||||
option('mse', type : 'feature', value : 'auto')
|
||||
option('mxf', type : 'feature', value : 'auto')
|
||||
option('netsim', type : 'feature', value : 'auto')
|
||||
option('onvif', type : 'feature', value : 'auto')
|
||||
|
|
1119
subprojects/gst-plugins-bad/tests/check/libs/mse.c
Normal file
1119
subprojects/gst-plugins-bad/tests/check/libs/mse.c
Normal file
File diff suppressed because it is too large
Load diff
|
@ -85,6 +85,7 @@ base_tests = [
|
|||
[['libs/nalutils.c', '../../gst-libs/gst/codecparsers/nalutils.c'], false, [nalutils_dep]],
|
||||
[['libs/mpegts.c'], false, [gstmpegts_dep]],
|
||||
[['libs/mpegvideoparser.c'], false, [gstcodecparsers_dep]],
|
||||
[['libs/mse.c'], false, [gstmse_private_test_dep]],
|
||||
[['libs/planaraudioadapter.c'], false, [gstbadaudio_dep]],
|
||||
[['libs/play.c'], not enable_gst_play_tests, [gstplay_dep, libsoup_dep]],
|
||||
[['libs/vc1parser.c'], false, [gstcodecparsers_dep]],
|
||||
|
|
BIN
subprojects/gst-plugins-bad/tests/files/mse.mp4
Normal file
BIN
subprojects/gst-plugins-bad/tests/files/mse.mp4
Normal file
Binary file not shown.
BIN
subprojects/gst-plugins-bad/tests/files/mse.webm
Normal file
BIN
subprojects/gst-plugins-bad/tests/files/mse.webm
Normal file
Binary file not shown.
|
@ -353,6 +353,7 @@ static const gchar *log_domains[] = {
|
|||
"GStreamer-GL",
|
||||
"GStreamer-InsertBin",
|
||||
"GStreamer-ISOFF",
|
||||
"GStreamer-MSE",
|
||||
"GStreamer-MpegTS",
|
||||
"GStreamer-Net",
|
||||
"GStreamer-OpenCV",
|
||||
|
|
Loading…
Reference in a new issue