mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-11-26 19:51:11 +00:00
mpegtsmux: aggregator port
This commit is contained in:
parent
a57f4dc8d9
commit
a1cadd11b8
14 changed files with 725 additions and 973 deletions
|
@ -34,7 +34,7 @@ GST_STATIC_PAD_TEMPLATE ("src",
|
|||
GST_PAD_SRC,
|
||||
GST_PAD_ALWAYS,
|
||||
GST_STATIC_CAPS ("video/mpegts, "
|
||||
"systemstream = (boolean) true, " "packetsize = (int) 192 ")
|
||||
"systemstream = (boolean) true, " "packetsize = (int) 188 ")
|
||||
);
|
||||
|
||||
static GstStaticPadTemplate gst_atsc_mux_sink_factory =
|
||||
|
@ -155,7 +155,7 @@ gst_atsc_mux_create_ts_mux (GstBaseTsMux * mpegtsmux)
|
|||
|
||||
static guint
|
||||
gst_atsc_mux_handle_media_type (GstBaseTsMux * mux, const gchar * media_type,
|
||||
GstBaseTsPadData * ts_data)
|
||||
GstBaseTsMuxPad * pad)
|
||||
{
|
||||
guint ret = TSMUX_ST_RESERVED;
|
||||
|
||||
|
@ -182,11 +182,11 @@ gst_atsc_mux_class_init (GstATSCMuxClass * klass)
|
|||
mpegtsmux_class->create_ts_mux = gst_atsc_mux_create_ts_mux;
|
||||
mpegtsmux_class->handle_media_type = gst_atsc_mux_handle_media_type;
|
||||
|
||||
gst_element_class_add_static_pad_template (gstelement_class,
|
||||
&gst_atsc_mux_sink_factory);
|
||||
gst_element_class_add_static_pad_template_with_gtype (gstelement_class,
|
||||
&gst_atsc_mux_sink_factory, GST_TYPE_BASE_TS_MUX_PAD);
|
||||
|
||||
gst_element_class_add_static_pad_template (gstelement_class,
|
||||
&gst_atsc_mux_src_factory);
|
||||
gst_element_class_add_static_pad_template_with_gtype (gstelement_class,
|
||||
&gst_atsc_mux_src_factory, GST_TYPE_AGGREGATOR_PAD);
|
||||
}
|
||||
|
||||
static void
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -86,12 +86,73 @@
|
|||
#include <gst/gst.h>
|
||||
#include <gst/base/gstcollectpads.h>
|
||||
#include <gst/base/gstadapter.h>
|
||||
#include <glib-object.h>
|
||||
#include <gst/base/gstaggregator.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#include "tsmux/tsmux.h"
|
||||
|
||||
#define GST_TYPE_BASE_TS_MUX_PAD (gst_base_ts_mux_pad_get_type())
|
||||
#define GST_BASE_TS_MUX_PAD(obj) \
|
||||
(G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_BASE_TS_MUX_PAD, GstBaseTsMuxPad))
|
||||
#define GST_BASE_TS_MUX_PAD_CAST(obj) ((GstBaseTsMuxPad *)(obj))
|
||||
#define GST_BASE_TS_MUX_PAD_CLASS(klass) \
|
||||
(G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_BASE_TS_MUX_PAD, GstBaseTsMuxPadClass))
|
||||
#define GST_IS_BASE_TS_MUX_PAD(obj) \
|
||||
(G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_BASE_TS_MUX_PAD))
|
||||
#define GST_IS_BASE_TS_MUX_PAD_CLASS(klass) \
|
||||
(G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_BASE_TS_MUX_PAD))
|
||||
#define GST_BASE_TS_MUX_PAD_GET_CLASS(obj) \
|
||||
(G_TYPE_INSTANCE_GET_CLASS((obj),GST_TYPE_BASE_TS_MUX_PAD,GstBaseTsMuxPadClass))
|
||||
|
||||
typedef struct _GstBaseTsMuxPad GstBaseTsMuxPad;
|
||||
typedef struct _GstBaseTsMuxPadClass GstBaseTsMuxPadClass;
|
||||
typedef struct _GstBaseTsMuxPadPrivate GstBaseTsMuxPadPrivate;
|
||||
typedef struct GstBaseTsMux GstBaseTsMux;
|
||||
typedef struct GstBaseTsMuxClass GstBaseTsMuxClass;
|
||||
typedef struct GstBaseTsPadData GstBaseTsPadData;
|
||||
|
||||
typedef GstBuffer * (*GstBaseTsMuxPadPrepareFunction) (GstBuffer * buf,
|
||||
GstBaseTsMuxPad * data, GstBaseTsMux * mux);
|
||||
|
||||
typedef void (*GstBaseTsMuxPadFreePrepareDataFunction) (gpointer prepare_data);
|
||||
|
||||
struct _GstBaseTsMuxPad
|
||||
{
|
||||
GstAggregatorPad parent;
|
||||
|
||||
gint pid;
|
||||
TsMuxStream *stream;
|
||||
|
||||
/* most recent DTS */
|
||||
gint64 dts;
|
||||
|
||||
/* optional codec data available in the caps */
|
||||
GstBuffer *codec_data;
|
||||
|
||||
/* Opaque data pointer to a structure used by the prepare function */
|
||||
gpointer prepare_data;
|
||||
|
||||
/* handler to prepare input data */
|
||||
GstBaseTsMuxPadPrepareFunction prepare_func;
|
||||
/* handler to free the private data */
|
||||
GstBaseTsMuxPadFreePrepareDataFunction free_func;
|
||||
|
||||
/* program id to which it is attached to (not program pid) */
|
||||
gint prog_id;
|
||||
/* program this stream belongs to */
|
||||
TsMuxProgram *prog;
|
||||
|
||||
gchar *language;
|
||||
};
|
||||
|
||||
struct _GstBaseTsMuxPadClass
|
||||
{
|
||||
GstAggregatorPadClass parent_class;
|
||||
};
|
||||
|
||||
GType gst_base_ts_mux_pad_get_type (void);
|
||||
|
||||
#define GST_TYPE_BASE_TS_MUX (gst_base_ts_mux_get_type())
|
||||
#define GST_BASE_TS_MUX(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), GST_TYPE_BASE_TS_MUX, GstBaseTsMux))
|
||||
#define GST_BASE_TS_MUX_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_BASE_TS_MUX, GstBaseTsMuxClass))
|
||||
|
@ -99,21 +160,8 @@ G_BEGIN_DECLS
|
|||
|
||||
#define GST_BASE_TS_MUX_NORMAL_PACKET_LENGTH 188
|
||||
|
||||
typedef struct GstBaseTsMux GstBaseTsMux;
|
||||
typedef struct GstBaseTsMuxClass GstBaseTsMuxClass;
|
||||
typedef struct GstBaseTsPadData GstBaseTsPadData;
|
||||
|
||||
typedef GstBuffer * (*GstBaseTsPadDataPrepareFunction) (GstBuffer * buf,
|
||||
GstBaseTsPadData * data, GstBaseTsMux * mux);
|
||||
|
||||
typedef void (*GstBaseTsPadDataFreePrepareDataFunction) (gpointer prepare_data);
|
||||
|
||||
struct GstBaseTsMux {
|
||||
GstElement parent;
|
||||
|
||||
GstPad *srcpad;
|
||||
|
||||
GstCollectPads *collect;
|
||||
GstAggregator parent;
|
||||
|
||||
TsMux *tsmux;
|
||||
GHashTable *programs;
|
||||
|
@ -166,10 +214,10 @@ struct GstBaseTsMux {
|
|||
* Called at EOS, if the subclass has data it needs to drain.
|
||||
*/
|
||||
struct GstBaseTsMuxClass {
|
||||
GstElementClass parent_class;
|
||||
GstAggregatorClass parent_class;
|
||||
|
||||
TsMux * (*create_ts_mux) (GstBaseTsMux *mux);
|
||||
guint (*handle_media_type) (GstBaseTsMux *mux, const gchar *media_type, GstBaseTsPadData * ts_data);
|
||||
guint (*handle_media_type) (GstBaseTsMux *mux, const gchar *media_type, GstBaseTsMuxPad * pad);
|
||||
void (*allocate_packet) (GstBaseTsMux *mux, GstBuffer **buffer);
|
||||
gboolean (*output_packet) (GstBaseTsMux *mux, GstBuffer *buffer, gint64 new_pcr);
|
||||
void (*reset) (GstBaseTsMux *mux);
|
||||
|
@ -179,6 +227,11 @@ struct GstBaseTsMuxClass {
|
|||
void gst_base_ts_mux_set_packet_size (GstBaseTsMux *mux, gsize size);
|
||||
void gst_base_ts_mux_set_automatic_alignment (GstBaseTsMux *mux, gsize alignment);
|
||||
|
||||
typedef GstBuffer * (*GstBaseTsPadDataPrepareFunction) (GstBuffer * buf,
|
||||
GstBaseTsPadData * data, GstBaseTsMux * mux);
|
||||
|
||||
typedef void (*GstBaseTsPadDataFreePrepareDataFunction) (gpointer prepare_data);
|
||||
|
||||
struct GstBaseTsPadData {
|
||||
/* parent */
|
||||
GstCollectData collect;
|
||||
|
@ -210,7 +263,6 @@ struct GstBaseTsPadData {
|
|||
|
||||
GType gst_base_ts_mux_get_type (void);
|
||||
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif
|
||||
|
|
|
@ -90,7 +90,7 @@
|
|||
#define GST_CAT_DEFAULT gst_base_ts_mux_debug
|
||||
|
||||
GstBuffer *
|
||||
gst_base_ts_mux_prepare_aac (GstBuffer * buf, GstBaseTsPadData * data,
|
||||
gst_base_ts_mux_prepare_aac (GstBuffer * buf, GstBaseTsMuxPad * pad,
|
||||
GstBaseTsMux * mux)
|
||||
{
|
||||
guint8 adts_header[7] = { 0, };
|
||||
|
@ -106,7 +106,7 @@ gst_base_ts_mux_prepare_aac (GstBuffer * buf, GstBaseTsPadData * data,
|
|||
gst_buffer_copy_into (out_buf, buf,
|
||||
GST_BUFFER_COPY_METADATA | GST_BUFFER_COPY_TIMESTAMPS, 0, 0);
|
||||
|
||||
gst_buffer_map (data->codec_data, &codec_data_map, GST_MAP_READ);
|
||||
gst_buffer_map (pad->codec_data, &codec_data_map, GST_MAP_READ);
|
||||
|
||||
/* Generate ADTS header */
|
||||
obj_type = GST_READ_UINT8 (codec_data_map.data) >> 3;
|
||||
|
@ -149,7 +149,7 @@ gst_base_ts_mux_prepare_aac (GstBuffer * buf, GstBaseTsPadData * data,
|
|||
/* Now copy complete frame */
|
||||
gst_buffer_fill (out_buf, out_offset, buf_map.data, buf_map.size);
|
||||
|
||||
gst_buffer_unmap (data->codec_data, &codec_data_map);
|
||||
gst_buffer_unmap (pad->codec_data, &codec_data_map);
|
||||
gst_buffer_unmap (buf, &buf_map);
|
||||
|
||||
return out_buf;
|
||||
|
|
|
@ -85,7 +85,7 @@
|
|||
|
||||
#include "gstbasetsmux.h"
|
||||
|
||||
GstBuffer * gst_base_ts_mux_prepare_aac (GstBuffer * buf, GstBaseTsPadData * data,
|
||||
GstBuffer * gst_base_ts_mux_prepare_aac (GstBuffer * buf, GstBaseTsMuxPad * pad,
|
||||
GstBaseTsMux * mux);
|
||||
|
||||
#endif /* __BASETSMUX_AAC_H__ */
|
||||
|
|
|
@ -36,10 +36,10 @@
|
|||
#define GST_CAT_DEFAULT gst_base_ts_mux_debug
|
||||
|
||||
GstBuffer *
|
||||
gst_base_ts_mux_prepare_jpeg2000 (GstBuffer * buf, GstBaseTsPadData * data,
|
||||
gst_base_ts_mux_prepare_jpeg2000 (GstBuffer * buf, GstBaseTsMuxPad * pad,
|
||||
GstBaseTsMux * mux)
|
||||
{
|
||||
j2k_private_data *private_data = data->prepare_data;
|
||||
j2k_private_data *private_data = pad->prepare_data;
|
||||
GstByteWriter wr;
|
||||
GstBuffer *out_buf = NULL;
|
||||
guint8 *elsm_header = NULL;
|
||||
|
|
|
@ -55,7 +55,7 @@ typedef struct j2k_private_data
|
|||
guint8 color_spec;
|
||||
} j2k_private_data;
|
||||
|
||||
GstBuffer *gst_base_ts_mux_prepare_jpeg2000 (GstBuffer * buf, GstBaseTsPadData * data,
|
||||
GstBuffer *gst_base_ts_mux_prepare_jpeg2000 (GstBuffer * buf, GstBaseTsMuxPad * pad,
|
||||
GstBaseTsMux * mux);
|
||||
|
||||
void gst_base_ts_mux_free_jpeg2000 (gpointer prepare_data);
|
||||
|
|
|
@ -91,7 +91,7 @@
|
|||
#define GST_CAT_DEFAULT gst_base_ts_mux_debug
|
||||
|
||||
GstBuffer *
|
||||
gst_base_ts_mux_prepare_opus (GstBuffer * buf, GstBaseTsPadData * pad_data,
|
||||
gst_base_ts_mux_prepare_opus (GstBuffer * buf, GstBaseTsMuxPad * pad,
|
||||
GstBaseTsMux * mux)
|
||||
{
|
||||
gssize insize = gst_buffer_get_size (buf);
|
||||
|
|
|
@ -85,7 +85,7 @@
|
|||
|
||||
#include "gstbasetsmux.h"
|
||||
|
||||
GstBuffer * gst_base_ts_mux_prepare_opus (GstBuffer * buf, GstBaseTsPadData * data,
|
||||
GstBuffer * gst_base_ts_mux_prepare_opus (GstBuffer * buf, GstBaseTsMuxPad * pad,
|
||||
GstBaseTsMux * mux);
|
||||
|
||||
#endif /* __BASETSMUX_OPUS_H__ */
|
||||
|
|
|
@ -98,7 +98,7 @@
|
|||
*/
|
||||
|
||||
GstBuffer *
|
||||
gst_base_ts_mux_prepare_teletext (GstBuffer * buf, GstBaseTsPadData * pad_data,
|
||||
gst_base_ts_mux_prepare_teletext (GstBuffer * buf, GstBaseTsMuxPad * pad,
|
||||
GstBaseTsMux * mux)
|
||||
{
|
||||
GstBuffer *out_buf;
|
||||
|
|
|
@ -85,7 +85,7 @@
|
|||
|
||||
#include "gstbasetsmux.h"
|
||||
|
||||
GstBuffer * gst_base_ts_mux_prepare_teletext (GstBuffer * buf, GstBaseTsPadData * data,
|
||||
GstBuffer * gst_base_ts_mux_prepare_teletext (GstBuffer * buf, GstBaseTsMuxPad * pad,
|
||||
GstBaseTsMux * mux);
|
||||
|
||||
#endif /* __BASETSMUX_TTXT_H__ */
|
||||
|
|
|
@ -395,11 +395,11 @@ gst_mpeg_ts_mux_class_init (GstMpegTsMuxClass * klass)
|
|||
"Multiplexes media streams into an MPEG Transport Stream",
|
||||
"Fluendo <contact@fluendo.com>");
|
||||
|
||||
gst_element_class_add_static_pad_template (gstelement_class,
|
||||
&gst_mpeg_ts_mux_sink_factory);
|
||||
gst_element_class_add_static_pad_template_with_gtype (gstelement_class,
|
||||
&gst_mpeg_ts_mux_sink_factory, GST_TYPE_BASE_TS_MUX_PAD);
|
||||
|
||||
gst_element_class_add_static_pad_template (gstelement_class,
|
||||
&gst_mpeg_ts_mux_src_factory);
|
||||
gst_element_class_add_static_pad_template_with_gtype (gstelement_class,
|
||||
&gst_mpeg_ts_mux_src_factory, GST_TYPE_AGGREGATOR_PAD);
|
||||
|
||||
g_object_class_install_property (gobject_class, PROP_M2TS_MODE,
|
||||
g_param_spec_boolean ("m2ts-mode", "M2TS(192 bytes) Mode",
|
||||
|
|
|
@ -1085,7 +1085,7 @@ pad_stream (TsMux * mux, TsMuxStream * stream, gint64 cur_ts, gint64 * cur_pcr)
|
|||
GST_LOG ("Transport stream bitrate: %" G_GUINT64_FORMAT, bitrate);
|
||||
|
||||
if (bitrate < mux->bitrate) {
|
||||
GST_LOG_OBJECT (mux, "Padding transport stream");
|
||||
GST_LOG ("Padding transport stream");
|
||||
|
||||
if (!tsmux_get_buffer (mux, &buf)) {
|
||||
ret = FALSE;
|
||||
|
|
|
@ -75,14 +75,14 @@ setup_src_pad (GstElement * element,
|
|||
sinkpad = gst_element_get_request_pad (element, sinkname);
|
||||
fail_if (sinkpad == NULL, "Could not get sink pad from %s",
|
||||
GST_ELEMENT_NAME (element));
|
||||
/* references are owned by: 1) us, 2) tsmux, 3) collect pads */
|
||||
ASSERT_OBJECT_REFCOUNT (sinkpad, "sinkpad", 3);
|
||||
/* references are owned by: 1) us, 2) tsmux */
|
||||
ASSERT_OBJECT_REFCOUNT (sinkpad, "sinkpad", 2);
|
||||
fail_unless (gst_pad_link (srcpad, sinkpad) == GST_PAD_LINK_OK,
|
||||
"Could not link source and %s sink pads", GST_ELEMENT_NAME (element));
|
||||
gst_object_unref (sinkpad); /* because we got it higher up */
|
||||
|
||||
/* references are owned by: 1) tsmux, 2) collect pads */
|
||||
ASSERT_OBJECT_REFCOUNT (sinkpad, "sinkpad", 2);
|
||||
/* references are owned by: 1) tsmux */
|
||||
ASSERT_OBJECT_REFCOUNT (sinkpad, "sinkpad", 1);
|
||||
|
||||
if (padname)
|
||||
*padname = g_strdup (GST_PAD_NAME (sinkpad));
|
||||
|
@ -98,16 +98,16 @@ teardown_src_pad (GstElement * element, const gchar * sinkname)
|
|||
/* clean up floating src pad */
|
||||
if (!(sinkpad = gst_element_get_static_pad (element, sinkname)))
|
||||
sinkpad = gst_element_get_request_pad (element, sinkname);
|
||||
/* pad refs held by 1) tsmux 2) collectpads and 3) us (through _get) */
|
||||
ASSERT_OBJECT_REFCOUNT (sinkpad, "sinkpad", 3);
|
||||
/* pad refs held by 1) tsmux 2) us (through _get) */
|
||||
ASSERT_OBJECT_REFCOUNT (sinkpad, "sinkpad", 2);
|
||||
srcpad = gst_pad_get_peer (sinkpad);
|
||||
|
||||
gst_pad_unlink (srcpad, sinkpad);
|
||||
GST_DEBUG ("src %p", srcpad);
|
||||
|
||||
/* after unlinking, pad refs still held by
|
||||
* 1) tsmux and 2) collectpads and 3) us (through _get) */
|
||||
ASSERT_OBJECT_REFCOUNT (sinkpad, "sinkpad", 3);
|
||||
* 1) tsmux and 2) us (through _get) */
|
||||
ASSERT_OBJECT_REFCOUNT (sinkpad, "sinkpad", 2);
|
||||
gst_object_unref (sinkpad);
|
||||
/* one more ref is held by element itself */
|
||||
|
||||
|
@ -161,6 +161,7 @@ check_tsmux_pad (GstStaticPadTemplate * srctemplate,
|
|||
gint i;
|
||||
gint pmt_pid = -1, el_pid = -1, pcr_pid = -1, packets = 0;
|
||||
gchar *padname;
|
||||
GstQuery *drain;
|
||||
|
||||
mux = setup_tsmux (srctemplate, sinkname, &padname);
|
||||
|
||||
|
@ -200,6 +201,10 @@ check_tsmux_pad (GstStaticPadTemplate * srctemplate,
|
|||
ts += 40 * GST_MSECOND;
|
||||
}
|
||||
|
||||
drain = gst_query_new_drain ();
|
||||
gst_pad_peer_query (mysrcpad, drain);
|
||||
gst_query_unref (drain);
|
||||
|
||||
if (check_func)
|
||||
check_func (buffers);
|
||||
|
||||
|
@ -366,339 +371,6 @@ GST_START_TEST (test_audio)
|
|||
|
||||
GST_END_TEST;
|
||||
|
||||
|
||||
typedef struct _TestData
|
||||
{
|
||||
GstEvent *sink_event;
|
||||
gint src_events;
|
||||
} TestData;
|
||||
|
||||
typedef struct _ThreadData
|
||||
{
|
||||
GstPad *pad;
|
||||
GstBuffer *buffer;
|
||||
GstFlowReturn flow_return;
|
||||
GThread *thread;
|
||||
} ThreadData;
|
||||
|
||||
static gboolean
|
||||
src_event (GstPad * pad, GstObject * parent, GstEvent * event)
|
||||
{
|
||||
TestData *data = (TestData *) gst_pad_get_element_private (pad);
|
||||
|
||||
if (event->type == GST_EVENT_CUSTOM_UPSTREAM)
|
||||
data->src_events += 1;
|
||||
|
||||
gst_event_unref (event);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
sink_event (GstPad * pad, GstObject * parent, GstEvent * event)
|
||||
{
|
||||
TestData *data = (TestData *) gst_pad_get_element_private (pad);
|
||||
|
||||
if (event->type == GST_EVENT_CUSTOM_DOWNSTREAM)
|
||||
data->sink_event = event;
|
||||
|
||||
gst_event_unref (event);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
link_sinks (GstElement * mpegtsmux,
|
||||
GstPad ** src1, GstPad ** src2, GstPad ** src3, TestData * test_data)
|
||||
{
|
||||
GstPad *mux_sink1, *mux_sink2, *mux_sink3;
|
||||
|
||||
/* link 3 sink pads, 2 video 1 audio */
|
||||
*src1 = gst_pad_new_from_static_template (&video_src_template, "src1");
|
||||
gst_pad_set_active (*src1, TRUE);
|
||||
gst_pad_set_element_private (*src1, test_data);
|
||||
gst_pad_set_event_function (*src1, src_event);
|
||||
mux_sink1 = gst_element_get_request_pad (mpegtsmux, "sink_1");
|
||||
fail_unless (gst_pad_link (*src1, mux_sink1) == GST_PAD_LINK_OK);
|
||||
|
||||
*src2 = gst_pad_new_from_static_template (&video_src_template, "src2");
|
||||
gst_pad_set_active (*src2, TRUE);
|
||||
gst_pad_set_element_private (*src2, test_data);
|
||||
gst_pad_set_event_function (*src2, src_event);
|
||||
mux_sink2 = gst_element_get_request_pad (mpegtsmux, "sink_2");
|
||||
fail_unless (gst_pad_link (*src2, mux_sink2) == GST_PAD_LINK_OK);
|
||||
|
||||
*src3 = gst_pad_new_from_static_template (&audio_src_template, "src3");
|
||||
gst_pad_set_active (*src3, TRUE);
|
||||
gst_pad_set_element_private (*src3, test_data);
|
||||
gst_pad_set_event_function (*src3, src_event);
|
||||
mux_sink3 = gst_element_get_request_pad (mpegtsmux, "sink_3");
|
||||
fail_unless (gst_pad_link (*src3, mux_sink3) == GST_PAD_LINK_OK);
|
||||
|
||||
gst_object_unref (mux_sink1);
|
||||
gst_object_unref (mux_sink2);
|
||||
gst_object_unref (mux_sink3);
|
||||
}
|
||||
|
||||
static void
|
||||
link_src (GstElement * mpegtsmux, GstPad ** sink, TestData * test_data)
|
||||
{
|
||||
GstPad *mux_src;
|
||||
|
||||
mux_src = gst_element_get_static_pad (mpegtsmux, "src");
|
||||
*sink = gst_pad_new_from_static_template (&sink_template, "sink");
|
||||
gst_pad_set_active (*sink, TRUE);
|
||||
gst_pad_set_event_function (*sink, sink_event);
|
||||
gst_pad_set_element_private (*sink, test_data);
|
||||
fail_unless (gst_pad_link (mux_src, *sink) == GST_PAD_LINK_OK);
|
||||
|
||||
gst_object_unref (mux_src);
|
||||
}
|
||||
|
||||
static void
|
||||
setup_caps (GstElement * mpegtsmux, GstPad * src1, GstPad * src2, GstPad * src3)
|
||||
{
|
||||
GstSegment segment;
|
||||
GstCaps *caps;
|
||||
|
||||
gst_segment_init (&segment, GST_FORMAT_TIME);
|
||||
|
||||
caps = gst_caps_new_simple ("video/x-h264",
|
||||
"stream-format", G_TYPE_STRING, "byte-stream",
|
||||
"alignment", G_TYPE_STRING, "nal", NULL);
|
||||
gst_pad_push_event (src1, gst_event_new_stream_start ("1"));
|
||||
gst_pad_push_event (src1, gst_event_new_caps (caps));
|
||||
gst_pad_push_event (src1, gst_event_new_segment (&segment));
|
||||
gst_pad_push_event (src2, gst_event_new_stream_start ("2"));
|
||||
gst_pad_push_event (src2, gst_event_new_caps (caps));
|
||||
gst_pad_push_event (src2, gst_event_new_segment (&segment));
|
||||
gst_caps_unref (caps);
|
||||
caps = gst_caps_new_simple ("audio/mpeg", "mpegversion", G_TYPE_INT, 4,
|
||||
"stream-format", G_TYPE_STRING, "raw", "framed", G_TYPE_BOOLEAN, TRUE,
|
||||
NULL);
|
||||
gst_pad_push_event (src3, gst_event_new_stream_start ("3"));
|
||||
gst_pad_push_event (src3, gst_event_new_caps (caps));
|
||||
gst_pad_push_event (src3, gst_event_new_segment (&segment));
|
||||
gst_caps_unref (caps);
|
||||
}
|
||||
|
||||
static gpointer
|
||||
pad_push_thread (gpointer user_data)
|
||||
{
|
||||
ThreadData *data = (ThreadData *) user_data;
|
||||
|
||||
data->flow_return = gst_pad_push (data->pad, data->buffer);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static ThreadData *
|
||||
pad_push (GstPad * pad, GstBuffer * buffer, GstClockTime timestamp)
|
||||
{
|
||||
ThreadData *data;
|
||||
|
||||
data = g_new0 (ThreadData, 1);
|
||||
data->pad = pad;
|
||||
data->buffer = buffer;
|
||||
GST_BUFFER_TIMESTAMP (buffer) = timestamp;
|
||||
data->thread = g_thread_try_new ("gst-check", pad_push_thread, data, NULL);
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
GST_START_TEST (test_force_key_unit_event_downstream)
|
||||
{
|
||||
GstElement *mpegtsmux;
|
||||
GstPad *sink;
|
||||
GstPad *src1;
|
||||
GstPad *src2;
|
||||
GstPad *src3;
|
||||
GstEvent *sink_event;
|
||||
GstClockTime timestamp, stream_time, running_time;
|
||||
gboolean all_headers = TRUE;
|
||||
gint count = 0;
|
||||
ThreadData *thread_data_1, *thread_data_2, *thread_data_3, *thread_data_4;
|
||||
TestData test_data = { 0, };
|
||||
|
||||
mpegtsmux = gst_check_setup_element ("mpegtsmux");
|
||||
|
||||
link_src (mpegtsmux, &sink, &test_data);
|
||||
link_sinks (mpegtsmux, &src1, &src2, &src3, &test_data);
|
||||
gst_element_set_state (mpegtsmux, GST_STATE_PLAYING);
|
||||
setup_caps (mpegtsmux, src1, src2, src3);
|
||||
|
||||
/* send a force-key-unit event with running_time=2s */
|
||||
timestamp = stream_time = running_time = 2 * GST_SECOND;
|
||||
sink_event = gst_video_event_new_downstream_force_key_unit (timestamp,
|
||||
stream_time, running_time, all_headers, count);
|
||||
|
||||
fail_unless (gst_pad_push_event (src1, sink_event));
|
||||
fail_unless (test_data.sink_event == NULL);
|
||||
|
||||
/* push 4 buffers, make sure mpegtsmux handles the force-key-unit event when
|
||||
* the buffer with the requested running time is collected */
|
||||
thread_data_1 = pad_push (src1, gst_buffer_new (), 1 * GST_SECOND);
|
||||
thread_data_2 = pad_push (src2, gst_buffer_new (), 2 * GST_SECOND);
|
||||
thread_data_3 = pad_push (src3, gst_buffer_new (), 3 * GST_SECOND);
|
||||
|
||||
g_thread_join (thread_data_1->thread);
|
||||
fail_unless (test_data.sink_event == NULL);
|
||||
|
||||
/* push again on src1 so that the buffer on src2 is collected */
|
||||
thread_data_4 = pad_push (src1, gst_buffer_new (), 4 * GST_SECOND);
|
||||
|
||||
g_thread_join (thread_data_2->thread);
|
||||
fail_unless (test_data.sink_event != NULL);
|
||||
|
||||
gst_element_set_state (mpegtsmux, GST_STATE_NULL);
|
||||
|
||||
g_thread_join (thread_data_3->thread);
|
||||
g_thread_join (thread_data_4->thread);
|
||||
|
||||
g_free (thread_data_1);
|
||||
g_free (thread_data_2);
|
||||
g_free (thread_data_3);
|
||||
g_free (thread_data_4);
|
||||
gst_object_unref (src1);
|
||||
gst_object_unref (src2);
|
||||
gst_object_unref (src3);
|
||||
gst_object_unref (sink);
|
||||
gst_object_unref (mpegtsmux);
|
||||
}
|
||||
|
||||
GST_END_TEST;
|
||||
|
||||
GST_START_TEST (test_force_key_unit_event_upstream)
|
||||
{
|
||||
GstElement *mpegtsmux;
|
||||
GstPad *sink;
|
||||
GstPad *src1;
|
||||
GstPad *src2;
|
||||
GstPad *src3;
|
||||
GstClockTime timestamp, stream_time, running_time;
|
||||
gboolean all_headers = TRUE;
|
||||
gint count = 0;
|
||||
TestData test_data = { 0, };
|
||||
ThreadData *thread_data_1, *thread_data_2, *thread_data_3, *thread_data_4;
|
||||
GstEvent *event;
|
||||
|
||||
mpegtsmux = gst_check_setup_element ("mpegtsmux");
|
||||
|
||||
link_src (mpegtsmux, &sink, &test_data);
|
||||
link_sinks (mpegtsmux, &src1, &src2, &src3, &test_data);
|
||||
gst_element_set_state (mpegtsmux, GST_STATE_PLAYING);
|
||||
setup_caps (mpegtsmux, src1, src2, src3);
|
||||
|
||||
/* send an upstream force-key-unit event with running_time=2s */
|
||||
timestamp = stream_time = running_time = 2 * GST_SECOND;
|
||||
event =
|
||||
gst_video_event_new_upstream_force_key_unit (running_time, TRUE, count);
|
||||
fail_unless (gst_pad_push_event (sink, event));
|
||||
|
||||
fail_unless (test_data.sink_event == NULL);
|
||||
fail_unless_equals_int (test_data.src_events, 3);
|
||||
|
||||
/* send downstream events with unrelated seqnums */
|
||||
event = gst_video_event_new_downstream_force_key_unit (timestamp,
|
||||
stream_time, running_time, all_headers, count);
|
||||
fail_unless (gst_pad_push_event (src1, event));
|
||||
event = gst_video_event_new_downstream_force_key_unit (timestamp,
|
||||
stream_time, running_time, all_headers, count);
|
||||
fail_unless (gst_pad_push_event (src2, event));
|
||||
|
||||
/* events should be skipped */
|
||||
fail_unless (test_data.sink_event == NULL);
|
||||
|
||||
/* push 4 buffers, make sure mpegtsmux handles the force-key-unit event when
|
||||
* the buffer with the requested running time is collected */
|
||||
thread_data_1 = pad_push (src1, gst_buffer_new (), 1 * GST_SECOND);
|
||||
thread_data_2 = pad_push (src2, gst_buffer_new (), 2 * GST_SECOND);
|
||||
thread_data_3 = pad_push (src3, gst_buffer_new (), 3 * GST_SECOND);
|
||||
|
||||
g_thread_join (thread_data_1->thread);
|
||||
fail_unless (test_data.sink_event == NULL);
|
||||
|
||||
/* push again on src1 so that the buffer on src2 is collected */
|
||||
thread_data_4 = pad_push (src1, gst_buffer_new (), 4 * GST_SECOND);
|
||||
|
||||
g_thread_join (thread_data_2->thread);
|
||||
fail_unless (test_data.sink_event != NULL);
|
||||
|
||||
gst_element_set_state (mpegtsmux, GST_STATE_NULL);
|
||||
|
||||
g_thread_join (thread_data_3->thread);
|
||||
g_thread_join (thread_data_4->thread);
|
||||
|
||||
g_free (thread_data_1);
|
||||
g_free (thread_data_2);
|
||||
g_free (thread_data_3);
|
||||
g_free (thread_data_4);
|
||||
|
||||
gst_object_unref (src1);
|
||||
gst_object_unref (src2);
|
||||
gst_object_unref (src3);
|
||||
gst_object_unref (sink);
|
||||
gst_object_unref (mpegtsmux);
|
||||
}
|
||||
|
||||
GST_END_TEST;
|
||||
|
||||
static GstFlowReturn expected_flow;
|
||||
|
||||
static GstFlowReturn
|
||||
flow_test_stat_chain_func (GstPad * pad, GstObject * parent, GstBuffer * buffer)
|
||||
{
|
||||
gst_buffer_unref (buffer);
|
||||
|
||||
GST_INFO ("returning flow %s (%d)", gst_flow_get_name (expected_flow),
|
||||
expected_flow);
|
||||
return expected_flow;
|
||||
}
|
||||
|
||||
GST_START_TEST (test_propagate_flow_status)
|
||||
{
|
||||
GstElement *mux;
|
||||
gchar *padname;
|
||||
GstBuffer *inbuffer;
|
||||
GstCaps *caps;
|
||||
guint i;
|
||||
|
||||
GstFlowReturn expected[] = { GST_FLOW_OK, GST_FLOW_FLUSHING, GST_FLOW_EOS,
|
||||
GST_FLOW_NOT_NEGOTIATED, GST_FLOW_ERROR, GST_FLOW_NOT_SUPPORTED
|
||||
};
|
||||
|
||||
mux = setup_tsmux (&video_src_template, "sink_%d", &padname);
|
||||
gst_pad_set_chain_function (mysinkpad, flow_test_stat_chain_func);
|
||||
|
||||
fail_unless (gst_element_set_state (mux,
|
||||
GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS,
|
||||
"could not set to playing");
|
||||
|
||||
caps = gst_caps_from_string (VIDEO_CAPS_STRING);
|
||||
gst_check_setup_events (mysrcpad, mux, caps, GST_FORMAT_TIME);
|
||||
gst_caps_unref (caps);
|
||||
|
||||
for (i = 0; i < G_N_ELEMENTS (expected); ++i) {
|
||||
GstFlowReturn res;
|
||||
|
||||
inbuffer = gst_buffer_new_and_alloc (1);
|
||||
ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1);
|
||||
|
||||
expected_flow = expected[i];
|
||||
GST_INFO ("expecting flow %s (%d)", gst_flow_get_name (expected_flow),
|
||||
expected_flow);
|
||||
|
||||
GST_BUFFER_TIMESTAMP (inbuffer) = i * GST_SECOND;
|
||||
|
||||
res = gst_pad_push (mysrcpad, inbuffer);
|
||||
|
||||
fail_unless_equals_int (res, expected[i]);
|
||||
}
|
||||
|
||||
cleanup_tsmux (mux, padname);
|
||||
g_free (padname);
|
||||
}
|
||||
|
||||
GST_END_TEST;
|
||||
|
||||
GST_START_TEST (test_multiple_state_change)
|
||||
{
|
||||
GstElement *mux;
|
||||
|
@ -716,7 +388,6 @@ GST_START_TEST (test_multiple_state_change)
|
|||
size_t num_transitions_to_test = 10;
|
||||
|
||||
mux = setup_tsmux (&video_src_template, "sink_%d", &padname);
|
||||
gst_pad_set_chain_function (mysinkpad, flow_test_stat_chain_func);
|
||||
gst_segment_init (&segment, GST_FORMAT_TIME);
|
||||
|
||||
caps = gst_caps_from_string (VIDEO_CAPS_STRING);
|
||||
|
@ -724,6 +395,7 @@ GST_START_TEST (test_multiple_state_change)
|
|||
gst_caps_unref (caps);
|
||||
|
||||
for (i = 0; i < num_transitions_to_test; ++i) {
|
||||
GstQuery *drain;
|
||||
GstState next_state = states[i % G_N_ELEMENTS (states)];
|
||||
fail_unless (gst_element_set_state (mux,
|
||||
next_state) == GST_STATE_CHANGE_SUCCESS,
|
||||
|
@ -739,9 +411,12 @@ GST_START_TEST (test_multiple_state_change)
|
|||
inbuffer = gst_buffer_new_and_alloc (1);
|
||||
ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1);
|
||||
|
||||
expected_flow = GST_FLOW_OK;
|
||||
GST_BUFFER_PTS (inbuffer) = 0;
|
||||
fail_unless (GST_FLOW_OK == gst_pad_push (mysrcpad, inbuffer));
|
||||
|
||||
drain = gst_query_new_drain ();
|
||||
gst_pad_peer_query (mysrcpad, drain);
|
||||
gst_query_unref (drain);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -813,9 +488,6 @@ mpegtsmux_suite (void)
|
|||
|
||||
tcase_add_test (tc_chain, test_audio);
|
||||
tcase_add_test (tc_chain, test_video);
|
||||
tcase_add_test (tc_chain, test_force_key_unit_event_downstream);
|
||||
tcase_add_test (tc_chain, test_force_key_unit_event_upstream);
|
||||
tcase_add_test (tc_chain, test_propagate_flow_status);
|
||||
tcase_add_test (tc_chain, test_multiple_state_change);
|
||||
tcase_add_test (tc_chain, test_align);
|
||||
tcase_add_test (tc_chain, test_keyframe_flag_propagation);
|
||||
|
|
Loading…
Reference in a new issue