qtmux: port to GstAggregator

This commit is contained in:
Mathieu Duponchelle 2019-12-03 15:30:06 +01:00 committed by GStreamer Merge Bot
parent 4d7d577496
commit e2462005fb
3 changed files with 434 additions and 384 deletions

File diff suppressed because it is too large Load diff

View file

@ -44,7 +44,7 @@
#define __GST_QT_MUX_H__ #define __GST_QT_MUX_H__
#include <gst/gst.h> #include <gst/gst.h>
#include <gst/base/gstcollectpads.h> #include <gst/base/gstaggregator.h>
#include "fourcc.h" #include "fourcc.h"
#include "atoms.h" #include "atoms.h"
@ -63,7 +63,8 @@ G_BEGIN_DECLS
typedef struct _GstQTMux GstQTMux; typedef struct _GstQTMux GstQTMux;
typedef struct _GstQTMuxClass GstQTMuxClass; typedef struct _GstQTMuxClass GstQTMuxClass;
typedef struct _GstQTPad GstQTPad; typedef struct _GstQTMuxPad GstQTMuxPad;
typedef struct _GstQTMuxPadClass GstQTMuxPadClass;
/* /*
* GstQTPadPrepareBufferFunc * GstQTPadPrepareBufferFunc
@ -75,17 +76,31 @@ typedef struct _GstQTPad GstQTPad;
* being muxed. (Originally added for image/x-jpc support, for which buffers * being muxed. (Originally added for image/x-jpc support, for which buffers
* need to be wrapped into a isom box) * need to be wrapped into a isom box)
*/ */
typedef GstBuffer * (*GstQTPadPrepareBufferFunc) (GstQTPad * pad, typedef GstBuffer * (*GstQTPadPrepareBufferFunc) (GstQTMuxPad * pad,
GstBuffer * buf, GstQTMux * qtmux); GstBuffer * buf, GstQTMux * qtmux);
typedef gboolean (*GstQTPadSetCapsFunc) (GstQTMuxPad * pad, GstCaps * caps);
typedef GstBuffer * (*GstQTPadCreateEmptyBufferFunc) (GstQTMuxPad * pad, gint64 duration);
typedef gboolean (*GstQTPadSetCapsFunc) (GstQTPad * pad, GstCaps * caps); GType gst_qt_mux_pad_get_type (void);
typedef GstBuffer * (*GstQTPadCreateEmptyBufferFunc) (GstQTPad * pad, gint64 duration);
#define QTMUX_NO_OF_TS 10 #define GST_TYPE_QT_MUX_PAD \
(gst_qt_mux_pad_get_type())
#define GST_QT_MUX_PAD(obj) \
(G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_QT_MUX_PAD, GstQTMuxPad))
#define GST_QT_MUX_PAD_CLASS(klass) \
(G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_QT_MUX_PAD, GstQTMuxPadClass))
#define GST_IS_QT_MUX_PAD(obj) \
(G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_QT_MUX_PAD))
#define GST_IS_QT_MUX_PAD_CLASS(klass) \
(G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_QT_MUX_PAD))
#define GST_QT_MUX_PAD_CAST(obj) \
((GstQTMuxPad *)(obj))
struct _GstQTPad struct _GstQTMuxPad
{ {
GstCollectData collect; /* we extend the CollectData */ GstAggregatorPad parent;
guint32 trak_timescale;
/* fourcc id of stream */ /* fourcc id of stream */
guint32 fourcc; guint32 fourcc;
@ -122,6 +137,8 @@ struct _GstQTPad
GstClockTime first_ts; GstClockTime first_ts;
GstClockTime first_dts; GstClockTime first_dts;
gint64 dts; /* the signed version of the DTS converted to running time. */
/* all the atom and chunk book-keeping is delegated here /* all the atom and chunk book-keeping is delegated here
* unowned/uncounted reference, parent MOOV owns */ * unowned/uncounted reference, parent MOOV owns */
AtomTRAK *trak; AtomTRAK *trak;
@ -162,6 +179,13 @@ struct _GstQTPad
GstFlowReturn flow_status; GstFlowReturn flow_status;
}; };
struct _GstQTMuxPadClass
{
GstAggregatorPadClass parent;
};
#define QTMUX_NO_OF_TS 10
typedef enum _GstQTMuxState typedef enum _GstQTMuxState
{ {
GST_QT_MUX_STATE_NONE, GST_QT_MUX_STATE_NONE,
@ -181,11 +205,7 @@ typedef enum _GstQtMuxMode {
struct _GstQTMux struct _GstQTMux
{ {
GstElement element; GstAggregator parent;
GstPad *srcpad;
GstCollectPads *collect;
GSList *sinkpads;
/* state */ /* state */
GstQTMuxState state; GstQTMuxState state;
@ -215,7 +235,7 @@ struct _GstQTMux
GstClockTime last_dts; GstClockTime last_dts;
/* Last pad we used for writing the current chunk */ /* Last pad we used for writing the current chunk */
GstQTPad *current_pad; GstQTMuxPad *current_pad;
guint64 current_chunk_size; guint64 current_chunk_size;
GstClockTime current_chunk_duration; GstClockTime current_chunk_duration;
guint64 current_chunk_offset; guint64 current_chunk_offset;
@ -299,7 +319,7 @@ struct _GstQTMux
struct _GstQTMuxClass struct _GstQTMuxClass
{ {
GstElementClass parent_class; GstAggregatorClass parent_class;
GstQTMuxFormat format; GstQTMuxFormat format;
}; };

View file

@ -126,14 +126,14 @@ setup_src_pad (GstElement * element,
sinkpad = gst_element_get_request_pad (element, sinkname); sinkpad = gst_element_get_request_pad (element, sinkname);
fail_if (sinkpad == NULL, "Could not get sink pad from %s", fail_if (sinkpad == NULL, "Could not get sink pad from %s",
GST_ELEMENT_NAME (element)); GST_ELEMENT_NAME (element));
/* references are owned by: 1) us, 2) qtmux, 3) collect pads */ /* references are owned by: 1) us, 2) qtmux */
ASSERT_OBJECT_REFCOUNT (sinkpad, "sinkpad", 3); ASSERT_OBJECT_REFCOUNT (sinkpad, "sinkpad", 2);
fail_unless (gst_pad_link (srcpad, sinkpad) == GST_PAD_LINK_OK, fail_unless (gst_pad_link (srcpad, sinkpad) == GST_PAD_LINK_OK,
"Could not link source and %s sink pads", GST_ELEMENT_NAME (element)); "Could not link source and %s sink pads", GST_ELEMENT_NAME (element));
gst_object_unref (sinkpad); /* because we got it higher up */ gst_object_unref (sinkpad); /* because we got it higher up */
/* references are owned by: 1) qtmux, 2) collect pads */ /* references are owned by: 1) qtmux */
ASSERT_OBJECT_REFCOUNT (sinkpad, "sinkpad", 2); ASSERT_OBJECT_REFCOUNT (sinkpad, "sinkpad", 1);
return srcpad; return srcpad;
} }
@ -146,14 +146,14 @@ teardown_src_pad (GstPad * srcpad)
/* clean up floating src pad */ /* clean up floating src pad */
sinkpad = gst_pad_get_peer (srcpad); sinkpad = gst_pad_get_peer (srcpad);
fail_if (sinkpad == NULL); fail_if (sinkpad == NULL);
/* pad refs held by 1) qtmux 2) collectpads and 3) us (through _get_peer) */ /* pad refs held by 1) qtmux 2) us (through _get_peer) */
ASSERT_OBJECT_REFCOUNT (sinkpad, "sinkpad", 3); ASSERT_OBJECT_REFCOUNT (sinkpad, "sinkpad", 2);
gst_pad_unlink (srcpad, sinkpad); gst_pad_unlink (srcpad, sinkpad);
/* after unlinking, pad refs still held by /* after unlinking, pad refs still held by
* 1) qtmux and 2) collectpads and 3) us (through _get_peer) */ * 1) qtmux and 2) us (through _get_peer) */
ASSERT_OBJECT_REFCOUNT (sinkpad, "sinkpad", 3); ASSERT_OBJECT_REFCOUNT (sinkpad, "sinkpad", 2);
gst_object_unref (sinkpad); gst_object_unref (sinkpad);
/* one more ref is held by element itself */ /* one more ref is held by element itself */
@ -177,6 +177,28 @@ qtmux_sinkpad_query (GstPad * pad, GstObject * parent, GstQuery * query)
return ret; return ret;
} }
static gboolean have_eos;
static GCond eos_cond;
static GMutex event_mutex;
static gboolean
qtmux_sinkpad_event (GstPad * pad, GstObject * parent, GstEvent * event)
{
gboolean res = TRUE;
g_mutex_lock (&event_mutex);
if (GST_EVENT_TYPE (event) == GST_EVENT_EOS) {
have_eos = TRUE;
GST_DEBUG ("signal EOS");
g_cond_broadcast (&eos_cond);
}
g_mutex_unlock (&event_mutex);
gst_event_unref (event);
return res;
}
static GstElement * static GstElement *
setup_qtmux (GstStaticPadTemplate * srctemplate, const gchar * sinkname, setup_qtmux (GstStaticPadTemplate * srctemplate, const gchar * sinkname,
gboolean seekable) gboolean seekable)
@ -184,12 +206,18 @@ setup_qtmux (GstStaticPadTemplate * srctemplate, const gchar * sinkname,
GstElement *qtmux; GstElement *qtmux;
GST_DEBUG ("setup_qtmux"); GST_DEBUG ("setup_qtmux");
g_cond_init (&eos_cond);
g_mutex_init (&event_mutex);
have_eos = FALSE;
qtmux = gst_check_setup_element ("qtmux"); qtmux = gst_check_setup_element ("qtmux");
mysrcpad = setup_src_pad (qtmux, srctemplate, sinkname); mysrcpad = setup_src_pad (qtmux, srctemplate, sinkname);
mysinkpad = gst_check_setup_sink_pad (qtmux, &sinktemplate); mysinkpad = gst_check_setup_sink_pad (qtmux, &sinktemplate);
downstream_is_seekable = seekable; downstream_is_seekable = seekable;
gst_pad_set_query_function (mysinkpad, qtmux_sinkpad_query); gst_pad_set_query_function (mysinkpad, qtmux_sinkpad_query);
gst_pad_set_event_function (mysinkpad, qtmux_sinkpad_event);
gst_pad_set_active (mysrcpad, TRUE); gst_pad_set_active (mysrcpad, TRUE);
gst_pad_set_active (mysinkpad, TRUE); gst_pad_set_active (mysinkpad, TRUE);
@ -197,6 +225,16 @@ setup_qtmux (GstStaticPadTemplate * srctemplate, const gchar * sinkname,
return qtmux; return qtmux;
} }
static void
wait_for_eos (void)
{
g_mutex_lock (&event_mutex);
while (!have_eos)
g_cond_wait (&eos_cond, &event_mutex);
g_mutex_unlock (&event_mutex);
}
static void static void
cleanup_qtmux (GstElement * qtmux, const gchar * sinkname) cleanup_qtmux (GstElement * qtmux, const gchar * sinkname)
{ {
@ -250,6 +288,9 @@ check_qtmux_pad (GstStaticPadTemplate * srctemplate, const gchar * sinkname,
/* send eos to have moov written */ /* send eos to have moov written */
fail_unless (gst_pad_push_event (mysrcpad, gst_event_new_eos ()) == TRUE); fail_unless (gst_pad_push_event (mysrcpad, gst_event_new_eos ()) == TRUE);
/* Muxing occurs on the aggregate thread */
wait_for_eos ();
num_buffers = g_list_length (buffers); num_buffers = g_list_length (buffers);
/* at least expect ftyp, mdat header, buffer chunk and moov */ /* at least expect ftyp, mdat header, buffer chunk and moov */
fail_unless (num_buffers >= 4); fail_unless (num_buffers >= 4);
@ -342,6 +383,8 @@ check_qtmux_pad_fragmented (GstStaticPadTemplate * srctemplate,
/* send eos to have all written */ /* send eos to have all written */
fail_unless (gst_pad_push_event (mysrcpad, gst_event_new_eos ()) == TRUE); fail_unless (gst_pad_push_event (mysrcpad, gst_event_new_eos ()) == TRUE);
wait_for_eos ();
num_buffers = g_list_length (buffers); num_buffers = g_list_length (buffers);
/* at least expect ftyp, moov, moof, mdat header, buffer chunk /* at least expect ftyp, moov, moof, mdat header, buffer chunk
* and optionally mfra */ * and optionally mfra */
@ -833,6 +876,7 @@ test_average_bitrate_custom (const gchar * elementname,
gint64 total_bytes = 0; gint64 total_bytes = 0;
GstClockTime total_duration = 0; GstClockTime total_duration = 0;
GstSegment segment; GstSegment segment;
GstBus *bus;
location = g_strdup_printf ("%s/%s-%d", g_get_tmp_dir (), "qtmuxtest", location = g_strdup_printf ("%s/%s-%d", g_get_tmp_dir (), "qtmuxtest",
g_random_int ()); g_random_int ());
@ -845,6 +889,9 @@ test_average_bitrate_custom (const gchar * elementname,
fail_unless (mysrcpad != NULL); fail_unless (mysrcpad != NULL);
gst_pad_set_active (mysrcpad, TRUE); gst_pad_set_active (mysrcpad, TRUE);
bus = gst_bus_new ();
gst_element_set_bus (filesink, bus);
gst_object_unref (bus);
fail_unless (gst_element_set_state (filesink, fail_unless (gst_element_set_state (filesink,
GST_STATE_PLAYING) != GST_STATE_CHANGE_FAILURE, GST_STATE_PLAYING) != GST_STATE_CHANGE_FAILURE,
@ -878,9 +925,14 @@ test_average_bitrate_custom (const gchar * elementname,
/* send eos to have moov written */ /* send eos to have moov written */
fail_unless (gst_pad_push_event (mysrcpad, gst_event_new_eos ()) == TRUE); fail_unless (gst_pad_push_event (mysrcpad, gst_event_new_eos ()) == TRUE);
gst_message_unref (gst_bus_timed_pop_filtered (bus, GST_CLOCK_TIME_NONE,
GST_MESSAGE_EOS));
gst_element_set_state (qtmux, GST_STATE_NULL); gst_element_set_state (qtmux, GST_STATE_NULL);
gst_element_set_state (filesink, GST_STATE_NULL); gst_element_set_state (filesink, GST_STATE_NULL);
gst_element_set_bus (filesink, NULL);
gst_check_drop_buffers (); gst_check_drop_buffers ();
gst_pad_set_active (mysrcpad, FALSE); gst_pad_set_active (mysrcpad, FALSE);
teardown_src_pad (mysrcpad); teardown_src_pad (mysrcpad);
@ -1212,6 +1264,7 @@ run_muxing_test (struct TestInputData *input1, struct TestInputData *input2)
gchar *location; gchar *location;
GstElement *qtmux; GstElement *qtmux;
GstElement *filesink; GstElement *filesink;
GstBus *bus;
location = g_strdup_printf ("%s/%s-%d", g_get_tmp_dir (), "qtmuxtest", location = g_strdup_printf ("%s/%s-%d", g_get_tmp_dir (), "qtmuxtest",
g_random_int ()); g_random_int ());
@ -1220,6 +1273,10 @@ run_muxing_test (struct TestInputData *input1, struct TestInputData *input2)
g_object_set (filesink, "location", location, NULL); g_object_set (filesink, "location", location, NULL);
gst_element_link (qtmux, filesink); gst_element_link (qtmux, filesink);
bus = gst_bus_new ();
gst_element_set_bus (filesink, bus);
gst_object_unref (bus);
input1->srcpad = setup_src_pad (qtmux, &srcvideorawtemplate, "video_%u"); input1->srcpad = setup_src_pad (qtmux, &srcvideorawtemplate, "video_%u");
fail_unless (input1->srcpad != NULL); fail_unless (input1->srcpad != NULL);
gst_pad_set_active (input1->srcpad, TRUE); gst_pad_set_active (input1->srcpad, TRUE);
@ -1240,16 +1297,19 @@ run_muxing_test (struct TestInputData *input1, struct TestInputData *input2)
input2->thread = input2->thread =
g_thread_new ("test-push-data-2", test_input_push_data, input2); g_thread_new ("test-push-data-2", test_input_push_data, input2);
/* FIXME set a mainloop and wait for EOS */
g_thread_join (input1->thread); g_thread_join (input1->thread);
g_thread_join (input2->thread); g_thread_join (input2->thread);
input1->thread = NULL; input1->thread = NULL;
input2->thread = NULL; input2->thread = NULL;
gst_message_unref (gst_bus_timed_pop_filtered (bus, GST_CLOCK_TIME_NONE,
GST_MESSAGE_EOS));
gst_element_set_state (qtmux, GST_STATE_NULL); gst_element_set_state (qtmux, GST_STATE_NULL);
gst_element_set_state (filesink, GST_STATE_NULL); gst_element_set_state (filesink, GST_STATE_NULL);
gst_element_set_bus (filesink, NULL);
check_output (location, input1, input2); check_output (location, input1, input2);
gst_object_unref (filesink); gst_object_unref (filesink);