mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-12-30 12:10:37 +00:00
be1f48de3a
Original commit message from CVS: * docs/design/part-element-sink.txt: Updated document. * libs/gst/base/gstbasesink.c: (gst_base_sink_init), (gst_base_sink_finalize), (gst_base_sink_preroll_queue_flush), (gst_base_sink_configure_segment), (gst_base_sink_commit_state), (gst_base_sink_get_sync_times), (gst_base_sink_wait_clock), (gst_base_sink_do_sync), (gst_base_sink_render_object), (gst_base_sink_preroll_object), (gst_base_sink_queue_object_unlocked), (gst_base_sink_queue_object), (gst_base_sink_event), (gst_base_sink_chain_unlocked), (gst_base_sink_chain), (gst_base_sink_loop), (gst_base_sink_activate_pull), (gst_base_sink_get_position), (gst_base_sink_change_state): * libs/gst/base/gstbasesink.h: Totally refactored matching the design doc. Use two segments, one to clip incomming buffers and another to perform sync. Handle queueing correctly, bypass the queue when playing. Make EOS cancelable. Handle errors correctly when operating in pull based mode. * tests/check/elements/fakesink.c: (GST_START_TEST), (fakesink_suite): Added new check for sinks.
323 lines
8.5 KiB
C
323 lines
8.5 KiB
C
/* GStreamer
|
|
*
|
|
* unit test for fakesink
|
|
*
|
|
* Copyright (C) <2005> Thomas Vander Stichele <thomas at apestaart dot org>
|
|
*
|
|
* 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., 59 Temple Place - Suite 330,
|
|
* Boston, MA 02111-1307, USA.
|
|
*/
|
|
|
|
#include <unistd.h>
|
|
|
|
#include <gst/check/gstcheck.h>
|
|
|
|
typedef struct
|
|
{
|
|
GstPad *pad;
|
|
GstBuffer *buffer;
|
|
GThread *thread;
|
|
GstFlowReturn ret;
|
|
} ChainData;
|
|
|
|
static gpointer
|
|
chain_async_buffer (gpointer data)
|
|
{
|
|
ChainData *chain_data = (ChainData *) data;
|
|
|
|
chain_data->ret = gst_pad_chain (chain_data->pad, chain_data->buffer);
|
|
|
|
return chain_data;
|
|
}
|
|
|
|
static ChainData *
|
|
chain_async (GstPad * pad, GstBuffer * buffer)
|
|
{
|
|
GThread *thread;
|
|
ChainData *chain_data;
|
|
GError *error = NULL;
|
|
|
|
chain_data = g_new (ChainData, 1);
|
|
chain_data->pad = pad;
|
|
chain_data->buffer = buffer;
|
|
chain_data->ret = GST_FLOW_ERROR;
|
|
|
|
thread = g_thread_create (chain_async_buffer, chain_data, TRUE, &error);
|
|
if (error != NULL) {
|
|
g_warning ("could not create thread reason: %s", error->message);
|
|
g_free (chain_data);
|
|
return NULL;
|
|
}
|
|
chain_data->thread = thread;
|
|
|
|
return chain_data;
|
|
}
|
|
|
|
static GstFlowReturn
|
|
chain_async_return (ChainData * data)
|
|
{
|
|
GstFlowReturn ret;
|
|
|
|
g_thread_join (data->thread);
|
|
ret = data->ret;
|
|
g_free (data);
|
|
|
|
return ret;
|
|
}
|
|
|
|
GST_START_TEST (test_clipping)
|
|
{
|
|
GstElement *sink;
|
|
GstPad *sinkpad;
|
|
GstStateChangeReturn ret;
|
|
|
|
/* create sink */
|
|
sink = gst_element_factory_make ("fakesink", "sink");
|
|
fail_if (sink == NULL);
|
|
|
|
sinkpad = gst_element_get_pad (sink, "sink");
|
|
fail_if (sinkpad == NULL);
|
|
|
|
/* make element ready to accept data */
|
|
ret = gst_element_set_state (sink, GST_STATE_PAUSED);
|
|
fail_unless (ret == GST_STATE_CHANGE_ASYNC);
|
|
|
|
/* send segment */
|
|
{
|
|
GstEvent *segment;
|
|
gboolean eret;
|
|
|
|
GST_DEBUG ("sending segment");
|
|
segment = gst_event_new_new_segment (FALSE,
|
|
1.0, GST_FORMAT_TIME, 1 * GST_SECOND, 5 * GST_SECOND, 1 * GST_SECOND);
|
|
|
|
eret = gst_pad_send_event (sinkpad, segment);
|
|
fail_if (eret == FALSE);
|
|
}
|
|
|
|
/* new segment should not have finished preroll */
|
|
ret = gst_element_get_state (sink, NULL, NULL, 0);
|
|
fail_unless (ret == GST_STATE_CHANGE_ASYNC);
|
|
|
|
/* send buffer that should be dropped */
|
|
{
|
|
GstBuffer *buffer;
|
|
GstFlowReturn fret;
|
|
|
|
buffer = gst_buffer_new ();
|
|
GST_BUFFER_TIMESTAMP (buffer) = 0;
|
|
GST_BUFFER_DURATION (buffer) = 1 * GST_MSECOND;
|
|
|
|
GST_DEBUG ("sending buffer to be dropped");
|
|
fret = gst_pad_chain (sinkpad, buffer);
|
|
fail_if (fret != GST_FLOW_OK);
|
|
}
|
|
/* dropped buffer should not have finished preroll */
|
|
ret = gst_element_get_state (sink, NULL, NULL, 0);
|
|
fail_unless (ret == GST_STATE_CHANGE_ASYNC);
|
|
|
|
/* send buffer that should be dropped */
|
|
{
|
|
GstBuffer *buffer;
|
|
GstFlowReturn fret;
|
|
|
|
buffer = gst_buffer_new ();
|
|
GST_BUFFER_TIMESTAMP (buffer) = 5 * GST_SECOND;
|
|
GST_BUFFER_DURATION (buffer) = 1 * GST_MSECOND;
|
|
|
|
GST_DEBUG ("sending buffer to be dropped");
|
|
fret = gst_pad_chain (sinkpad, buffer);
|
|
fail_if (fret != GST_FLOW_OK);
|
|
}
|
|
/* dropped buffer should not have finished preroll */
|
|
ret = gst_element_get_state (sink, NULL, NULL, 0);
|
|
fail_unless (ret == GST_STATE_CHANGE_ASYNC);
|
|
|
|
/* send buffer that should block and finish preroll */
|
|
{
|
|
GstBuffer *buffer;
|
|
GstFlowReturn fret;
|
|
ChainData *data;
|
|
GstState current, pending;
|
|
|
|
buffer = gst_buffer_new ();
|
|
GST_BUFFER_TIMESTAMP (buffer) = 1 * GST_SECOND;
|
|
GST_BUFFER_DURATION (buffer) = 1 * GST_MSECOND;
|
|
|
|
GST_DEBUG ("sending buffer to finish preroll");
|
|
data = chain_async (sinkpad, buffer);
|
|
fail_if (data == NULL);
|
|
|
|
/* state should now eventually change to PAUSED */
|
|
ret = gst_element_get_state (sink, ¤t, &pending, GST_CLOCK_TIME_NONE);
|
|
fail_unless (ret == GST_STATE_CHANGE_SUCCESS);
|
|
fail_unless (current == GST_STATE_PAUSED);
|
|
fail_unless (pending == GST_STATE_VOID_PENDING);
|
|
|
|
/* playing should render the buffer */
|
|
ret = gst_element_set_state (sink, GST_STATE_PLAYING);
|
|
fail_unless (ret == GST_STATE_CHANGE_SUCCESS);
|
|
|
|
/* and we should get a success return value */
|
|
fret = chain_async_return (data);
|
|
fail_if (fret != GST_FLOW_OK);
|
|
}
|
|
|
|
/* send some buffer that will be dropped or clipped, this can
|
|
* only be observed in the debug log. */
|
|
{
|
|
GstBuffer *buffer;
|
|
GstFlowReturn fret;
|
|
|
|
buffer = gst_buffer_new ();
|
|
GST_BUFFER_TIMESTAMP (buffer) = 6 * GST_SECOND;
|
|
GST_BUFFER_DURATION (buffer) = 1 * GST_MSECOND;
|
|
|
|
/* should be dropped */
|
|
GST_DEBUG ("sending buffer to drop");
|
|
fret = gst_pad_chain (sinkpad, buffer);
|
|
fail_if (fret != GST_FLOW_OK);
|
|
|
|
buffer = gst_buffer_new ();
|
|
GST_BUFFER_TIMESTAMP (buffer) = 0 * GST_SECOND;
|
|
GST_BUFFER_DURATION (buffer) = 2 * GST_SECOND;
|
|
|
|
/* should be clipped */
|
|
GST_DEBUG ("sending buffer to clip");
|
|
fret = gst_pad_chain (sinkpad, buffer);
|
|
fail_if (fret != GST_FLOW_OK);
|
|
|
|
buffer = gst_buffer_new ();
|
|
GST_BUFFER_TIMESTAMP (buffer) = 4 * GST_SECOND;
|
|
GST_BUFFER_DURATION (buffer) = 2 * GST_SECOND;
|
|
|
|
/* should be clipped */
|
|
GST_DEBUG ("sending buffer to clip");
|
|
fret = gst_pad_chain (sinkpad, buffer);
|
|
fail_if (fret != GST_FLOW_OK);
|
|
}
|
|
|
|
gst_element_set_state (sink, GST_STATE_NULL);
|
|
gst_element_get_state (sink, NULL, NULL, GST_CLOCK_TIME_NONE);
|
|
gst_object_unref (sinkpad);
|
|
gst_object_unref (sink);
|
|
}
|
|
|
|
GST_END_TEST;
|
|
|
|
GST_START_TEST (test_preroll_sync)
|
|
{
|
|
GstElement *pipeline, *sink;
|
|
GstPad *sinkpad;
|
|
GstStateChangeReturn ret;
|
|
|
|
/* create sink */
|
|
pipeline = gst_pipeline_new ("pipeline");
|
|
fail_if (pipeline == NULL);
|
|
|
|
sink = gst_element_factory_make ("fakesink", "sink");
|
|
fail_if (sink == NULL);
|
|
g_object_set (G_OBJECT (sink), "sync", TRUE, NULL);
|
|
|
|
gst_bin_add (GST_BIN (pipeline), sink);
|
|
|
|
sinkpad = gst_element_get_pad (sink, "sink");
|
|
fail_if (sinkpad == NULL);
|
|
|
|
/* make pipeline and element ready to accept data */
|
|
ret = gst_element_set_state (pipeline, GST_STATE_PAUSED);
|
|
fail_unless (ret == GST_STATE_CHANGE_ASYNC);
|
|
|
|
/* send segment */
|
|
{
|
|
GstEvent *segment;
|
|
gboolean eret;
|
|
|
|
GST_DEBUG ("sending segment");
|
|
segment = gst_event_new_new_segment (FALSE,
|
|
1.0, GST_FORMAT_TIME, 0 * GST_SECOND, 2 * GST_SECOND, 0 * GST_SECOND);
|
|
|
|
eret = gst_pad_send_event (sinkpad, segment);
|
|
fail_if (eret == FALSE);
|
|
}
|
|
|
|
/* send buffer that should block and finish preroll */
|
|
{
|
|
GstBuffer *buffer;
|
|
GstFlowReturn fret;
|
|
ChainData *data;
|
|
GstState current, pending;
|
|
|
|
buffer = gst_buffer_new ();
|
|
GST_BUFFER_TIMESTAMP (buffer) = 1 * GST_SECOND;
|
|
GST_BUFFER_DURATION (buffer) = 1 * GST_SECOND;
|
|
|
|
GST_DEBUG ("sending buffer to finish preroll");
|
|
data = chain_async (sinkpad, buffer);
|
|
fail_if (data == NULL);
|
|
|
|
/* state should now eventually change to PAUSED */
|
|
ret =
|
|
gst_element_get_state (pipeline, ¤t, &pending,
|
|
GST_CLOCK_TIME_NONE);
|
|
fail_unless (ret == GST_STATE_CHANGE_SUCCESS);
|
|
fail_unless (current == GST_STATE_PAUSED);
|
|
fail_unless (pending == GST_STATE_VOID_PENDING);
|
|
|
|
/* playing should render the buffer */
|
|
ret = gst_element_set_state (pipeline, GST_STATE_PLAYING);
|
|
fail_unless (ret == GST_STATE_CHANGE_SUCCESS);
|
|
|
|
/* and we should get a success return value */
|
|
fret = chain_async_return (data);
|
|
fail_if (fret != GST_FLOW_OK);
|
|
}
|
|
gst_element_set_state (pipeline, GST_STATE_NULL);
|
|
gst_element_get_state (pipeline, NULL, NULL, GST_CLOCK_TIME_NONE);
|
|
gst_object_unref (sinkpad);
|
|
gst_object_unref (pipeline);
|
|
}
|
|
|
|
GST_END_TEST;
|
|
|
|
Suite *
|
|
fakesink_suite (void)
|
|
{
|
|
Suite *s = suite_create ("fakesink");
|
|
TCase *tc_chain = tcase_create ("general");
|
|
|
|
suite_add_tcase (s, tc_chain);
|
|
tcase_add_test (tc_chain, test_clipping);
|
|
tcase_add_test (tc_chain, test_preroll_sync);
|
|
|
|
return s;
|
|
}
|
|
|
|
int
|
|
main (int argc, char **argv)
|
|
{
|
|
int nf;
|
|
|
|
Suite *s = fakesink_suite ();
|
|
SRunner *sr = srunner_create (s);
|
|
|
|
gst_check_init (&argc, &argv);
|
|
|
|
srunner_run_all (sr, CK_NORMAL);
|
|
nf = srunner_ntests_failed (sr);
|
|
srunner_free (sr);
|
|
|
|
return nf;
|
|
}
|