mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-11-10 11:29:55 +00:00
309368bac3
Make the unit tests handle the fact that pads don't appear immediately. Before, the test assumed pads are exposed before the internal source element is created, which is no longer true.
634 lines
21 KiB
C
634 lines
21 KiB
C
/* A generic test engine for elements based upon GstAdaptiveDemux
|
|
*
|
|
* Copyright (c) <2015> YouView TV 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.
|
|
*/
|
|
|
|
#include <gst/check/gstcheck.h>
|
|
#include <gst/check/gsttestclock.h>
|
|
#include "adaptive_demux_engine.h"
|
|
|
|
typedef struct _GstAdaptiveDemuxTestEnginePrivate
|
|
{
|
|
GstAdaptiveDemuxTestEngine engine;
|
|
const GstAdaptiveDemuxTestCallbacks *callbacks;
|
|
gpointer user_data;
|
|
guint clock_update_id;
|
|
} GstAdaptiveDemuxTestEnginePrivate;
|
|
|
|
|
|
#define GST_TEST_GET_LOCK(d) (&(((GstAdaptiveDemuxTestEnginePrivate*)(d))->engine.lock))
|
|
#define GST_TEST_LOCK(d) g_mutex_lock (GST_TEST_GET_LOCK (d))
|
|
#define GST_TEST_UNLOCK(d) g_mutex_unlock (GST_TEST_GET_LOCK (d))
|
|
|
|
static void
|
|
adaptive_demux_engine_stream_state_finalize (gpointer data)
|
|
{
|
|
GstAdaptiveDemuxTestOutputStream *stream =
|
|
(GstAdaptiveDemuxTestOutputStream *) data;
|
|
g_free (stream->name);
|
|
if (stream->appsink)
|
|
gst_object_unref (stream->appsink);
|
|
if (stream->pad)
|
|
gst_object_unref (stream->pad);
|
|
if (stream->internal_pad)
|
|
gst_object_unref (stream->internal_pad);
|
|
g_slice_free (GstAdaptiveDemuxTestOutputStream, stream);
|
|
}
|
|
|
|
/* get the testOutput entry in testData corresponding to the given AppSink */
|
|
static GstAdaptiveDemuxTestOutputStream *
|
|
getTestOutputDataByAppsink (GstAdaptiveDemuxTestEnginePrivate * priv,
|
|
GstAppSink * appsink)
|
|
{
|
|
guint i;
|
|
|
|
for (i = 0; i < priv->engine.output_streams->len; ++i) {
|
|
GstAdaptiveDemuxTestOutputStream *state;
|
|
state = g_ptr_array_index (priv->engine.output_streams, i);
|
|
if (state->appsink == appsink) {
|
|
return state;
|
|
}
|
|
}
|
|
ck_abort_msg ("cannot find appsink %p in the output data", appsink);
|
|
return NULL;
|
|
}
|
|
|
|
/* get the output stream entry in corresponding to the given Pad */
|
|
static GstAdaptiveDemuxTestOutputStream *
|
|
getTestOutputDataByPad (GstAdaptiveDemuxTestEnginePrivate * priv,
|
|
GstPad * pad, gboolean abort_if_not_found)
|
|
{
|
|
guint i;
|
|
|
|
for (i = 0; i < priv->engine.output_streams->len; ++i) {
|
|
GstAdaptiveDemuxTestOutputStream *stream;
|
|
stream = g_ptr_array_index (priv->engine.output_streams, i);
|
|
if (stream->internal_pad == pad || stream->pad == pad) {
|
|
return stream;
|
|
}
|
|
}
|
|
if (abort_if_not_found)
|
|
ck_abort_msg ("cannot find pad %p in the output data", pad);
|
|
return NULL;
|
|
}
|
|
|
|
/* get the output stream entry in corresponding to the given Pad */
|
|
static GstAdaptiveDemuxTestOutputStream *
|
|
getTestOutputDataByName (GstAdaptiveDemuxTestEnginePrivate * priv,
|
|
const gchar * name, gboolean abort_if_not_found)
|
|
{
|
|
guint i;
|
|
|
|
for (i = 0; i < priv->engine.output_streams->len; ++i) {
|
|
GstAdaptiveDemuxTestOutputStream *stream;
|
|
stream = g_ptr_array_index (priv->engine.output_streams, i);
|
|
if (strstr (stream->name, name) != NULL) {
|
|
return stream;
|
|
}
|
|
}
|
|
if (abort_if_not_found)
|
|
ck_abort_msg ("cannot find pad %s in the output data", name);
|
|
return NULL;
|
|
}
|
|
|
|
/* callback called when AppSink receives data */
|
|
|
|
/* callback called when AppSink receives data */
|
|
static GstFlowReturn
|
|
on_appSinkNewSample (GstAppSink * appsink, gpointer user_data)
|
|
{
|
|
GstAdaptiveDemuxTestEnginePrivate *priv =
|
|
(GstAdaptiveDemuxTestEnginePrivate *) user_data;
|
|
GstAdaptiveDemuxTestEngine *engine;
|
|
GstAdaptiveDemuxTestOutputStream *testOutputStream = NULL;
|
|
GstSample *sample;
|
|
GstBuffer *buffer;
|
|
gboolean ret = TRUE;
|
|
|
|
fail_unless (priv != NULL);
|
|
GST_TEST_LOCK (priv);
|
|
engine = &priv->engine;
|
|
testOutputStream = getTestOutputDataByAppsink (priv, appsink);
|
|
|
|
sample = gst_app_sink_pull_sample (appsink);
|
|
fail_unless (sample != NULL);
|
|
buffer = gst_sample_get_buffer (sample);
|
|
fail_unless (buffer != NULL);
|
|
|
|
/* call the test callback, if registered */
|
|
if (priv->callbacks->appsink_received_data)
|
|
ret = priv->callbacks->appsink_received_data (engine,
|
|
testOutputStream, buffer, priv->user_data);
|
|
|
|
testOutputStream->segment_received_size += gst_buffer_get_size (buffer);
|
|
|
|
gst_sample_unref (sample);
|
|
|
|
GST_TEST_UNLOCK (priv);
|
|
|
|
if (!ret)
|
|
return GST_FLOW_EOS;
|
|
|
|
return GST_FLOW_OK;
|
|
}
|
|
|
|
/* callback called when AppSink receives eos */
|
|
static void
|
|
on_appSinkEOS (GstAppSink * appsink, gpointer user_data)
|
|
{
|
|
GstAdaptiveDemuxTestEnginePrivate *priv =
|
|
(GstAdaptiveDemuxTestEnginePrivate *) user_data;
|
|
GstAdaptiveDemuxTestOutputStream *testOutputStream = NULL;
|
|
|
|
fail_unless (priv != NULL);
|
|
GST_TEST_LOCK (priv);
|
|
testOutputStream = getTestOutputDataByAppsink (priv, appsink);
|
|
|
|
testOutputStream->total_received_size +=
|
|
testOutputStream->segment_received_size;
|
|
testOutputStream->segment_received_size = 0;
|
|
|
|
if (priv->callbacks->appsink_eos)
|
|
priv->callbacks->appsink_eos (&priv->engine,
|
|
testOutputStream, priv->user_data);
|
|
|
|
GST_TEST_UNLOCK (priv);
|
|
}
|
|
|
|
static GstPadProbeReturn
|
|
on_appsink_event (GstPad * pad, GstPadProbeInfo * info, gpointer data)
|
|
{
|
|
GstAdaptiveDemuxTestEnginePrivate *priv =
|
|
(GstAdaptiveDemuxTestEnginePrivate *) data;
|
|
GstAdaptiveDemuxTestOutputStream *stream = NULL;
|
|
GstEvent *event;
|
|
|
|
event = GST_PAD_PROBE_INFO_EVENT (info);
|
|
GST_DEBUG ("Received event %" GST_PTR_FORMAT " on pad %" GST_PTR_FORMAT,
|
|
event, pad);
|
|
|
|
if (priv->callbacks->appsink_event) {
|
|
GstPad *stream_pad = gst_pad_get_peer (pad);
|
|
fail_unless (stream_pad != NULL);
|
|
|
|
GST_TEST_LOCK (priv);
|
|
stream = getTestOutputDataByPad (priv, stream_pad, TRUE);
|
|
GST_TEST_UNLOCK (priv);
|
|
gst_object_unref (stream_pad);
|
|
priv->callbacks->appsink_event (&priv->engine, stream, event,
|
|
priv->user_data);
|
|
}
|
|
|
|
return GST_PAD_PROBE_OK;
|
|
}
|
|
|
|
|
|
/* callback called when demux sends data to AppSink */
|
|
static GstPadProbeReturn
|
|
on_demux_sent_data (GstPad * pad, GstPadProbeInfo * info, gpointer data)
|
|
{
|
|
GstAdaptiveDemuxTestEnginePrivate *priv =
|
|
(GstAdaptiveDemuxTestEnginePrivate *) data;
|
|
GstAdaptiveDemuxTestOutputStream *stream = NULL;
|
|
GstBuffer *buffer;
|
|
|
|
buffer = GST_PAD_PROBE_INFO_BUFFER (info);
|
|
|
|
GST_TEST_LOCK (priv);
|
|
stream = getTestOutputDataByPad (priv, pad, TRUE);
|
|
GST_TEST_UNLOCK (priv);
|
|
|
|
if (priv->callbacks->demux_sent_data) {
|
|
(*priv->callbacks->demux_sent_data) (&priv->engine,
|
|
stream, buffer, priv->user_data);
|
|
}
|
|
|
|
return GST_PAD_PROBE_OK;
|
|
}
|
|
|
|
/* callback called when dash sends event to AppSink */
|
|
static GstPadProbeReturn
|
|
on_demux_sent_event (GstPad * pad, GstPadProbeInfo * info, gpointer data)
|
|
{
|
|
GstAdaptiveDemuxTestEnginePrivate *priv =
|
|
(GstAdaptiveDemuxTestEnginePrivate *) data;
|
|
GstAdaptiveDemuxTestOutputStream *stream = NULL;
|
|
GstEvent *event;
|
|
|
|
event = GST_PAD_PROBE_INFO_EVENT (info);
|
|
|
|
GST_TEST_LOCK (&priv->engine);
|
|
|
|
if (priv->callbacks->demux_sent_event) {
|
|
stream = getTestOutputDataByPad (priv, pad, TRUE);
|
|
(*priv->callbacks->demux_sent_event) (&priv->engine,
|
|
stream, event, priv->user_data);
|
|
}
|
|
|
|
GST_TEST_UNLOCK (&priv->engine);
|
|
return GST_PAD_PROBE_OK;
|
|
}
|
|
|
|
/* callback called when demux receives events from GstFakeSoupHTTPSrc */
|
|
static GstPadProbeReturn
|
|
on_demuxReceivesEvent (GstPad * pad, GstPadProbeInfo * info, gpointer data)
|
|
{
|
|
GstAdaptiveDemuxTestEnginePrivate *priv =
|
|
(GstAdaptiveDemuxTestEnginePrivate *) data;
|
|
GstAdaptiveDemuxTestOutputStream *stream = NULL;
|
|
GstEvent *event;
|
|
const GstSegment *segment;
|
|
|
|
event = GST_PAD_PROBE_INFO_EVENT (info);
|
|
GST_DEBUG ("Received event %" GST_PTR_FORMAT " on pad %" GST_PTR_FORMAT,
|
|
event, pad);
|
|
|
|
if (GST_EVENT_TYPE (event) == GST_EVENT_SEGMENT) {
|
|
/* a new segment will start arriving
|
|
* update segment_start used by pattern validation
|
|
*/
|
|
gst_event_parse_segment (event, &segment);
|
|
|
|
GST_TEST_LOCK (priv);
|
|
stream = getTestOutputDataByPad (priv, pad, TRUE);
|
|
stream->total_received_size += stream->segment_received_size;
|
|
stream->segment_received_size = 0;
|
|
stream->segment_start = segment->start;
|
|
GST_TEST_UNLOCK (priv);
|
|
}
|
|
|
|
return GST_PAD_PROBE_OK;
|
|
}
|
|
|
|
|
|
static void
|
|
on_demuxElementAdded (GstBin * demux, GstElement * element, gpointer user_data)
|
|
{
|
|
GstAdaptiveDemuxTestEnginePrivate *priv =
|
|
(GstAdaptiveDemuxTestEnginePrivate *) user_data;
|
|
GstAdaptiveDemuxTestOutputStream *stream = NULL;
|
|
GstPad *internal_pad;
|
|
gchar *srcbin_name;
|
|
|
|
srcbin_name = GST_ELEMENT_NAME (element);
|
|
GST_TEST_LOCK (priv);
|
|
|
|
stream = getTestOutputDataByName (priv, srcbin_name, FALSE);
|
|
if (stream == NULL) {
|
|
/* Pad wasn't exposed yet, create the stream */
|
|
stream = g_slice_new0 (GstAdaptiveDemuxTestOutputStream);
|
|
stream->name = g_strdup (srcbin_name);
|
|
g_ptr_array_add (priv->engine.output_streams, stream);
|
|
}
|
|
|
|
/* keep the reference to the internal_pad.
|
|
* We will need it to identify the stream in the on_demuxReceivesEvent callback
|
|
*/
|
|
if (stream->internal_pad) {
|
|
gst_pad_remove_probe (stream->internal_pad, stream->internal_pad_probe);
|
|
gst_object_unref (stream->internal_pad);
|
|
}
|
|
internal_pad = gst_element_get_static_pad (element, "src");
|
|
stream->internal_pad_probe =
|
|
gst_pad_add_probe (internal_pad, GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM,
|
|
(GstPadProbeCallback) on_demuxReceivesEvent, priv, NULL);
|
|
stream->internal_pad = internal_pad;
|
|
GST_TEST_UNLOCK (priv);
|
|
|
|
}
|
|
|
|
|
|
/* callback called when demux creates a src pad.
|
|
* We will create an AppSink to get the data
|
|
*/
|
|
static void
|
|
on_demuxNewPad (GstElement * demux, GstPad * pad, gpointer user_data)
|
|
{
|
|
GstAdaptiveDemuxTestEnginePrivate *priv =
|
|
(GstAdaptiveDemuxTestEnginePrivate *) user_data;
|
|
GstElement *pipeline;
|
|
GstElement *sink;
|
|
gboolean ret;
|
|
gchar *name;
|
|
GstPad *appsink_pad;
|
|
GstAppSinkCallbacks appSinkCallbacks;
|
|
GstAdaptiveDemuxTestOutputStream *stream;
|
|
GObjectClass *gobject_class;
|
|
|
|
fail_unless (priv != NULL);
|
|
name = gst_pad_get_name (pad);
|
|
|
|
GST_DEBUG ("demux created pad %p", pad);
|
|
|
|
stream = getTestOutputDataByName (priv, name, FALSE);
|
|
if (stream == NULL) {
|
|
stream = g_slice_new0 (GstAdaptiveDemuxTestOutputStream);
|
|
stream->name = g_strdup (name);
|
|
g_ptr_array_add (priv->engine.output_streams, stream);
|
|
}
|
|
|
|
sink = gst_element_factory_make ("appsink", name);
|
|
g_free (name);
|
|
fail_unless (sink != NULL);
|
|
|
|
GST_TEST_LOCK (priv);
|
|
|
|
/* register the AppSink pointer in the test output data */
|
|
gst_object_ref (sink);
|
|
stream->appsink = GST_APP_SINK (sink);
|
|
|
|
appSinkCallbacks.eos = on_appSinkEOS;
|
|
appSinkCallbacks.new_preroll = NULL;
|
|
appSinkCallbacks.new_sample = on_appSinkNewSample;
|
|
|
|
gst_app_sink_set_callbacks (GST_APP_SINK (sink), &appSinkCallbacks, priv,
|
|
NULL);
|
|
appsink_pad = gst_element_get_static_pad (sink, "sink");
|
|
gst_pad_add_probe (appsink_pad,
|
|
GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM | GST_PAD_PROBE_TYPE_EVENT_FLUSH,
|
|
(GstPadProbeCallback) on_appsink_event, priv, NULL);
|
|
gst_object_unref (appsink_pad);
|
|
|
|
gst_pad_add_probe (pad, GST_PAD_PROBE_TYPE_BUFFER,
|
|
(GstPadProbeCallback) on_demux_sent_data, priv, NULL);
|
|
gst_pad_add_probe (pad, GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM |
|
|
GST_PAD_PROBE_TYPE_EVENT_FLUSH,
|
|
(GstPadProbeCallback) on_demux_sent_event, priv, NULL);
|
|
gobject_class = G_OBJECT_GET_CLASS (sink);
|
|
if (g_object_class_find_property (gobject_class, "sync")) {
|
|
GST_DEBUG ("Setting sync=FALSE on AppSink");
|
|
g_object_set (G_OBJECT (sink), "sync", FALSE, NULL);
|
|
}
|
|
stream->pad = gst_object_ref (pad);
|
|
|
|
GST_TEST_UNLOCK (priv);
|
|
|
|
pipeline = GST_ELEMENT (gst_element_get_parent (demux));
|
|
fail_unless (pipeline != NULL);
|
|
ret = gst_bin_add (GST_BIN (pipeline), sink);
|
|
fail_unless_equals_int (ret, TRUE);
|
|
gst_object_unref (pipeline);
|
|
ret = gst_element_link (demux, sink);
|
|
fail_unless_equals_int (ret, TRUE);
|
|
ret = gst_element_sync_state_with_parent (sink);
|
|
fail_unless_equals_int (ret, TRUE);
|
|
GST_TEST_LOCK (priv);
|
|
if (priv->callbacks->demux_pad_added) {
|
|
priv->callbacks->demux_pad_added (&priv->engine, stream, priv->user_data);
|
|
}
|
|
GST_TEST_UNLOCK (priv);
|
|
}
|
|
|
|
/* callback called when demux removes a src pad.
|
|
* We remove the AppSink associated with this pad
|
|
*/
|
|
static void
|
|
on_demuxPadRemoved (GstElement * demux, GstPad * pad, gpointer user_data)
|
|
{
|
|
GstAdaptiveDemuxTestEnginePrivate *priv =
|
|
(GstAdaptiveDemuxTestEnginePrivate *) user_data;
|
|
GstAdaptiveDemuxTestOutputStream *stream = NULL;
|
|
GstStateChangeReturn ret;
|
|
GstState currentState, pending;
|
|
GstElement *appSink;
|
|
|
|
fail_unless (priv != NULL);
|
|
|
|
GST_DEBUG ("Pad removed: %" GST_PTR_FORMAT, pad);
|
|
|
|
GST_TEST_LOCK (priv);
|
|
stream = getTestOutputDataByPad (priv, pad, TRUE);
|
|
if (priv->callbacks->demux_pad_removed) {
|
|
priv->callbacks->demux_pad_removed (&priv->engine, stream, priv->user_data);
|
|
}
|
|
fail_unless (stream->appsink != NULL);
|
|
fail_unless (stream->internal_pad != NULL);
|
|
gst_object_unref (stream->internal_pad);
|
|
stream->internal_pad = NULL;
|
|
appSink = GST_ELEMENT (stream->appsink);
|
|
ret = gst_element_get_state (appSink, ¤tState, &pending, 0);
|
|
if ((ret == GST_STATE_CHANGE_SUCCESS && currentState == GST_STATE_PLAYING)
|
|
|| (ret == GST_STATE_CHANGE_ASYNC && pending == GST_STATE_PLAYING)) {
|
|
GST_DEBUG ("Changing AppSink element to PAUSED");
|
|
gst_element_set_state (appSink, GST_STATE_PAUSED);
|
|
}
|
|
GST_TEST_UNLOCK (priv);
|
|
}
|
|
|
|
/* callback called when main_loop detects an error message
|
|
* We will signal main loop to quit
|
|
*/
|
|
static void
|
|
on_ErrorMessageOnBus (GstBus * bus, GstMessage * msg, gpointer user_data)
|
|
{
|
|
GstAdaptiveDemuxTestEnginePrivate *priv =
|
|
(GstAdaptiveDemuxTestEnginePrivate *) user_data;
|
|
GError *err = NULL;
|
|
gchar *dbg_info = NULL;
|
|
|
|
gst_message_parse_error (msg, &err, &dbg_info);
|
|
GST_DEBUG ("ERROR from element %s: '%s'. Debugging info: %s",
|
|
GST_OBJECT_NAME (msg->src), err->message, (dbg_info) ? dbg_info : "none");
|
|
g_error_free (err);
|
|
g_free (dbg_info);
|
|
|
|
GST_TEST_LOCK (priv);
|
|
|
|
fail_unless (priv->callbacks->bus_error_message,
|
|
"unexpected error detected on bus");
|
|
|
|
priv->callbacks->bus_error_message (&priv->engine, msg, priv->user_data);
|
|
|
|
GST_TEST_UNLOCK (priv);
|
|
}
|
|
|
|
static gboolean
|
|
gst_adaptive_demux_update_test_clock (gpointer user_data)
|
|
{
|
|
GstAdaptiveDemuxTestEnginePrivate *priv =
|
|
(GstAdaptiveDemuxTestEnginePrivate *) user_data;
|
|
GstClockID id;
|
|
GstClockTime next_entry;
|
|
GstTestClock *clock = GST_TEST_CLOCK (priv->engine.clock);
|
|
|
|
fail_unless (clock != NULL);
|
|
next_entry = gst_test_clock_get_next_entry_time (clock);
|
|
if (next_entry != GST_CLOCK_TIME_NONE) {
|
|
/* tests that do not want the manifest to update will set the update period
|
|
* to a big value, eg 500s. The manifest update task will register an alarm
|
|
* for that value.
|
|
* We do not want the clock to jump to that. If it does, the manifest update
|
|
* task will keep scheduling and use all the cpu power, starving the other
|
|
* threads.
|
|
* Usually the test require the clock to update with approx 3s, so we will
|
|
* allow only updates smaller than 100s
|
|
*/
|
|
GstClockTime curr_time = gst_clock_get_time (GST_CLOCK (clock));
|
|
if (next_entry - curr_time < 100 * GST_SECOND) {
|
|
gst_test_clock_set_time (clock, next_entry);
|
|
id = gst_test_clock_process_next_clock_id (clock);
|
|
fail_unless (id != NULL);
|
|
gst_clock_id_unref (id);
|
|
}
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
start_pipeline_playing (gpointer user_data)
|
|
{
|
|
GstAdaptiveDemuxTestEnginePrivate *priv =
|
|
(GstAdaptiveDemuxTestEnginePrivate *) user_data;
|
|
GstStateChangeReturn stateChange;
|
|
|
|
GST_DEBUG ("Moving pipeline to PLAYING state");
|
|
stateChange =
|
|
gst_element_set_state (priv->engine.pipeline, GST_STATE_PLAYING);
|
|
fail_unless (stateChange != GST_STATE_CHANGE_FAILURE);
|
|
GST_DEBUG ("PLAYING stateChange = %d", stateChange);
|
|
return FALSE;
|
|
}
|
|
|
|
/*
|
|
* Create a demux element, run a test using the input data and check
|
|
* the output data
|
|
*/
|
|
void
|
|
gst_adaptive_demux_test_run (const gchar * element_name,
|
|
const gchar * manifest_uri,
|
|
const GstAdaptiveDemuxTestCallbacks * callbacks, gpointer user_data)
|
|
{
|
|
GstBus *bus;
|
|
GstElement *demux;
|
|
GstElement *manifest_source;
|
|
gboolean ret;
|
|
GstStateChangeReturn stateChange;
|
|
GstAdaptiveDemuxTestEnginePrivate *priv;
|
|
|
|
priv = g_slice_new0 (GstAdaptiveDemuxTestEnginePrivate);
|
|
priv->engine.output_streams =
|
|
g_ptr_array_new_with_free_func
|
|
(adaptive_demux_engine_stream_state_finalize);
|
|
g_mutex_init (&priv->engine.lock);
|
|
priv->callbacks = callbacks;
|
|
priv->user_data = user_data;
|
|
priv->engine.loop = g_main_loop_new (NULL, TRUE);
|
|
fail_unless (priv->engine.loop != NULL);
|
|
GST_TEST_LOCK (priv);
|
|
priv->engine.pipeline = gst_pipeline_new ("pipeline");
|
|
fail_unless (priv->engine.pipeline != NULL);
|
|
GST_DEBUG ("created pipeline %" GST_PTR_FORMAT, priv->engine.pipeline);
|
|
|
|
/* register a callback to listen for error messages */
|
|
bus = gst_pipeline_get_bus (GST_PIPELINE (priv->engine.pipeline));
|
|
gst_bus_add_signal_watch_full (bus, G_PRIORITY_HIGH);
|
|
g_signal_connect (bus, "message::error",
|
|
G_CALLBACK (on_ErrorMessageOnBus), priv);
|
|
|
|
manifest_source =
|
|
gst_element_make_from_uri (GST_URI_SRC, manifest_uri, NULL, NULL);
|
|
fail_unless (manifest_source != NULL);
|
|
priv->engine.manifest_source = manifest_source;
|
|
|
|
demux = gst_check_setup_element (element_name);
|
|
fail_unless (demux != NULL);
|
|
priv->engine.demux = demux;
|
|
GST_DEBUG ("created demux %" GST_PTR_FORMAT, demux);
|
|
|
|
g_signal_connect (demux, "element-added", G_CALLBACK (on_demuxElementAdded),
|
|
priv);
|
|
g_signal_connect (demux, "pad-added", G_CALLBACK (on_demuxNewPad), priv);
|
|
g_signal_connect (demux, "pad-removed",
|
|
G_CALLBACK (on_demuxPadRemoved), priv);
|
|
|
|
gst_bin_add_many (GST_BIN (priv->engine.pipeline), manifest_source, demux,
|
|
NULL);
|
|
ASSERT_OBJECT_REFCOUNT (manifest_source, element_name, 1);
|
|
ASSERT_OBJECT_REFCOUNT (demux, element_name, 1);
|
|
|
|
ret = gst_element_link (manifest_source, demux);
|
|
fail_unless_equals_int (ret, TRUE);
|
|
|
|
priv->engine.clock = gst_system_clock_obtain ();
|
|
if (GST_IS_TEST_CLOCK (priv->engine.clock)) {
|
|
/*
|
|
* live tests will want to manipulate the clock, so they will register a
|
|
* gst_test_clock as the system clock.
|
|
* The on demand tests do not care about the clock, so they will let the
|
|
* system clock to the default one.
|
|
* If a gst_test_clock was installed as system clock, we register a
|
|
* periodic callback to update its value.
|
|
*/
|
|
priv->clock_update_id =
|
|
g_timeout_add (100, gst_adaptive_demux_update_test_clock, priv);
|
|
}
|
|
|
|
/* call a test callback before we start the pipeline */
|
|
if (callbacks->pre_test)
|
|
(*callbacks->pre_test) (&priv->engine, priv->user_data);
|
|
|
|
GST_TEST_UNLOCK (priv);
|
|
|
|
GST_DEBUG ("Starting pipeline");
|
|
stateChange = gst_element_set_state (priv->engine.pipeline, GST_STATE_PAUSED);
|
|
fail_unless (stateChange != GST_STATE_CHANGE_FAILURE);
|
|
/* wait for completion of the move to PAUSED */
|
|
stateChange = gst_element_get_state (priv->engine.pipeline, NULL, NULL,
|
|
GST_CLOCK_TIME_NONE);
|
|
fail_unless (stateChange != GST_STATE_CHANGE_FAILURE);
|
|
|
|
g_idle_add ((GSourceFunc) start_pipeline_playing, priv);
|
|
|
|
/* block until a callback calls g_main_loop_quit (engine.loop) */
|
|
GST_DEBUG ("main thread waiting for streams to finish");
|
|
g_main_loop_run (priv->engine.loop);
|
|
GST_DEBUG ("main thread finished. Stopping pipeline");
|
|
|
|
/* no need to use gst_element_get_state as the move the GST_STATE_NULL
|
|
is always synchronous */
|
|
stateChange = gst_element_set_state (priv->engine.pipeline, GST_STATE_NULL);
|
|
fail_unless (stateChange != GST_STATE_CHANGE_FAILURE);
|
|
|
|
GST_TEST_LOCK (priv);
|
|
|
|
/* call a test callback after the stop of the pipeline */
|
|
if (callbacks->post_test)
|
|
(*callbacks->post_test) (&priv->engine, priv->user_data);
|
|
|
|
g_signal_handlers_disconnect_by_func (bus,
|
|
G_CALLBACK (on_ErrorMessageOnBus), priv);
|
|
gst_bus_remove_signal_watch (bus);
|
|
g_signal_handlers_disconnect_by_func (demux, G_CALLBACK (on_demuxNewPad),
|
|
priv);
|
|
g_signal_handlers_disconnect_by_func (demux, G_CALLBACK (on_demuxPadRemoved),
|
|
priv);
|
|
|
|
GST_DEBUG ("main thread pipeline stopped");
|
|
if (priv->clock_update_id != 0)
|
|
g_source_remove (priv->clock_update_id);
|
|
gst_object_unref (priv->engine.clock);
|
|
gst_object_unref (priv->engine.pipeline);
|
|
priv->engine.pipeline = NULL;
|
|
g_main_loop_unref (priv->engine.loop);
|
|
g_ptr_array_unref (priv->engine.output_streams);
|
|
gst_object_unref (bus);
|
|
|
|
GST_TEST_UNLOCK (priv);
|
|
g_mutex_clear (&priv->engine.lock);
|
|
g_slice_free (GstAdaptiveDemuxTestEnginePrivate, priv);
|
|
}
|