mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-11-27 12:11:13 +00:00
Use the new GstSample for snapshots
Make appsink return a GstSample. Remove the pull_buffer_list method because it is not very useful anymore. Pass GstSample to the conversion function. Update playbin2 and examples
This commit is contained in:
parent
66d7151787
commit
59113af604
13 changed files with 461 additions and 556 deletions
|
@ -60,8 +60,7 @@ gst_app_sink_get_max_buffers
|
||||||
gst_app_sink_set_drop
|
gst_app_sink_set_drop
|
||||||
gst_app_sink_get_drop
|
gst_app_sink_get_drop
|
||||||
gst_app_sink_pull_preroll
|
gst_app_sink_pull_preroll
|
||||||
gst_app_sink_pull_buffer
|
gst_app_sink_pull_sample
|
||||||
gst_app_sink_pull_buffer_list
|
|
||||||
GstAppSinkCallbacks
|
GstAppSinkCallbacks
|
||||||
gst_app_sink_set_callbacks
|
gst_app_sink_set_callbacks
|
||||||
<SUBSECTION Standard>
|
<SUBSECTION Standard>
|
||||||
|
@ -2103,9 +2102,9 @@ gst_video_calculate_display_ratio
|
||||||
gst_video_format_to_fourcc
|
gst_video_format_to_fourcc
|
||||||
gst_video_format_from_fourcc
|
gst_video_format_from_fourcc
|
||||||
gst_video_parse_caps_palette
|
gst_video_parse_caps_palette
|
||||||
GstVideoConvertFrameCallback
|
GstVideoConvertSampleCallback
|
||||||
gst_video_convert_frame
|
gst_video_convert_sample
|
||||||
gst_video_convert_frame_async
|
gst_video_convert_sample_async
|
||||||
gst_video_event_new_still_frame
|
gst_video_event_new_still_frame
|
||||||
gst_video_event_parse_still_frame
|
gst_video_event_parse_still_frame
|
||||||
<SUBSECTION Standard>
|
<SUBSECTION Standard>
|
||||||
|
|
|
@ -19,9 +19,9 @@
|
||||||
*/
|
*/
|
||||||
/**
|
/**
|
||||||
* SECTION:gstappsink
|
* SECTION:gstappsink
|
||||||
* @short_description: Easy way for applications to extract buffers from a
|
* @short_description: Easy way for applications to extract samples from a
|
||||||
* pipeline
|
* pipeline
|
||||||
* @see_also: #GstBaseSink, appsrc
|
* @see_also: #GstSample, #GstBaseSink, appsrc
|
||||||
*
|
*
|
||||||
* Appsink is a sink plugin that supports many different methods for making
|
* Appsink is a sink plugin that supports many different methods for making
|
||||||
* the application get a handle on the GStreamer data in a pipeline. Unlike
|
* the application get a handle on the GStreamer data in a pipeline. Unlike
|
||||||
|
@ -30,13 +30,13 @@
|
||||||
* appsink can be used by linking to the gstappsink.h header file to access the
|
* appsink can be used by linking to the gstappsink.h header file to access the
|
||||||
* methods or by using the appsink action signals and properties.
|
* methods or by using the appsink action signals and properties.
|
||||||
*
|
*
|
||||||
* The normal way of retrieving buffers from appsink is by using the
|
* The normal way of retrieving samples from appsink is by using the
|
||||||
* gst_app_sink_pull_buffer() and gst_app_sink_pull_preroll() methods.
|
* gst_app_sink_pull_sample() and gst_app_sink_pull_preroll() methods.
|
||||||
* These methods block until a buffer becomes available in the sink or when the
|
* These methods block until a sample becomes available in the sink or when the
|
||||||
* sink is shut down or reaches EOS.
|
* sink is shut down or reaches EOS.
|
||||||
*
|
*
|
||||||
* Appsink will internally use a queue to collect buffers from the streaming
|
* Appsink will internally use a queue to collect buffers from the streaming
|
||||||
* thread. If the application is not pulling buffers fast enough, this queue
|
* thread. If the application is not pulling samples fast enough, this queue
|
||||||
* will consume a lot of memory over time. The "max-buffers" property can be
|
* will consume a lot of memory over time. The "max-buffers" property can be
|
||||||
* used to limit the queue size. The "drop" property controls whether the
|
* used to limit the queue size. The "drop" property controls whether the
|
||||||
* streaming thread blocks or if older buffers are dropped when the maximum
|
* streaming thread blocks or if older buffers are dropped when the maximum
|
||||||
|
@ -44,14 +44,14 @@
|
||||||
* affect real-time performance and should be avoided.
|
* affect real-time performance and should be avoided.
|
||||||
*
|
*
|
||||||
* If a blocking behaviour is not desirable, setting the "emit-signals" property
|
* If a blocking behaviour is not desirable, setting the "emit-signals" property
|
||||||
* to %TRUE will make appsink emit the "new-buffer" and "new-preroll" signals
|
* to %TRUE will make appsink emit the "new-sample" and "new-preroll" signals
|
||||||
* when a buffer can be pulled without blocking.
|
* when a sample can be pulled without blocking.
|
||||||
*
|
*
|
||||||
* The "caps" property on appsink can be used to control the formats that
|
* The "caps" property on appsink can be used to control the formats that
|
||||||
* appsink can receive. This property can contain non-fixed caps, the format of
|
* appsink can receive. This property can contain non-fixed caps, the format of
|
||||||
* the pulled buffers can be obtained by getting the buffer caps.
|
* the pulled samples can be obtained by getting the sample caps.
|
||||||
*
|
*
|
||||||
* If one of the pull-preroll or pull-buffer methods return %NULL, the appsink
|
* If one of the pull-preroll or pull-sample methods return %NULL, the appsink
|
||||||
* is stopped or in the EOS state. You can check for the EOS state with the
|
* is stopped or in the EOS state. You can check for the EOS state with the
|
||||||
* "eos" property or with the gst_app_sink_is_eos() method.
|
* "eos" property or with the gst_app_sink_is_eos() method.
|
||||||
*
|
*
|
||||||
|
@ -70,16 +70,17 @@
|
||||||
#include <gst/gst.h>
|
#include <gst/gst.h>
|
||||||
#include <gst/base/gstbasesink.h>
|
#include <gst/base/gstbasesink.h>
|
||||||
#include <gst/gstbuffer.h>
|
#include <gst/gstbuffer.h>
|
||||||
#include <gst/gstbufferlist.h>
|
|
||||||
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "gstapp-marshal.h"
|
||||||
#include "gstappsink.h"
|
#include "gstappsink.h"
|
||||||
|
|
||||||
struct _GstAppSinkPrivate
|
struct _GstAppSinkPrivate
|
||||||
{
|
{
|
||||||
GstCaps *caps;
|
GstCaps *caps;
|
||||||
gboolean emit_signals;
|
gboolean emit_signals;
|
||||||
|
guint num_buffers;
|
||||||
guint max_buffers;
|
guint max_buffers;
|
||||||
gboolean drop;
|
gboolean drop;
|
||||||
|
|
||||||
|
@ -87,6 +88,9 @@ struct _GstAppSinkPrivate
|
||||||
GMutex *mutex;
|
GMutex *mutex;
|
||||||
GQueue *queue;
|
GQueue *queue;
|
||||||
GstBuffer *preroll;
|
GstBuffer *preroll;
|
||||||
|
GstCaps *preroll_caps;
|
||||||
|
GstCaps *last_caps;
|
||||||
|
GstSegment last_segment;
|
||||||
gboolean flushing;
|
gboolean flushing;
|
||||||
gboolean unlock;
|
gboolean unlock;
|
||||||
gboolean started;
|
gboolean started;
|
||||||
|
@ -95,8 +99,6 @@ struct _GstAppSinkPrivate
|
||||||
GstAppSinkCallbacks callbacks;
|
GstAppSinkCallbacks callbacks;
|
||||||
gpointer user_data;
|
gpointer user_data;
|
||||||
GDestroyNotify notify;
|
GDestroyNotify notify;
|
||||||
|
|
||||||
gboolean buffer_lists_supported;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
GST_DEBUG_CATEGORY_STATIC (app_sink_debug);
|
GST_DEBUG_CATEGORY_STATIC (app_sink_debug);
|
||||||
|
@ -107,13 +109,11 @@ enum
|
||||||
/* signals */
|
/* signals */
|
||||||
SIGNAL_EOS,
|
SIGNAL_EOS,
|
||||||
SIGNAL_NEW_PREROLL,
|
SIGNAL_NEW_PREROLL,
|
||||||
SIGNAL_NEW_BUFFER,
|
SIGNAL_NEW_SAMPLE,
|
||||||
SIGNAL_NEW_BUFFER_LIST,
|
|
||||||
|
|
||||||
/* actions */
|
/* actions */
|
||||||
SIGNAL_PULL_PREROLL,
|
SIGNAL_PULL_PREROLL,
|
||||||
SIGNAL_PULL_BUFFER,
|
SIGNAL_PULL_SAMPLE,
|
||||||
SIGNAL_PULL_BUFFER_LIST,
|
|
||||||
|
|
||||||
LAST_SIGNAL
|
LAST_SIGNAL
|
||||||
};
|
};
|
||||||
|
@ -158,14 +158,10 @@ static gboolean gst_app_sink_stop (GstBaseSink * psink);
|
||||||
static gboolean gst_app_sink_event (GstBaseSink * sink, GstEvent * event);
|
static gboolean gst_app_sink_event (GstBaseSink * sink, GstEvent * event);
|
||||||
static GstFlowReturn gst_app_sink_preroll (GstBaseSink * psink,
|
static GstFlowReturn gst_app_sink_preroll (GstBaseSink * psink,
|
||||||
GstBuffer * buffer);
|
GstBuffer * buffer);
|
||||||
static GstFlowReturn gst_app_sink_render_common (GstBaseSink * psink,
|
|
||||||
GstMiniObject * data, gboolean is_list);
|
|
||||||
static GstFlowReturn gst_app_sink_render (GstBaseSink * psink,
|
static GstFlowReturn gst_app_sink_render (GstBaseSink * psink,
|
||||||
GstBuffer * buffer);
|
GstBuffer * buffer);
|
||||||
static GstFlowReturn gst_app_sink_render_list (GstBaseSink * psink,
|
static gboolean gst_app_sink_setcaps (GstBaseSink * sink, GstCaps * caps);
|
||||||
GstBufferList * list);
|
|
||||||
static GstCaps *gst_app_sink_getcaps (GstBaseSink * psink, GstCaps * filter);
|
static GstCaps *gst_app_sink_getcaps (GstBaseSink * psink, GstCaps * filter);
|
||||||
static GstMiniObject *gst_app_sink_pull_object (GstAppSink * appsink);
|
|
||||||
|
|
||||||
static guint gst_app_sink_signals[LAST_SIGNAL] = { 0 };
|
static guint gst_app_sink_signals[LAST_SIGNAL] = { 0 };
|
||||||
|
|
||||||
|
@ -174,40 +170,6 @@ G_DEFINE_TYPE_WITH_CODE (GstAppSink, gst_app_sink, GST_TYPE_BASE_SINK,
|
||||||
G_IMPLEMENT_INTERFACE (GST_TYPE_URI_HANDLER,
|
G_IMPLEMENT_INTERFACE (GST_TYPE_URI_HANDLER,
|
||||||
gst_app_sink_uri_handler_init));
|
gst_app_sink_uri_handler_init));
|
||||||
|
|
||||||
/* Can't use glib-genmarshal for this, as it doesn't know how to handle
|
|
||||||
* GstMiniObject-based types, which are a new fundamental type */
|
|
||||||
static void
|
|
||||||
gst_app_marshal_BUFFER__VOID (GClosure * closure,
|
|
||||||
GValue * return_value,
|
|
||||||
guint n_param_values,
|
|
||||||
const GValue * param_values,
|
|
||||||
gpointer invocation_hint, gpointer marshal_data)
|
|
||||||
{
|
|
||||||
typedef GstBuffer *(*GMarshalFunc_BUFFER__VOID) (gpointer data1,
|
|
||||||
gpointer data2);
|
|
||||||
register GMarshalFunc_BUFFER__VOID callback;
|
|
||||||
register GCClosure *cc = (GCClosure *) closure;
|
|
||||||
register gpointer data1, data2;
|
|
||||||
GstBuffer *v_return;
|
|
||||||
|
|
||||||
g_return_if_fail (return_value != NULL);
|
|
||||||
g_return_if_fail (n_param_values == 1);
|
|
||||||
|
|
||||||
if (G_CCLOSURE_SWAP_DATA (closure)) {
|
|
||||||
data1 = closure->data;
|
|
||||||
data2 = g_value_peek_pointer (param_values + 0);
|
|
||||||
} else {
|
|
||||||
data1 = g_value_peek_pointer (param_values + 0);
|
|
||||||
data2 = closure->data;
|
|
||||||
}
|
|
||||||
callback =
|
|
||||||
(GMarshalFunc_BUFFER__VOID) (marshal_data ? marshal_data : cc->callback);
|
|
||||||
|
|
||||||
v_return = callback (data1, data2);
|
|
||||||
|
|
||||||
gst_value_take_buffer (return_value, v_return);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
gst_app_sink_class_init (GstAppSinkClass * klass)
|
gst_app_sink_class_init (GstAppSinkClass * klass)
|
||||||
{
|
{
|
||||||
|
@ -265,12 +227,12 @@ gst_app_sink_class_init (GstAppSinkClass * klass)
|
||||||
* GstAppSink::new-preroll:
|
* GstAppSink::new-preroll:
|
||||||
* @appsink: the appsink element that emited the signal
|
* @appsink: the appsink element that emited the signal
|
||||||
*
|
*
|
||||||
* Signal that a new preroll buffer is available.
|
* Signal that a new preroll sample is available.
|
||||||
*
|
*
|
||||||
* This signal is emited from the steaming thread and only when the
|
* This signal is emited from the steaming thread and only when the
|
||||||
* "emit-signals" property is %TRUE.
|
* "emit-signals" property is %TRUE.
|
||||||
*
|
*
|
||||||
* The new preroll buffer can be retrieved with the "pull-preroll" action
|
* The new preroll sample can be retrieved with the "pull-preroll" action
|
||||||
* signal or gst_app_sink_pull_preroll() either from this signal callback
|
* signal or gst_app_sink_pull_preroll() either from this signal callback
|
||||||
* or from any other thread.
|
* or from any other thread.
|
||||||
*
|
*
|
||||||
|
@ -282,126 +244,80 @@ gst_app_sink_class_init (GstAppSinkClass * klass)
|
||||||
G_STRUCT_OFFSET (GstAppSinkClass, new_preroll),
|
G_STRUCT_OFFSET (GstAppSinkClass, new_preroll),
|
||||||
NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0, G_TYPE_NONE);
|
NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0, G_TYPE_NONE);
|
||||||
/**
|
/**
|
||||||
* GstAppSink::new-buffer:
|
* GstAppSink::new-sample:
|
||||||
* @appsink: the appsink element that emited the signal
|
* @appsink: the appsink element that emited the signal
|
||||||
*
|
*
|
||||||
* Signal that a new buffer is available.
|
* Signal that a new sample is available.
|
||||||
*
|
*
|
||||||
* This signal is emited from the steaming thread and only when the
|
* This signal is emited from the steaming thread and only when the
|
||||||
* "emit-signals" property is %TRUE.
|
* "emit-signals" property is %TRUE.
|
||||||
*
|
*
|
||||||
* The new buffer can be retrieved with the "pull-buffer" action
|
* The new sample can be retrieved with the "pull-sample" action
|
||||||
* signal or gst_app_sink_pull_buffer() either from this signal callback
|
* signal or gst_app_sink_pull_sample() either from this signal callback
|
||||||
* or from any other thread.
|
* or from any other thread.
|
||||||
*
|
*
|
||||||
* Note that this signal is only emited when the "emit-signals" property is
|
* Note that this signal is only emited when the "emit-signals" property is
|
||||||
* set to %TRUE, which it is not by default for performance reasons.
|
* set to %TRUE, which it is not by default for performance reasons.
|
||||||
*/
|
*/
|
||||||
gst_app_sink_signals[SIGNAL_NEW_BUFFER] =
|
gst_app_sink_signals[SIGNAL_NEW_SAMPLE] =
|
||||||
g_signal_new ("new-buffer", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST,
|
g_signal_new ("new-sample", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST,
|
||||||
G_STRUCT_OFFSET (GstAppSinkClass, new_buffer),
|
G_STRUCT_OFFSET (GstAppSinkClass, new_sample),
|
||||||
NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0, G_TYPE_NONE);
|
|
||||||
/**
|
|
||||||
* GstAppSink::new-buffer-list:
|
|
||||||
* @appsink: the appsink element that emited the signal
|
|
||||||
*
|
|
||||||
* Signal that a new bufferlist is available.
|
|
||||||
*
|
|
||||||
* This signal is emited from the steaming thread and only when the
|
|
||||||
* "emit-signals" property is %TRUE.
|
|
||||||
*
|
|
||||||
* The new buffer can be retrieved with the "pull-buffer-list" action
|
|
||||||
* signal or gst_app_sink_pull_buffer_list() either from this signal callback
|
|
||||||
* or from any other thread.
|
|
||||||
*
|
|
||||||
* Note that this signal is only emited when the "emit-signals" property is
|
|
||||||
* set to %TRUE, which it is not by default for performance reasons.
|
|
||||||
*/
|
|
||||||
gst_app_sink_signals[SIGNAL_NEW_BUFFER_LIST] =
|
|
||||||
g_signal_new ("new-buffer-list", G_TYPE_FROM_CLASS (klass),
|
|
||||||
G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstAppSinkClass, new_buffer_list),
|
|
||||||
NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0, G_TYPE_NONE);
|
NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0, G_TYPE_NONE);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* GstAppSink::pull-preroll:
|
* GstAppSink::pull-preroll:
|
||||||
* @appsink: the appsink element to emit this signal on
|
* @appsink: the appsink element to emit this signal on
|
||||||
*
|
*
|
||||||
* Get the last preroll buffer in @appsink. This was the buffer that caused the
|
* Get the last preroll sample in @appsink. This was the sample that caused the
|
||||||
* appsink to preroll in the PAUSED state. This buffer can be pulled many times
|
* appsink to preroll in the PAUSED state. This sample can be pulled many times
|
||||||
* and remains available to the application even after EOS.
|
* and remains available to the application even after EOS.
|
||||||
*
|
*
|
||||||
* This function is typically used when dealing with a pipeline in the PAUSED
|
* This function is typically used when dealing with a pipeline in the PAUSED
|
||||||
* state. Calling this function after doing a seek will give the buffer right
|
* state. Calling this function after doing a seek will give the sample right
|
||||||
* after the seek position.
|
* after the seek position.
|
||||||
*
|
*
|
||||||
* Note that the preroll buffer will also be returned as the first buffer
|
* Note that the preroll sample will also be returned as the first sample
|
||||||
* when calling gst_app_sink_pull_buffer() or the "pull-buffer" action signal.
|
* when calling gst_app_sink_pull_sample() or the "pull-sample" action signal.
|
||||||
*
|
*
|
||||||
* If an EOS event was received before any buffers, this function returns
|
* If an EOS event was received before any buffers, this function returns
|
||||||
* %NULL. Use gst_app_sink_is_eos () to check for the EOS condition.
|
* %NULL. Use gst_app_sink_is_eos () to check for the EOS condition.
|
||||||
*
|
*
|
||||||
* This function blocks until a preroll buffer or EOS is received or the appsink
|
* This function blocks until a preroll sample or EOS is received or the appsink
|
||||||
* element is set to the READY/NULL state.
|
* element is set to the READY/NULL state.
|
||||||
*
|
*
|
||||||
* Returns: a #GstBuffer or NULL when the appsink is stopped or EOS.
|
* Returns: a #GstSample or NULL when the appsink is stopped or EOS.
|
||||||
*/
|
*/
|
||||||
gst_app_sink_signals[SIGNAL_PULL_PREROLL] =
|
gst_app_sink_signals[SIGNAL_PULL_PREROLL] =
|
||||||
g_signal_new ("pull-preroll", G_TYPE_FROM_CLASS (klass),
|
g_signal_new ("pull-preroll", G_TYPE_FROM_CLASS (klass),
|
||||||
G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, G_STRUCT_OFFSET (GstAppSinkClass,
|
G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, G_STRUCT_OFFSET (GstAppSinkClass,
|
||||||
pull_preroll), NULL, NULL, gst_app_marshal_BUFFER__VOID,
|
pull_preroll), NULL, NULL, __gst_app_marshal_BOXED__VOID,
|
||||||
GST_TYPE_BUFFER, 0, G_TYPE_NONE);
|
GST_TYPE_SAMPLE, 0, G_TYPE_NONE);
|
||||||
/**
|
/**
|
||||||
* GstAppSink::pull-buffer:
|
* GstAppSink::pull-sample:
|
||||||
* @appsink: the appsink element to emit this signal on
|
* @appsink: the appsink element to emit this signal on
|
||||||
*
|
*
|
||||||
* This function blocks until a buffer or EOS becomes available or the appsink
|
* This function blocks until a sample or EOS becomes available or the appsink
|
||||||
* element is set to the READY/NULL state.
|
* element is set to the READY/NULL state.
|
||||||
*
|
*
|
||||||
* This function will only return buffers when the appsink is in the PLAYING
|
* This function will only return samples when the appsink is in the PLAYING
|
||||||
* state. All rendered buffers will be put in a queue so that the application
|
* state. All rendered samples will be put in a queue so that the application
|
||||||
* can pull buffers at its own rate.
|
* can pull samples at its own rate.
|
||||||
*
|
*
|
||||||
* Note that when the application does not pull buffers fast enough, the
|
* Note that when the application does not pull samples fast enough, the
|
||||||
* queued buffers could consume a lot of memory, especially when dealing with
|
* queued samples could consume a lot of memory, especially when dealing with
|
||||||
* raw video frames. It's possible to control the behaviour of the queue with
|
* raw video frames. It's possible to control the behaviour of the queue with
|
||||||
* the "drop" and "max-buffers" properties.
|
* the "drop" and "max-buffers" properties.
|
||||||
*
|
*
|
||||||
* If an EOS event was received before any buffers, this function returns
|
* If an EOS event was received before any buffers, this function returns
|
||||||
* %NULL. Use gst_app_sink_is_eos () to check for the EOS condition.
|
* %NULL. Use gst_app_sink_is_eos () to check for the EOS condition.
|
||||||
*
|
*
|
||||||
* Returns: a #GstBuffer or NULL when the appsink is stopped or EOS.
|
* Returns: a #GstSample or NULL when the appsink is stopped or EOS.
|
||||||
*/
|
*/
|
||||||
gst_app_sink_signals[SIGNAL_PULL_BUFFER] =
|
gst_app_sink_signals[SIGNAL_PULL_SAMPLE] =
|
||||||
g_signal_new ("pull-buffer", G_TYPE_FROM_CLASS (klass),
|
g_signal_new ("pull-sample", G_TYPE_FROM_CLASS (klass),
|
||||||
G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, G_STRUCT_OFFSET (GstAppSinkClass,
|
G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, G_STRUCT_OFFSET (GstAppSinkClass,
|
||||||
pull_buffer), NULL, NULL, gst_app_marshal_BUFFER__VOID,
|
pull_sample), NULL, NULL, __gst_app_marshal_BOXED__VOID,
|
||||||
GST_TYPE_BUFFER, 0, G_TYPE_NONE);
|
GST_TYPE_SAMPLE, 0, G_TYPE_NONE);
|
||||||
/**
|
|
||||||
* GstAppSink::pull-buffer-list:
|
|
||||||
* @appsink: the appsink element to emit this signal on
|
|
||||||
*
|
|
||||||
* This function blocks until a buffer list or EOS becomes available or the appsink
|
|
||||||
* element is set to the READY/NULL state.
|
|
||||||
*
|
|
||||||
* This function will only return bufferlists when the appsink is in the PLAYING
|
|
||||||
* state. All rendered bufferlists will be put in a queue so that the application
|
|
||||||
* can pull bufferlists at its own rate.
|
|
||||||
*
|
|
||||||
* Note that when the application does not pull bufferlists fast enough, the
|
|
||||||
* queued bufferlists could consume a lot of memory, especially when dealing with
|
|
||||||
* raw video frames. It's possible to control the behaviour of the queue with
|
|
||||||
* the "drop" and "max-buffers" properties.
|
|
||||||
*
|
|
||||||
* If an EOS event was received before any buffers, this function returns
|
|
||||||
* %NULL. Use gst_app_sink_is_eos () to check for the EOS condition.
|
|
||||||
*
|
|
||||||
* Returns: a #GstBufferList or NULL when the appsink is stopped or EOS.
|
|
||||||
*/
|
|
||||||
gst_app_sink_signals[SIGNAL_PULL_BUFFER_LIST] =
|
|
||||||
g_signal_new ("pull-buffer-list", G_TYPE_FROM_CLASS (klass),
|
|
||||||
G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, G_STRUCT_OFFSET (GstAppSinkClass,
|
|
||||||
pull_buffer_list), NULL, NULL, gst_app_marshal_BUFFER__VOID,
|
|
||||||
GST_TYPE_BUFFER_LIST, 0, G_TYPE_NONE);
|
|
||||||
|
|
||||||
gst_element_class_set_details_simple (element_class, "AppSink",
|
gst_element_class_set_details_simple (element_class, "AppSink",
|
||||||
"Generic/Sink", "Allow the application to get access to raw buffer",
|
"Generic/Sink", "Allow the application to get access to raw buffer",
|
||||||
|
@ -417,12 +333,11 @@ gst_app_sink_class_init (GstAppSinkClass * klass)
|
||||||
basesink_class->event = gst_app_sink_event;
|
basesink_class->event = gst_app_sink_event;
|
||||||
basesink_class->preroll = gst_app_sink_preroll;
|
basesink_class->preroll = gst_app_sink_preroll;
|
||||||
basesink_class->render = gst_app_sink_render;
|
basesink_class->render = gst_app_sink_render;
|
||||||
basesink_class->render_list = gst_app_sink_render_list;
|
|
||||||
basesink_class->get_caps = gst_app_sink_getcaps;
|
basesink_class->get_caps = gst_app_sink_getcaps;
|
||||||
|
basesink_class->set_caps = gst_app_sink_setcaps;
|
||||||
|
|
||||||
klass->pull_preroll = gst_app_sink_pull_preroll;
|
klass->pull_preroll = gst_app_sink_pull_preroll;
|
||||||
klass->pull_buffer = gst_app_sink_pull_buffer;
|
klass->pull_sample = gst_app_sink_pull_sample;
|
||||||
klass->pull_buffer_list = gst_app_sink_pull_buffer_list;
|
|
||||||
|
|
||||||
g_type_class_add_private (klass, sizeof (GstAppSinkPrivate));
|
g_type_class_add_private (klass, sizeof (GstAppSinkPrivate));
|
||||||
}
|
}
|
||||||
|
@ -466,12 +381,11 @@ gst_app_sink_dispose (GObject * obj)
|
||||||
GST_OBJECT_UNLOCK (appsink);
|
GST_OBJECT_UNLOCK (appsink);
|
||||||
|
|
||||||
g_mutex_lock (priv->mutex);
|
g_mutex_lock (priv->mutex);
|
||||||
if (priv->preroll) {
|
|
||||||
gst_buffer_unref (priv->preroll);
|
|
||||||
priv->preroll = NULL;
|
|
||||||
}
|
|
||||||
while ((queue_obj = g_queue_pop_head (priv->queue)))
|
while ((queue_obj = g_queue_pop_head (priv->queue)))
|
||||||
gst_mini_object_unref (queue_obj);
|
gst_mini_object_unref (queue_obj);
|
||||||
|
gst_buffer_replace (&priv->preroll, NULL);
|
||||||
|
gst_caps_replace (&priv->preroll_caps, NULL);
|
||||||
|
gst_caps_replace (&priv->last_caps, NULL);
|
||||||
g_mutex_unlock (priv->mutex);
|
g_mutex_unlock (priv->mutex);
|
||||||
|
|
||||||
G_OBJECT_CLASS (parent_class)->dispose (obj);
|
G_OBJECT_CLASS (parent_class)->dispose (obj);
|
||||||
|
@ -591,24 +505,10 @@ gst_app_sink_flush_unlocked (GstAppSink * appsink)
|
||||||
gst_buffer_replace (&priv->preroll, NULL);
|
gst_buffer_replace (&priv->preroll, NULL);
|
||||||
while ((obj = g_queue_pop_head (priv->queue)))
|
while ((obj = g_queue_pop_head (priv->queue)))
|
||||||
gst_mini_object_unref (obj);
|
gst_mini_object_unref (obj);
|
||||||
|
priv->num_buffers = 0;
|
||||||
g_cond_signal (priv->cond);
|
g_cond_signal (priv->cond);
|
||||||
}
|
}
|
||||||
|
|
||||||
#define NEW_BUFFER_LIST_SIGID \
|
|
||||||
gst_app_sink_signals[SIGNAL_NEW_BUFFER_LIST]
|
|
||||||
|
|
||||||
static gboolean
|
|
||||||
gst_app_sink_check_buffer_lists_support (GstAppSink * appsink)
|
|
||||||
{
|
|
||||||
gboolean ret;
|
|
||||||
|
|
||||||
ret = (appsink->priv->callbacks.new_buffer_list != NULL) ||
|
|
||||||
g_signal_has_handler_pending (appsink, NEW_BUFFER_LIST_SIGID, 0, FALSE);
|
|
||||||
|
|
||||||
GST_INFO_OBJECT (appsink, "application supports buffer lists: %d", ret);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static gboolean
|
static gboolean
|
||||||
gst_app_sink_start (GstBaseSink * psink)
|
gst_app_sink_start (GstBaseSink * psink)
|
||||||
{
|
{
|
||||||
|
@ -619,8 +519,7 @@ gst_app_sink_start (GstBaseSink * psink)
|
||||||
GST_DEBUG_OBJECT (appsink, "starting");
|
GST_DEBUG_OBJECT (appsink, "starting");
|
||||||
priv->flushing = FALSE;
|
priv->flushing = FALSE;
|
||||||
priv->started = TRUE;
|
priv->started = TRUE;
|
||||||
priv->buffer_lists_supported =
|
gst_segment_init (&priv->last_segment, GST_FORMAT_TIME);
|
||||||
gst_app_sink_check_buffer_lists_support (appsink);
|
|
||||||
g_mutex_unlock (priv->mutex);
|
g_mutex_unlock (priv->mutex);
|
||||||
|
|
||||||
return TRUE;
|
return TRUE;
|
||||||
|
@ -637,6 +536,23 @@ gst_app_sink_stop (GstBaseSink * psink)
|
||||||
priv->flushing = TRUE;
|
priv->flushing = TRUE;
|
||||||
priv->started = FALSE;
|
priv->started = FALSE;
|
||||||
gst_app_sink_flush_unlocked (appsink);
|
gst_app_sink_flush_unlocked (appsink);
|
||||||
|
gst_caps_replace (&priv->preroll_caps, NULL);
|
||||||
|
gst_caps_replace (&priv->last_caps, NULL);
|
||||||
|
g_mutex_unlock (priv->mutex);
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
gst_app_sink_setcaps (GstBaseSink * sink, GstCaps * caps)
|
||||||
|
{
|
||||||
|
GstAppSink *appsink = GST_APP_SINK_CAST (sink);
|
||||||
|
GstAppSinkPrivate *priv = appsink->priv;
|
||||||
|
|
||||||
|
g_mutex_lock (priv->mutex);
|
||||||
|
GST_DEBUG_OBJECT (appsink, "receiving CAPS");
|
||||||
|
g_queue_push_tail (priv->queue, gst_event_new_caps (caps));
|
||||||
|
gst_caps_replace (&priv->preroll_caps, caps);
|
||||||
g_mutex_unlock (priv->mutex);
|
g_mutex_unlock (priv->mutex);
|
||||||
|
|
||||||
return TRUE;
|
return TRUE;
|
||||||
|
@ -649,8 +565,13 @@ gst_app_sink_event (GstBaseSink * sink, GstEvent * event)
|
||||||
GstAppSinkPrivate *priv = appsink->priv;
|
GstAppSinkPrivate *priv = appsink->priv;
|
||||||
|
|
||||||
switch (event->type) {
|
switch (event->type) {
|
||||||
|
case GST_EVENT_SEGMENT:
|
||||||
|
g_mutex_lock (priv->mutex);
|
||||||
|
GST_DEBUG_OBJECT (appsink, "receiving SEGMENT");
|
||||||
|
g_queue_push_tail (priv->queue, gst_event_ref (event));
|
||||||
|
g_mutex_unlock (priv->mutex);
|
||||||
|
break;
|
||||||
case GST_EVENT_EOS:
|
case GST_EVENT_EOS:
|
||||||
|
|
||||||
g_mutex_lock (priv->mutex);
|
g_mutex_lock (priv->mutex);
|
||||||
GST_DEBUG_OBJECT (appsink, "receiving EOS");
|
GST_DEBUG_OBJECT (appsink, "receiving EOS");
|
||||||
priv->is_eos = TRUE;
|
priv->is_eos = TRUE;
|
||||||
|
@ -715,9 +636,52 @@ flushing:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static GstBuffer *
|
||||||
|
dequeue_buffer (GstAppSink * appsink)
|
||||||
|
{
|
||||||
|
GstAppSinkPrivate *priv = appsink->priv;
|
||||||
|
GstBuffer *buffer;
|
||||||
|
|
||||||
|
do {
|
||||||
|
GstMiniObject *obj;
|
||||||
|
|
||||||
|
obj = g_queue_pop_head (priv->queue);
|
||||||
|
|
||||||
|
if (GST_IS_BUFFER (obj)) {
|
||||||
|
buffer = GST_BUFFER_CAST (obj);
|
||||||
|
GST_DEBUG_OBJECT (appsink, "dequeued buffer %p", buffer);
|
||||||
|
priv->num_buffers--;
|
||||||
|
break;
|
||||||
|
} else if (GST_IS_EVENT (obj)) {
|
||||||
|
GstEvent *event = GST_EVENT_CAST (obj);
|
||||||
|
|
||||||
|
switch (GST_EVENT_TYPE (obj)) {
|
||||||
|
case GST_EVENT_CAPS:
|
||||||
|
{
|
||||||
|
GstCaps *caps;
|
||||||
|
|
||||||
|
gst_event_parse_caps (event, &caps);
|
||||||
|
GST_DEBUG_OBJECT (appsink, "activating caps %" GST_PTR_FORMAT, caps);
|
||||||
|
gst_caps_replace (priv->last_caps, caps);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case GST_EVENT_SEGMENT:
|
||||||
|
gst_event_copy_segment (event, &priv->last_segment);
|
||||||
|
GST_DEBUG_OBJECT (appsink, "activated segment %" GST_SEGMENT_FORMAT,
|
||||||
|
&priv->last_segment);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
gst_mini_object_unref (obj);
|
||||||
|
}
|
||||||
|
} while (TRUE);
|
||||||
|
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
static GstFlowReturn
|
static GstFlowReturn
|
||||||
gst_app_sink_render_common (GstBaseSink * psink, GstMiniObject * data,
|
gst_app_sink_render (GstBaseSink * psink, GstBuffer * buffer)
|
||||||
gboolean is_list)
|
|
||||||
{
|
{
|
||||||
GstFlowReturn ret;
|
GstFlowReturn ret;
|
||||||
GstAppSink *appsink = GST_APP_SINK_CAST (psink);
|
GstAppSink *appsink = GST_APP_SINK_CAST (psink);
|
||||||
|
@ -729,20 +693,21 @@ restart:
|
||||||
if (priv->flushing)
|
if (priv->flushing)
|
||||||
goto flushing;
|
goto flushing;
|
||||||
|
|
||||||
GST_DEBUG_OBJECT (appsink, "pushing render buffer%s %p on queue (%d)",
|
GST_DEBUG_OBJECT (appsink, "pushing render buffer %p on queue (%d)",
|
||||||
is_list ? " list" : "", data, priv->queue->length);
|
buffer, priv->num_buffers);
|
||||||
|
|
||||||
while (priv->max_buffers > 0 && priv->queue->length >= priv->max_buffers) {
|
while (priv->max_buffers > 0 && priv->num_buffers >= priv->max_buffers) {
|
||||||
if (priv->drop) {
|
if (priv->drop) {
|
||||||
GstMiniObject *obj;
|
GstBuffer *old;
|
||||||
|
|
||||||
/* we need to drop the oldest buffer/list and try again */
|
/* we need to drop the oldest buffer and try again */
|
||||||
obj = g_queue_pop_head (priv->queue);
|
if ((old = dequeue_buffer (appsink))) {
|
||||||
GST_DEBUG_OBJECT (appsink, "dropping old buffer/list %p", obj);
|
GST_DEBUG_OBJECT (appsink, "dropping old buffer %p", old);
|
||||||
gst_mini_object_unref (obj);
|
gst_buffer_unref (old);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
GST_DEBUG_OBJECT (appsink, "waiting for free space, length %d >= %d",
|
GST_DEBUG_OBJECT (appsink, "waiting for free space, length %d >= %d",
|
||||||
priv->queue->length, priv->max_buffers);
|
priv->num_buffers, priv->max_buffers);
|
||||||
|
|
||||||
if (priv->unlock) {
|
if (priv->unlock) {
|
||||||
/* we are asked to unlock, call the wait_preroll method */
|
/* we are asked to unlock, call the wait_preroll method */
|
||||||
|
@ -761,22 +726,17 @@ restart:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/* we need to ref the buffer when pushing it in the queue */
|
/* we need to ref the buffer when pushing it in the queue */
|
||||||
g_queue_push_tail (priv->queue, gst_mini_object_ref (data));
|
g_queue_push_tail (priv->queue, gst_buffer_ref (buffer));
|
||||||
|
priv->num_buffers++;
|
||||||
g_cond_signal (priv->cond);
|
g_cond_signal (priv->cond);
|
||||||
emit = priv->emit_signals;
|
emit = priv->emit_signals;
|
||||||
g_mutex_unlock (priv->mutex);
|
g_mutex_unlock (priv->mutex);
|
||||||
|
|
||||||
if (is_list) {
|
if (priv->callbacks.new_sample)
|
||||||
if (priv->callbacks.new_buffer_list)
|
priv->callbacks.new_sample (appsink, priv->user_data);
|
||||||
priv->callbacks.new_buffer_list (appsink, priv->user_data);
|
|
||||||
else if (emit)
|
else if (emit)
|
||||||
g_signal_emit (appsink, gst_app_sink_signals[SIGNAL_NEW_BUFFER_LIST], 0);
|
g_signal_emit (appsink, gst_app_sink_signals[SIGNAL_NEW_SAMPLE], 0);
|
||||||
} else {
|
|
||||||
if (priv->callbacks.new_buffer)
|
|
||||||
priv->callbacks.new_buffer (appsink, priv->user_data);
|
|
||||||
else if (emit)
|
|
||||||
g_signal_emit (appsink, gst_app_sink_signals[SIGNAL_NEW_BUFFER], 0);
|
|
||||||
}
|
|
||||||
return GST_FLOW_OK;
|
return GST_FLOW_OK;
|
||||||
|
|
||||||
flushing:
|
flushing:
|
||||||
|
@ -792,43 +752,6 @@ stopping:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static GstFlowReturn
|
|
||||||
gst_app_sink_render (GstBaseSink * psink, GstBuffer * buffer)
|
|
||||||
{
|
|
||||||
return gst_app_sink_render_common (psink, GST_MINI_OBJECT_CAST (buffer),
|
|
||||||
FALSE);
|
|
||||||
}
|
|
||||||
|
|
||||||
static GstFlowReturn
|
|
||||||
gst_app_sink_render_list (GstBaseSink * sink, GstBufferList * list)
|
|
||||||
{
|
|
||||||
GstFlowReturn flow;
|
|
||||||
GstAppSink *appsink;
|
|
||||||
GstBuffer *buffer;
|
|
||||||
guint i, len;
|
|
||||||
|
|
||||||
appsink = GST_APP_SINK_CAST (sink);
|
|
||||||
|
|
||||||
if (appsink->priv->buffer_lists_supported)
|
|
||||||
return gst_app_sink_render_common (sink, GST_MINI_OBJECT_CAST (list), TRUE);
|
|
||||||
|
|
||||||
/* The application doesn't support buffer lists, extract individual buffers
|
|
||||||
* then and push them one-by-one */
|
|
||||||
GST_INFO_OBJECT (sink, "chaining each group in list as a merged buffer");
|
|
||||||
|
|
||||||
len = gst_buffer_list_length (list);
|
|
||||||
|
|
||||||
flow = GST_FLOW_OK;
|
|
||||||
for (i = 0; i < len; i++) {
|
|
||||||
buffer = gst_buffer_list_get (list, i);
|
|
||||||
flow = gst_app_sink_render (sink, buffer);
|
|
||||||
if (flow != GST_FLOW_OK)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return flow;
|
|
||||||
}
|
|
||||||
|
|
||||||
static GstCaps *
|
static GstCaps *
|
||||||
gst_app_sink_getcaps (GstBaseSink * psink, GstCaps * filter)
|
gst_app_sink_getcaps (GstBaseSink * psink, GstCaps * filter)
|
||||||
{
|
{
|
||||||
|
@ -849,55 +772,6 @@ gst_app_sink_getcaps (GstBaseSink * psink, GstCaps * filter)
|
||||||
return caps;
|
return caps;
|
||||||
}
|
}
|
||||||
|
|
||||||
static GstMiniObject *
|
|
||||||
gst_app_sink_pull_object (GstAppSink * appsink)
|
|
||||||
{
|
|
||||||
GstMiniObject *obj = NULL;
|
|
||||||
GstAppSinkPrivate *priv;
|
|
||||||
|
|
||||||
g_return_val_if_fail (GST_IS_APP_SINK (appsink), NULL);
|
|
||||||
|
|
||||||
priv = appsink->priv;
|
|
||||||
|
|
||||||
g_mutex_lock (priv->mutex);
|
|
||||||
|
|
||||||
while (TRUE) {
|
|
||||||
GST_DEBUG_OBJECT (appsink, "trying to grab a buffer/list");
|
|
||||||
if (!priv->started)
|
|
||||||
goto not_started;
|
|
||||||
|
|
||||||
if (!g_queue_is_empty (priv->queue))
|
|
||||||
break;
|
|
||||||
|
|
||||||
if (priv->is_eos)
|
|
||||||
goto eos;
|
|
||||||
|
|
||||||
/* nothing to return, wait */
|
|
||||||
GST_DEBUG_OBJECT (appsink, "waiting for a buffer/list");
|
|
||||||
g_cond_wait (priv->cond, priv->mutex);
|
|
||||||
}
|
|
||||||
obj = g_queue_pop_head (priv->queue);
|
|
||||||
GST_DEBUG_OBJECT (appsink, "we have a buffer/list %p", obj);
|
|
||||||
g_cond_signal (priv->cond);
|
|
||||||
g_mutex_unlock (priv->mutex);
|
|
||||||
|
|
||||||
return obj;
|
|
||||||
|
|
||||||
/* special conditions */
|
|
||||||
eos:
|
|
||||||
{
|
|
||||||
GST_DEBUG_OBJECT (appsink, "we are EOS, return NULL");
|
|
||||||
g_mutex_unlock (priv->mutex);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
not_started:
|
|
||||||
{
|
|
||||||
GST_DEBUG_OBJECT (appsink, "we are stopped, return NULL");
|
|
||||||
g_mutex_unlock (priv->mutex);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* external API */
|
/* external API */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -968,13 +842,13 @@ gst_app_sink_get_caps (GstAppSink * appsink)
|
||||||
* gst_app_sink_is_eos:
|
* gst_app_sink_is_eos:
|
||||||
* @appsink: a #GstAppSink
|
* @appsink: a #GstAppSink
|
||||||
*
|
*
|
||||||
* Check if @appsink is EOS, which is when no more buffers can be pulled because
|
* Check if @appsink is EOS, which is when no more samples can be pulled because
|
||||||
* an EOS event was received.
|
* an EOS event was received.
|
||||||
*
|
*
|
||||||
* This function also returns %TRUE when the appsink is not in the PAUSED or
|
* This function also returns %TRUE when the appsink is not in the PAUSED or
|
||||||
* PLAYING state.
|
* PLAYING state.
|
||||||
*
|
*
|
||||||
* Returns: %TRUE if no more buffers can be pulled and the appsink is EOS.
|
* Returns: %TRUE if no more samples can be pulled and the appsink is EOS.
|
||||||
*
|
*
|
||||||
* Since: 0.10.22
|
* Since: 0.10.22
|
||||||
*/
|
*/
|
||||||
|
@ -992,7 +866,7 @@ gst_app_sink_is_eos (GstAppSink * appsink)
|
||||||
if (!priv->started)
|
if (!priv->started)
|
||||||
goto not_started;
|
goto not_started;
|
||||||
|
|
||||||
if (priv->is_eos && g_queue_is_empty (priv->queue)) {
|
if (priv->is_eos && priv->num_buffers == 0) {
|
||||||
GST_DEBUG_OBJECT (appsink, "we are EOS and the queue is empty");
|
GST_DEBUG_OBJECT (appsink, "we are EOS and the queue is empty");
|
||||||
ret = TRUE;
|
ret = TRUE;
|
||||||
} else {
|
} else {
|
||||||
|
@ -1016,7 +890,7 @@ not_started:
|
||||||
* @appsink: a #GstAppSink
|
* @appsink: a #GstAppSink
|
||||||
* @emit: the new state
|
* @emit: the new state
|
||||||
*
|
*
|
||||||
* Make appsink emit the "new-preroll" and "new-buffer" signals. This option is
|
* Make appsink emit the "new-preroll" and "new-sample" signals. This option is
|
||||||
* by default disabled because signal emission is expensive and unneeded when
|
* by default disabled because signal emission is expensive and unneeded when
|
||||||
* the application prefers to operate in pull mode.
|
* the application prefers to operate in pull mode.
|
||||||
*
|
*
|
||||||
|
@ -1040,9 +914,9 @@ gst_app_sink_set_emit_signals (GstAppSink * appsink, gboolean emit)
|
||||||
* gst_app_sink_get_emit_signals:
|
* gst_app_sink_get_emit_signals:
|
||||||
* @appsink: a #GstAppSink
|
* @appsink: a #GstAppSink
|
||||||
*
|
*
|
||||||
* Check if appsink will emit the "new-preroll" and "new-buffer" signals.
|
* Check if appsink will emit the "new-preroll" and "new-sample" signals.
|
||||||
*
|
*
|
||||||
* Returns: %TRUE if @appsink is emiting the "new-preroll" and "new-buffer"
|
* Returns: %TRUE if @appsink is emiting the "new-preroll" and "new-sample"
|
||||||
* signals.
|
* signals.
|
||||||
*
|
*
|
||||||
* Since: 0.10.22
|
* Since: 0.10.22
|
||||||
|
@ -1071,7 +945,7 @@ gst_app_sink_get_emit_signals (GstAppSink * appsink)
|
||||||
*
|
*
|
||||||
* Set the maximum amount of buffers that can be queued in @appsink. After this
|
* Set the maximum amount of buffers that can be queued in @appsink. After this
|
||||||
* amount of buffers are queued in appsink, any more buffers will block upstream
|
* amount of buffers are queued in appsink, any more buffers will block upstream
|
||||||
* elements until a buffer is pulled from @appsink.
|
* elements until a sample is pulled from @appsink.
|
||||||
*
|
*
|
||||||
* Since: 0.10.22
|
* Since: 0.10.22
|
||||||
*/
|
*/
|
||||||
|
@ -1181,31 +1055,31 @@ gst_app_sink_get_drop (GstAppSink * appsink)
|
||||||
* gst_app_sink_pull_preroll:
|
* gst_app_sink_pull_preroll:
|
||||||
* @appsink: a #GstAppSink
|
* @appsink: a #GstAppSink
|
||||||
*
|
*
|
||||||
* Get the last preroll buffer in @appsink. This was the buffer that caused the
|
* Get the last preroll sample in @appsink. This was the sample that caused the
|
||||||
* appsink to preroll in the PAUSED state. This buffer can be pulled many times
|
* appsink to preroll in the PAUSED state. This sample can be pulled many times
|
||||||
* and remains available to the application even after EOS.
|
* and remains available to the application even after EOS.
|
||||||
*
|
*
|
||||||
* This function is typically used when dealing with a pipeline in the PAUSED
|
* This function is typically used when dealing with a pipeline in the PAUSED
|
||||||
* state. Calling this function after doing a seek will give the buffer right
|
* state. Calling this function after doing a seek will give the sample right
|
||||||
* after the seek position.
|
* after the seek position.
|
||||||
*
|
*
|
||||||
* Note that the preroll buffer will also be returned as the first buffer
|
* Note that the preroll sample will also be returned as the first sample
|
||||||
* when calling gst_app_sink_pull_buffer().
|
* when calling gst_app_sink_pull_sample().
|
||||||
*
|
*
|
||||||
* If an EOS event was received before any buffers, this function returns
|
* If an EOS event was received before any buffers, this function returns
|
||||||
* %NULL. Use gst_app_sink_is_eos () to check for the EOS condition.
|
* %NULL. Use gst_app_sink_is_eos () to check for the EOS condition.
|
||||||
*
|
*
|
||||||
* This function blocks until a preroll buffer or EOS is received or the appsink
|
* This function blocks until a preroll sample or EOS is received or the appsink
|
||||||
* element is set to the READY/NULL state.
|
* element is set to the READY/NULL state.
|
||||||
*
|
*
|
||||||
* Returns: a #GstBuffer or NULL when the appsink is stopped or EOS.
|
* Returns: a #GstBuffer or NULL when the appsink is stopped or EOS.
|
||||||
*
|
*
|
||||||
* Since: 0.10.22
|
* Since: 0.10.22
|
||||||
*/
|
*/
|
||||||
GstBuffer *
|
GstSample *
|
||||||
gst_app_sink_pull_preroll (GstAppSink * appsink)
|
gst_app_sink_pull_preroll (GstAppSink * appsink)
|
||||||
{
|
{
|
||||||
GstBuffer *buf = NULL;
|
GstSample *sample = NULL;
|
||||||
GstAppSinkPrivate *priv;
|
GstAppSinkPrivate *priv;
|
||||||
|
|
||||||
g_return_val_if_fail (GST_IS_APP_SINK (appsink), NULL);
|
g_return_val_if_fail (GST_IS_APP_SINK (appsink), NULL);
|
||||||
|
@ -1229,11 +1103,13 @@ gst_app_sink_pull_preroll (GstAppSink * appsink)
|
||||||
GST_DEBUG_OBJECT (appsink, "waiting for the preroll buffer");
|
GST_DEBUG_OBJECT (appsink, "waiting for the preroll buffer");
|
||||||
g_cond_wait (priv->cond, priv->mutex);
|
g_cond_wait (priv->cond, priv->mutex);
|
||||||
}
|
}
|
||||||
buf = gst_buffer_ref (priv->preroll);
|
sample =
|
||||||
GST_DEBUG_OBJECT (appsink, "we have the preroll buffer %p", buf);
|
gst_sample_new (priv->preroll, priv->preroll_caps, &priv->last_segment,
|
||||||
|
NULL);
|
||||||
|
GST_DEBUG_OBJECT (appsink, "we have the preroll sample %p", sample);
|
||||||
g_mutex_unlock (priv->mutex);
|
g_mutex_unlock (priv->mutex);
|
||||||
|
|
||||||
return buf;
|
return sample;
|
||||||
|
|
||||||
/* special conditions */
|
/* special conditions */
|
||||||
eos:
|
eos:
|
||||||
|
@ -1251,16 +1127,16 @@ not_started:
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* gst_app_sink_pull_buffer:
|
* gst_app_sink_pull_sample:
|
||||||
* @appsink: a #GstAppSink
|
* @appsink: a #GstAppSink
|
||||||
*
|
*
|
||||||
* This function blocks until a buffer or EOS becomes available or the appsink
|
* This function blocks until a sample or EOS becomes available or the appsink
|
||||||
* element is set to the READY/NULL state.
|
* element is set to the READY/NULL state.
|
||||||
*
|
*
|
||||||
* This function will only return buffers when the appsink is in the PLAYING
|
* This function will only return samples when the appsink is in the PLAYING
|
||||||
* state. All rendered buffers will be put in a queue so that the application
|
* state. All rendered buffers will be put in a queue so that the application
|
||||||
* can pull buffers at its own rate. Note that when the application does not
|
* can pull samples at its own rate. Note that when the application does not
|
||||||
* pull buffers fast enough, the queued buffers could consume a lot of memory,
|
* pull samples fast enough, the queued buffers could consume a lot of memory,
|
||||||
* especially when dealing with raw video frames.
|
* especially when dealing with raw video frames.
|
||||||
*
|
*
|
||||||
* If an EOS event was received before any buffers, this function returns
|
* If an EOS event was received before any buffers, this function returns
|
||||||
|
@ -1271,37 +1147,57 @@ not_started:
|
||||||
* Since: 0.10.22
|
* Since: 0.10.22
|
||||||
*/
|
*/
|
||||||
|
|
||||||
GstBuffer *
|
GstSample *
|
||||||
gst_app_sink_pull_buffer (GstAppSink * appsink)
|
gst_app_sink_pull_sample (GstAppSink * appsink)
|
||||||
{
|
{
|
||||||
GST_DEBUG_OBJECT (appsink, "pull a buffer");
|
GstSample *sample = NULL;
|
||||||
return GST_BUFFER_CAST (gst_app_sink_pull_object (appsink));
|
GstBuffer *buffer;
|
||||||
}
|
GstAppSinkPrivate *priv;
|
||||||
|
|
||||||
/**
|
g_return_val_if_fail (GST_IS_APP_SINK (appsink), NULL);
|
||||||
* gst_app_sink_pull_buffer_list:
|
|
||||||
* @appsink: a #GstAppSink
|
priv = appsink->priv;
|
||||||
*
|
|
||||||
* This function blocks until a buffer list or EOS becomes available or the
|
g_mutex_lock (priv->mutex);
|
||||||
* appsink element is set to the READY/NULL state.
|
|
||||||
*
|
while (TRUE) {
|
||||||
* This function will only return buffer lists when the appsink is in the
|
GST_DEBUG_OBJECT (appsink, "trying to grab a buffer");
|
||||||
* PLAYING state. All rendered buffer lists will be put in a queue so that
|
if (!priv->started)
|
||||||
* the application can pull buffer lists at its own rate. Note that when
|
goto not_started;
|
||||||
* the application does not pull buffer lists fast enough, the queued buffer
|
|
||||||
* lists could consume a lot of memory, especially when dealing with raw
|
if (priv->num_buffers == 0)
|
||||||
* video frames.
|
break;
|
||||||
*
|
|
||||||
* If an EOS event was received before any buffer lists, this function returns
|
if (priv->is_eos)
|
||||||
* %NULL. Use gst_app_sink_is_eos () to check for the EOS condition.
|
goto eos;
|
||||||
*
|
|
||||||
* Returns: a #GstBufferList or NULL when the appsink is stopped or EOS.
|
/* nothing to return, wait */
|
||||||
*/
|
GST_DEBUG_OBJECT (appsink, "waiting for a buffer");
|
||||||
GstBufferList *
|
g_cond_wait (priv->cond, priv->mutex);
|
||||||
gst_app_sink_pull_buffer_list (GstAppSink * appsink)
|
}
|
||||||
|
buffer = dequeue_buffer (appsink);
|
||||||
|
GST_DEBUG_OBJECT (appsink, "we have a buffer %p", buffer);
|
||||||
|
sample = gst_sample_new (buffer, priv->last_caps, &priv->last_segment, NULL);
|
||||||
|
gst_buffer_unref (buffer);
|
||||||
|
|
||||||
|
g_cond_signal (priv->cond);
|
||||||
|
g_mutex_unlock (priv->mutex);
|
||||||
|
|
||||||
|
return sample;
|
||||||
|
|
||||||
|
/* special conditions */
|
||||||
|
eos:
|
||||||
{
|
{
|
||||||
GST_DEBUG_OBJECT (appsink, "pull a buffer list");
|
GST_DEBUG_OBJECT (appsink, "we are EOS, return NULL");
|
||||||
return GST_BUFFER_LIST_CAST (gst_app_sink_pull_object (appsink));
|
g_mutex_unlock (priv->mutex);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
not_started:
|
||||||
|
{
|
||||||
|
GST_DEBUG_OBJECT (appsink, "we are stopped, return NULL");
|
||||||
|
g_mutex_unlock (priv->mutex);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1311,7 +1207,7 @@ gst_app_sink_pull_buffer_list (GstAppSink * appsink)
|
||||||
* @user_data: a user_data argument for the callbacks
|
* @user_data: a user_data argument for the callbacks
|
||||||
* @notify: a destroy notify function
|
* @notify: a destroy notify function
|
||||||
*
|
*
|
||||||
* Set callbacks which will be executed for each new preroll, new buffer and eos.
|
* Set callbacks which will be executed for each new preroll, new sample and eos.
|
||||||
* This is an alternative to using the signals, it has lower overhead and is thus
|
* This is an alternative to using the signals, it has lower overhead and is thus
|
||||||
* less expensive, but also less flexible.
|
* less expensive, but also less flexible.
|
||||||
*
|
*
|
||||||
|
@ -1351,8 +1247,6 @@ gst_app_sink_set_callbacks (GstAppSink * appsink,
|
||||||
priv->callbacks = *callbacks;
|
priv->callbacks = *callbacks;
|
||||||
priv->user_data = user_data;
|
priv->user_data = user_data;
|
||||||
priv->notify = notify;
|
priv->notify = notify;
|
||||||
priv->buffer_lists_supported =
|
|
||||||
gst_app_sink_check_buffer_lists_support (appsink);
|
|
||||||
GST_OBJECT_UNLOCK (appsink);
|
GST_OBJECT_UNLOCK (appsink);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -47,20 +47,15 @@ typedef struct _GstAppSinkPrivate GstAppSinkPrivate;
|
||||||
* GstAppSinkCallbacks:
|
* GstAppSinkCallbacks:
|
||||||
* @eos: Called when the end-of-stream has been reached. This callback
|
* @eos: Called when the end-of-stream has been reached. This callback
|
||||||
* is called from the steaming thread.
|
* is called from the steaming thread.
|
||||||
* @new_preroll: Called when a new preroll buffer is available.
|
* @new_preroll: Called when a new preroll sample is available.
|
||||||
* This callback is called from the steaming thread.
|
* This callback is called from the steaming thread.
|
||||||
* The new preroll buffer can be retrieved with
|
* The new preroll sample can be retrieved with
|
||||||
* gst_app_sink_pull_preroll() either from this callback
|
* gst_app_sink_pull_preroll() either from this callback
|
||||||
* or from any other thread.
|
* or from any other thread.
|
||||||
* @new_buffer: Called when a new buffer is available.
|
* @new_sample: Called when a new sample is available.
|
||||||
* This callback is called from the steaming thread.
|
* This callback is called from the steaming thread.
|
||||||
* The new buffer can be retrieved with
|
* The new sample can be retrieved with
|
||||||
* gst_app_sink_pull_buffer() either from this callback
|
* gst_app_sink_pull_sample() either from this callback
|
||||||
* or from any other thread.
|
|
||||||
* @new_buffer_list: Called when a new bufferlist is available.
|
|
||||||
* This callback is called from the steaming thread.
|
|
||||||
* The new bufferlist can be retrieved with
|
|
||||||
* gst_app_sink_pull_buffer_list() either from this callback
|
|
||||||
* or from any other thread.
|
* or from any other thread.
|
||||||
*
|
*
|
||||||
* A set of callbacks that can be installed on the appsink with
|
* A set of callbacks that can be installed on the appsink with
|
||||||
|
@ -71,8 +66,7 @@ typedef struct _GstAppSinkPrivate GstAppSinkPrivate;
|
||||||
typedef struct {
|
typedef struct {
|
||||||
void (*eos) (GstAppSink *sink, gpointer user_data);
|
void (*eos) (GstAppSink *sink, gpointer user_data);
|
||||||
GstFlowReturn (*new_preroll) (GstAppSink *sink, gpointer user_data);
|
GstFlowReturn (*new_preroll) (GstAppSink *sink, gpointer user_data);
|
||||||
GstFlowReturn (*new_buffer) (GstAppSink *sink, gpointer user_data);
|
GstFlowReturn (*new_sample) (GstAppSink *sink, gpointer user_data);
|
||||||
GstFlowReturn (*new_buffer_list) (GstAppSink *sink, gpointer user_data);
|
|
||||||
|
|
||||||
/*< private >*/
|
/*< private >*/
|
||||||
gpointer _gst_reserved[GST_PADDING];
|
gpointer _gst_reserved[GST_PADDING];
|
||||||
|
@ -96,13 +90,11 @@ struct _GstAppSinkClass
|
||||||
/* signals */
|
/* signals */
|
||||||
void (*eos) (GstAppSink *sink);
|
void (*eos) (GstAppSink *sink);
|
||||||
void (*new_preroll) (GstAppSink *sink);
|
void (*new_preroll) (GstAppSink *sink);
|
||||||
void (*new_buffer) (GstAppSink *sink);
|
void (*new_sample) (GstAppSink *sink);
|
||||||
void (*new_buffer_list) (GstAppSink *sink);
|
|
||||||
|
|
||||||
/* actions */
|
/* actions */
|
||||||
GstBuffer * (*pull_preroll) (GstAppSink *sink);
|
GstSample * (*pull_preroll) (GstAppSink *sink);
|
||||||
GstBuffer * (*pull_buffer) (GstAppSink *sink);
|
GstSample * (*pull_sample) (GstAppSink *sink);
|
||||||
GstBufferList * (*pull_buffer_list) (GstAppSink *sink);
|
|
||||||
|
|
||||||
/*< private >*/
|
/*< private >*/
|
||||||
gpointer _gst_reserved[GST_PADDING];
|
gpointer _gst_reserved[GST_PADDING];
|
||||||
|
@ -124,9 +116,8 @@ guint gst_app_sink_get_max_buffers (GstAppSink *appsink);
|
||||||
void gst_app_sink_set_drop (GstAppSink *appsink, gboolean drop);
|
void gst_app_sink_set_drop (GstAppSink *appsink, gboolean drop);
|
||||||
gboolean gst_app_sink_get_drop (GstAppSink *appsink);
|
gboolean gst_app_sink_get_drop (GstAppSink *appsink);
|
||||||
|
|
||||||
GstBuffer * gst_app_sink_pull_preroll (GstAppSink *appsink);
|
GstSample * gst_app_sink_pull_preroll (GstAppSink *appsink);
|
||||||
GstBuffer * gst_app_sink_pull_buffer (GstAppSink *appsink);
|
GstSample * gst_app_sink_pull_sample (GstAppSink *appsink);
|
||||||
GstBufferList * gst_app_sink_pull_buffer_list (GstAppSink *appsink);
|
|
||||||
|
|
||||||
void gst_app_sink_set_callbacks (GstAppSink * appsink,
|
void gst_app_sink_set_callbacks (GstAppSink * appsink,
|
||||||
GstAppSinkCallbacks *callbacks,
|
GstAppSinkCallbacks *callbacks,
|
||||||
|
|
|
@ -318,7 +318,7 @@ gst_audio_base_sink_init (GstAudioBaseSink * audiobasesink)
|
||||||
basesink->can_activate_push = TRUE;
|
basesink->can_activate_push = TRUE;
|
||||||
basesink->can_activate_pull = DEFAULT_CAN_ACTIVATE_PULL;
|
basesink->can_activate_pull = DEFAULT_CAN_ACTIVATE_PULL;
|
||||||
|
|
||||||
gst_base_sink_set_last_buffer_enabled (basesink, FALSE);
|
gst_base_sink_set_last_sample_enabled (basesink, FALSE);
|
||||||
if (DEFAULT_PROVIDE_CLOCK)
|
if (DEFAULT_PROVIDE_CLOCK)
|
||||||
GST_OBJECT_FLAG_SET (basesink, GST_ELEMENT_FLAG_PROVIDE_CLOCK);
|
GST_OBJECT_FLAG_SET (basesink, GST_ELEMENT_FLAG_PROVIDE_CLOCK);
|
||||||
else
|
else
|
||||||
|
|
|
@ -144,13 +144,15 @@ build_convert_frame_pipeline (GstElement ** src_element,
|
||||||
goto link_failed;
|
goto link_failed;
|
||||||
|
|
||||||
GST_DEBUG ("linking csp->vscale");
|
GST_DEBUG ("linking csp->vscale");
|
||||||
if (!gst_element_link_pads (csp, "src", vscale, "sink"))
|
if (!gst_element_link_pads_full (csp, "src", vscale, "sink",
|
||||||
|
GST_PAD_LINK_CHECK_NOTHING))
|
||||||
goto link_failed;
|
goto link_failed;
|
||||||
|
|
||||||
if (caps_are_raw (to_caps)) {
|
if (caps_are_raw (to_caps)) {
|
||||||
GST_DEBUG ("linking vscale->sink");
|
GST_DEBUG ("linking vscale->sink");
|
||||||
|
|
||||||
if (!gst_element_link_pads (vscale, "src", sink, "sink"))
|
if (!gst_element_link_pads_full (vscale, "src", sink, "sink",
|
||||||
|
GST_PAD_LINK_CHECK_NOTHING))
|
||||||
goto link_failed;
|
goto link_failed;
|
||||||
} else {
|
} else {
|
||||||
encoder = get_encoder (to_caps, &error);
|
encoder = get_encoder (to_caps, &error);
|
||||||
|
@ -230,9 +232,8 @@ link_failed:
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* gst_video_convert_frame:
|
* gst_video_convert_sample:
|
||||||
* @buf: a #GstBuffer
|
* @sample: a #GstSample
|
||||||
* @from_caps: the #GstCaps to convert from
|
|
||||||
* @to_caps: the #GstCaps to convert to
|
* @to_caps: the #GstCaps to convert to
|
||||||
* @timeout: the maximum amount of time allowed for the processing.
|
* @timeout: the maximum amount of time allowed for the processing.
|
||||||
* @error: pointer to a #GError. Can be %NULL.
|
* @error: pointer to a #GError. Can be %NULL.
|
||||||
|
@ -243,29 +244,36 @@ link_failed:
|
||||||
*
|
*
|
||||||
* The width, height and pixel-aspect-ratio can also be specified in the output caps.
|
* The width, height and pixel-aspect-ratio can also be specified in the output caps.
|
||||||
*
|
*
|
||||||
* Returns: The converted #GstBuffer, or %NULL if an error happened (in which case @err
|
* Returns: The converted #GstSample, or %NULL if an error happened (in which case @err
|
||||||
* will point to the #GError).
|
* will point to the #GError).
|
||||||
*
|
*
|
||||||
* Since: 0.10.31
|
* Since: 0.10.31
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
GstBuffer *
|
GstSample *
|
||||||
gst_video_convert_frame (GstBuffer * buf, GstCaps * from_caps,
|
gst_video_convert_sample (GstSample * sample, const GstCaps * to_caps,
|
||||||
const GstCaps * to_caps, GstClockTime timeout, GError ** error)
|
GstClockTime timeout, GError ** error)
|
||||||
{
|
{
|
||||||
GstMessage *msg;
|
GstMessage *msg;
|
||||||
GstBuffer *result = NULL;
|
GstBuffer *buf;
|
||||||
|
GstSample *result = NULL;
|
||||||
GError *err = NULL;
|
GError *err = NULL;
|
||||||
GstBus *bus;
|
GstBus *bus;
|
||||||
GstCaps *to_caps_copy = NULL;
|
GstCaps *from_caps, *to_caps_copy = NULL;
|
||||||
GstFlowReturn ret;
|
GstFlowReturn ret;
|
||||||
GstElement *pipeline, *src, *sink;
|
GstElement *pipeline, *src, *sink;
|
||||||
guint i, n;
|
guint i, n;
|
||||||
|
|
||||||
g_return_val_if_fail (buf != NULL, NULL);
|
g_return_val_if_fail (sample != NULL, NULL);
|
||||||
g_return_val_if_fail (to_caps != NULL, NULL);
|
g_return_val_if_fail (to_caps != NULL, NULL);
|
||||||
|
|
||||||
|
buf = gst_sample_get_buffer (sample);
|
||||||
|
g_return_val_if_fail (buf != NULL, NULL);
|
||||||
|
|
||||||
|
from_caps = gst_sample_get_caps (sample);
|
||||||
g_return_val_if_fail (from_caps != NULL, NULL);
|
g_return_val_if_fail (from_caps != NULL, NULL);
|
||||||
|
|
||||||
|
|
||||||
to_caps_copy = gst_caps_new_empty ();
|
to_caps_copy = gst_caps_new_empty ();
|
||||||
n = gst_caps_get_size (to_caps);
|
n = gst_caps_get_size (to_caps);
|
||||||
for (i = 0; i < n; i++) {
|
for (i = 0; i < n; i++) {
|
||||||
|
@ -364,28 +372,30 @@ typedef struct
|
||||||
{
|
{
|
||||||
GMutex *mutex;
|
GMutex *mutex;
|
||||||
GstElement *pipeline;
|
GstElement *pipeline;
|
||||||
GstVideoConvertFrameCallback callback;
|
GstVideoConvertSampleCallback callback;
|
||||||
gpointer user_data;
|
gpointer user_data;
|
||||||
GDestroyNotify destroy_notify;
|
GDestroyNotify destroy_notify;
|
||||||
GMainContext *context;
|
GMainContext *context;
|
||||||
GstBuffer *buffer;
|
GstSample *sample;
|
||||||
|
//GstBuffer *buffer;
|
||||||
gulong timeout_id;
|
gulong timeout_id;
|
||||||
gboolean finished;
|
gboolean finished;
|
||||||
} GstVideoConvertFrameContext;
|
} GstVideoConvertSampleContext;
|
||||||
|
|
||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
GstVideoConvertFrameCallback callback;
|
GstVideoConvertSampleCallback callback;
|
||||||
GstBuffer *buffer;
|
GstSample *sample;
|
||||||
|
//GstBuffer *buffer;
|
||||||
GError *error;
|
GError *error;
|
||||||
gpointer user_data;
|
gpointer user_data;
|
||||||
GDestroyNotify destroy_notify;
|
GDestroyNotify destroy_notify;
|
||||||
|
|
||||||
GstVideoConvertFrameContext *context;
|
GstVideoConvertSampleContext *context;
|
||||||
} GstVideoConvertFrameCallbackContext;
|
} GstVideoConvertSampleCallbackContext;
|
||||||
|
|
||||||
static void
|
static void
|
||||||
gst_video_convert_frame_context_free (GstVideoConvertFrameContext * ctx)
|
gst_video_convert_frame_context_free (GstVideoConvertSampleContext * ctx)
|
||||||
{
|
{
|
||||||
/* Wait until all users of the mutex are done */
|
/* Wait until all users of the mutex are done */
|
||||||
g_mutex_lock (ctx->mutex);
|
g_mutex_lock (ctx->mutex);
|
||||||
|
@ -393,29 +403,31 @@ gst_video_convert_frame_context_free (GstVideoConvertFrameContext * ctx)
|
||||||
g_mutex_free (ctx->mutex);
|
g_mutex_free (ctx->mutex);
|
||||||
if (ctx->timeout_id)
|
if (ctx->timeout_id)
|
||||||
g_source_remove (ctx->timeout_id);
|
g_source_remove (ctx->timeout_id);
|
||||||
if (ctx->buffer)
|
//if (ctx->buffer)
|
||||||
gst_buffer_unref (ctx->buffer);
|
// gst_buffer_unref (ctx->buffer);
|
||||||
|
if (ctx->sample)
|
||||||
|
gst_sample_unref (ctx->sample);
|
||||||
g_main_context_unref (ctx->context);
|
g_main_context_unref (ctx->context);
|
||||||
|
|
||||||
gst_element_set_state (ctx->pipeline, GST_STATE_NULL);
|
gst_element_set_state (ctx->pipeline, GST_STATE_NULL);
|
||||||
gst_object_unref (ctx->pipeline);
|
gst_object_unref (ctx->pipeline);
|
||||||
|
|
||||||
g_slice_free (GstVideoConvertFrameContext, ctx);
|
g_slice_free (GstVideoConvertSampleContext, ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
gst_video_convert_frame_callback_context_free
|
gst_video_convert_frame_callback_context_free
|
||||||
(GstVideoConvertFrameCallbackContext * ctx)
|
(GstVideoConvertSampleCallbackContext * ctx)
|
||||||
{
|
{
|
||||||
if (ctx->context)
|
if (ctx->context)
|
||||||
gst_video_convert_frame_context_free (ctx->context);
|
gst_video_convert_frame_context_free (ctx->context);
|
||||||
g_slice_free (GstVideoConvertFrameCallbackContext, ctx);
|
g_slice_free (GstVideoConvertSampleCallbackContext, ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
static gboolean
|
static gboolean
|
||||||
convert_frame_dispatch_callback (GstVideoConvertFrameCallbackContext * ctx)
|
convert_frame_dispatch_callback (GstVideoConvertSampleCallbackContext * ctx)
|
||||||
{
|
{
|
||||||
ctx->callback (ctx->buffer, ctx->error, ctx->user_data);
|
ctx->callback (ctx->sample, ctx->error, ctx->user_data);
|
||||||
|
|
||||||
if (ctx->destroy_notify)
|
if (ctx->destroy_notify)
|
||||||
ctx->destroy_notify (ctx->user_data);
|
ctx->destroy_notify (ctx->user_data);
|
||||||
|
@ -424,21 +436,22 @@ convert_frame_dispatch_callback (GstVideoConvertFrameCallbackContext * ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
convert_frame_finish (GstVideoConvertFrameContext * context, GstBuffer * buffer,
|
convert_frame_finish (GstVideoConvertSampleContext * context,
|
||||||
GError * error)
|
GstSample * sample, GError * error)
|
||||||
{
|
{
|
||||||
GSource *source;
|
GSource *source;
|
||||||
GstVideoConvertFrameCallbackContext *ctx;
|
GstVideoConvertSampleCallbackContext *ctx;
|
||||||
|
|
||||||
if (context->timeout_id)
|
if (context->timeout_id)
|
||||||
g_source_remove (context->timeout_id);
|
g_source_remove (context->timeout_id);
|
||||||
context->timeout_id = 0;
|
context->timeout_id = 0;
|
||||||
|
|
||||||
ctx = g_slice_new (GstVideoConvertFrameCallbackContext);
|
ctx = g_slice_new (GstVideoConvertSampleCallbackContext);
|
||||||
ctx->callback = context->callback;
|
ctx->callback = context->callback;
|
||||||
ctx->user_data = context->user_data;
|
ctx->user_data = context->user_data;
|
||||||
ctx->destroy_notify = context->destroy_notify;
|
ctx->destroy_notify = context->destroy_notify;
|
||||||
ctx->buffer = buffer;
|
ctx->sample = sample;
|
||||||
|
//ctx->buffer = buffer;
|
||||||
ctx->error = error;
|
ctx->error = error;
|
||||||
ctx->context = context;
|
ctx->context = context;
|
||||||
|
|
||||||
|
@ -453,7 +466,7 @@ convert_frame_finish (GstVideoConvertFrameContext * context, GstBuffer * buffer,
|
||||||
}
|
}
|
||||||
|
|
||||||
static gboolean
|
static gboolean
|
||||||
convert_frame_timeout_callback (GstVideoConvertFrameContext * context)
|
convert_frame_timeout_callback (GstVideoConvertSampleContext * context)
|
||||||
{
|
{
|
||||||
GError *error;
|
GError *error;
|
||||||
|
|
||||||
|
@ -476,7 +489,7 @@ done:
|
||||||
|
|
||||||
static gboolean
|
static gboolean
|
||||||
convert_frame_bus_callback (GstBus * bus, GstMessage * message,
|
convert_frame_bus_callback (GstBus * bus, GstMessage * message,
|
||||||
GstVideoConvertFrameContext * context)
|
GstVideoConvertSampleContext * context)
|
||||||
{
|
{
|
||||||
g_mutex_lock (context->mutex);
|
g_mutex_lock (context->mutex);
|
||||||
|
|
||||||
|
@ -510,19 +523,21 @@ done:
|
||||||
|
|
||||||
static void
|
static void
|
||||||
convert_frame_need_data_callback (GstElement * src, guint size,
|
convert_frame_need_data_callback (GstElement * src, guint size,
|
||||||
GstVideoConvertFrameContext * context)
|
GstVideoConvertSampleContext * context)
|
||||||
{
|
{
|
||||||
GstFlowReturn ret = GST_FLOW_ERROR;
|
GstFlowReturn ret = GST_FLOW_ERROR;
|
||||||
GError *error;
|
GError *error;
|
||||||
|
GstBuffer *buffer;
|
||||||
|
|
||||||
g_mutex_lock (context->mutex);
|
g_mutex_lock (context->mutex);
|
||||||
|
|
||||||
if (context->finished)
|
if (context->finished)
|
||||||
goto done;
|
goto done;
|
||||||
|
|
||||||
g_signal_emit_by_name (src, "push-buffer", context->buffer, &ret);
|
buffer = gst_sample_get_buffer (context->sample);
|
||||||
gst_buffer_unref (context->buffer);
|
g_signal_emit_by_name (src, "push-buffer", buffer, &ret);
|
||||||
context->buffer = NULL;
|
gst_sample_unref (context->sample);
|
||||||
|
context->sample = NULL;
|
||||||
|
|
||||||
if (ret != GST_FLOW_OK) {
|
if (ret != GST_FLOW_OK) {
|
||||||
GST_ERROR ("Could not push video frame: %s", gst_flow_get_name (ret));
|
GST_ERROR ("Could not push video frame: %s", gst_flow_get_name (ret));
|
||||||
|
@ -541,10 +556,10 @@ done:
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
convert_frame_new_buffer_callback (GstElement * sink,
|
convert_frame_new_preroll_callback (GstElement * sink,
|
||||||
GstVideoConvertFrameContext * context)
|
GstVideoConvertSampleContext * context)
|
||||||
{
|
{
|
||||||
GstBuffer *buf = NULL;
|
GstSample *sample = NULL;
|
||||||
GError *error = NULL;
|
GError *error = NULL;
|
||||||
|
|
||||||
g_mutex_lock (context->mutex);
|
g_mutex_lock (context->mutex);
|
||||||
|
@ -552,14 +567,13 @@ convert_frame_new_buffer_callback (GstElement * sink,
|
||||||
if (context->finished)
|
if (context->finished)
|
||||||
goto done;
|
goto done;
|
||||||
|
|
||||||
g_signal_emit_by_name (sink, "pull-preroll", &buf);
|
g_signal_emit_by_name (sink, "pull-preroll", &sample);
|
||||||
|
|
||||||
if (!buf) {
|
if (!sample) {
|
||||||
error = g_error_new (GST_CORE_ERROR, GST_CORE_ERROR_FAILED,
|
error = g_error_new (GST_CORE_ERROR, GST_CORE_ERROR_FAILED,
|
||||||
"Could not get converted video frame");
|
"Could not get converted video sample");
|
||||||
}
|
}
|
||||||
|
convert_frame_finish (context, sample, error);
|
||||||
convert_frame_finish (context, buf, error);
|
|
||||||
|
|
||||||
g_signal_handlers_disconnect_by_func (sink, convert_frame_need_data_callback,
|
g_signal_handlers_disconnect_by_func (sink, convert_frame_need_data_callback,
|
||||||
context);
|
context);
|
||||||
|
@ -569,12 +583,11 @@ done:
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* gst_video_convert_frame_async:
|
* gst_video_convert_sample_async:
|
||||||
* @buf: a #GstBuffer
|
* @sample: a #GstSample
|
||||||
* @from_caps: the #GstCaps to convert from
|
|
||||||
* @to_caps: the #GstCaps to convert to
|
* @to_caps: the #GstCaps to convert to
|
||||||
* @timeout: the maximum amount of time allowed for the processing.
|
* @timeout: the maximum amount of time allowed for the processing.
|
||||||
* @callback: %GstVideoConvertFrameCallback that will be called after conversion.
|
* @callback: %GstVideoConvertSampleCallback that will be called after conversion.
|
||||||
* @user_data: extra data that will be passed to the @callback
|
* @user_data: extra data that will be passed to the @callback
|
||||||
* @destroy_notify: %GDestroyNotify to be called after @user_data is not needed anymore
|
* @destroy_notify: %GDestroyNotify to be called after @user_data is not needed anymore
|
||||||
*
|
*
|
||||||
|
@ -596,22 +609,28 @@ done:
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
gst_video_convert_frame_async (GstBuffer * buf, GstCaps * from_caps,
|
gst_video_convert_sample_async (GstSample * sample,
|
||||||
const GstCaps * to_caps, GstClockTime timeout,
|
const GstCaps * to_caps, GstClockTime timeout,
|
||||||
GstVideoConvertFrameCallback callback, gpointer user_data,
|
GstVideoConvertSampleCallback callback, gpointer user_data,
|
||||||
GDestroyNotify destroy_notify)
|
GDestroyNotify destroy_notify)
|
||||||
{
|
{
|
||||||
GMainContext *context = NULL;
|
GMainContext *context = NULL;
|
||||||
GError *error = NULL;
|
GError *error = NULL;
|
||||||
GstBus *bus;
|
GstBus *bus;
|
||||||
GstCaps *to_caps_copy = NULL;
|
GstBuffer *buf;
|
||||||
|
GstCaps *from_caps, *to_caps_copy = NULL;
|
||||||
GstElement *pipeline, *src, *sink;
|
GstElement *pipeline, *src, *sink;
|
||||||
guint i, n;
|
guint i, n;
|
||||||
GSource *source;
|
GSource *source;
|
||||||
GstVideoConvertFrameContext *ctx;
|
GstVideoConvertSampleContext *ctx;
|
||||||
|
|
||||||
|
g_return_if_fail (sample != NULL);
|
||||||
|
buf = gst_sample_get_buffer (sample);
|
||||||
g_return_if_fail (buf != NULL);
|
g_return_if_fail (buf != NULL);
|
||||||
|
|
||||||
g_return_if_fail (to_caps != NULL);
|
g_return_if_fail (to_caps != NULL);
|
||||||
|
|
||||||
|
from_caps = gst_sample_get_caps (sample);
|
||||||
g_return_if_fail (from_caps != NULL);
|
g_return_if_fail (from_caps != NULL);
|
||||||
g_return_if_fail (callback != NULL);
|
g_return_if_fail (callback != NULL);
|
||||||
|
|
||||||
|
@ -638,9 +657,10 @@ gst_video_convert_frame_async (GstBuffer * buf, GstCaps * from_caps,
|
||||||
|
|
||||||
bus = gst_element_get_bus (pipeline);
|
bus = gst_element_get_bus (pipeline);
|
||||||
|
|
||||||
ctx = g_slice_new0 (GstVideoConvertFrameContext);
|
ctx = g_slice_new0 (GstVideoConvertSampleContext);
|
||||||
ctx->mutex = g_mutex_new ();
|
ctx->mutex = g_mutex_new ();
|
||||||
ctx->buffer = gst_buffer_ref (buf);
|
//ctx->buffer = gst_buffer_ref (buf);
|
||||||
|
ctx->sample = gst_sample_ref (sample);
|
||||||
ctx->callback = callback;
|
ctx->callback = callback;
|
||||||
ctx->user_data = user_data;
|
ctx->user_data = user_data;
|
||||||
ctx->destroy_notify = destroy_notify;
|
ctx->destroy_notify = destroy_notify;
|
||||||
|
@ -659,7 +679,7 @@ gst_video_convert_frame_async (GstBuffer * buf, GstCaps * from_caps,
|
||||||
g_signal_connect (src, "need-data",
|
g_signal_connect (src, "need-data",
|
||||||
G_CALLBACK (convert_frame_need_data_callback), ctx);
|
G_CALLBACK (convert_frame_need_data_callback), ctx);
|
||||||
g_signal_connect (sink, "new-preroll",
|
g_signal_connect (sink, "new-preroll",
|
||||||
G_CALLBACK (convert_frame_new_buffer_callback), ctx);
|
G_CALLBACK (convert_frame_new_preroll_callback), ctx);
|
||||||
|
|
||||||
source = gst_bus_create_watch (bus);
|
source = gst_bus_create_watch (bus);
|
||||||
g_source_set_callback (source, (GSourceFunc) convert_frame_bus_callback,
|
g_source_set_callback (source, (GSourceFunc) convert_frame_bus_callback,
|
||||||
|
@ -676,16 +696,16 @@ gst_video_convert_frame_async (GstBuffer * buf, GstCaps * from_caps,
|
||||||
/* ERRORS */
|
/* ERRORS */
|
||||||
no_pipeline:
|
no_pipeline:
|
||||||
{
|
{
|
||||||
GstVideoConvertFrameCallbackContext *ctx;
|
GstVideoConvertSampleCallbackContext *ctx;
|
||||||
GSource *source;
|
GSource *source;
|
||||||
|
|
||||||
gst_caps_unref (to_caps_copy);
|
gst_caps_unref (to_caps_copy);
|
||||||
|
|
||||||
ctx = g_slice_new0 (GstVideoConvertFrameCallbackContext);
|
ctx = g_slice_new0 (GstVideoConvertSampleCallbackContext);
|
||||||
ctx->callback = callback;
|
ctx->callback = callback;
|
||||||
ctx->user_data = user_data;
|
ctx->user_data = user_data;
|
||||||
ctx->destroy_notify = destroy_notify;
|
ctx->destroy_notify = destroy_notify;
|
||||||
ctx->buffer = NULL;
|
ctx->sample = NULL;
|
||||||
ctx->error = error;
|
ctx->error = error;
|
||||||
|
|
||||||
source = g_timeout_source_new (0);
|
source = g_timeout_source_new (0);
|
||||||
|
|
|
@ -705,20 +705,18 @@ GstEvent * gst_video_event_new_still_frame (gboolean in_still);
|
||||||
gboolean gst_video_event_parse_still_frame (GstEvent * event, gboolean * in_still);
|
gboolean gst_video_event_parse_still_frame (GstEvent * event, gboolean * in_still);
|
||||||
|
|
||||||
|
|
||||||
/* convert/encode video frame from one format to another */
|
/* convert/encode video sample from one format to another */
|
||||||
|
|
||||||
typedef void (*GstVideoConvertFrameCallback) (GstBuffer * buf, GError *error, gpointer user_data);
|
typedef void (*GstVideoConvertSampleCallback) (GstSample * sample, GError *error, gpointer user_data);
|
||||||
|
|
||||||
void gst_video_convert_frame_async (GstBuffer * buf,
|
void gst_video_convert_sample_async (GstSample * sample,
|
||||||
GstCaps * from_caps,
|
|
||||||
const GstCaps * to_caps,
|
const GstCaps * to_caps,
|
||||||
GstClockTime timeout,
|
GstClockTime timeout,
|
||||||
GstVideoConvertFrameCallback callback,
|
GstVideoConvertSampleCallback callback,
|
||||||
gpointer user_data,
|
gpointer user_data,
|
||||||
GDestroyNotify destroy_notify);
|
GDestroyNotify destroy_notify);
|
||||||
|
|
||||||
GstBuffer * gst_video_convert_frame (GstBuffer * buf,
|
GstSample * gst_video_convert_sample (GstSample * sample,
|
||||||
GstCaps * from_caps,
|
|
||||||
const GstCaps * to_caps,
|
const GstCaps * to_caps,
|
||||||
GstClockTime timeout,
|
GstClockTime timeout,
|
||||||
GError ** error);
|
GError ** error);
|
||||||
|
|
|
@ -44,7 +44,7 @@
|
||||||
* meta info (tag) extraction
|
* meta info (tag) extraction
|
||||||
* </listitem>
|
* </listitem>
|
||||||
* <listitem>
|
* <listitem>
|
||||||
* easy access to the last video frame
|
* easy access to the last video sample
|
||||||
* </listitem>
|
* </listitem>
|
||||||
* <listitem>
|
* <listitem>
|
||||||
* buffering when playing streams over a network
|
* buffering when playing streams over a network
|
||||||
|
@ -436,8 +436,8 @@ struct _GstPlayBinClass
|
||||||
GstTagList *(*get_audio_tags) (GstPlayBin * playbin, gint stream);
|
GstTagList *(*get_audio_tags) (GstPlayBin * playbin, gint stream);
|
||||||
GstTagList *(*get_text_tags) (GstPlayBin * playbin, gint stream);
|
GstTagList *(*get_text_tags) (GstPlayBin * playbin, gint stream);
|
||||||
|
|
||||||
/* get the last video frame and convert it to the given caps */
|
/* get the last video sample and convert it to the given caps */
|
||||||
GstBuffer *(*convert_frame) (GstPlayBin * playbin, GstCaps * caps);
|
GstSample *(*convert_sample) (GstPlayBin * playbin, GstCaps * caps);
|
||||||
|
|
||||||
/* get audio/video/text pad for a stream */
|
/* get audio/video/text pad for a stream */
|
||||||
GstPad *(*get_video_pad) (GstPlayBin * playbin, gint stream);
|
GstPad *(*get_video_pad) (GstPlayBin * playbin, gint stream);
|
||||||
|
@ -491,7 +491,7 @@ enum
|
||||||
PROP_TEXT_SINK,
|
PROP_TEXT_SINK,
|
||||||
PROP_VOLUME,
|
PROP_VOLUME,
|
||||||
PROP_MUTE,
|
PROP_MUTE,
|
||||||
PROP_FRAME,
|
PROP_SAMPLE,
|
||||||
PROP_FONT_DESC,
|
PROP_FONT_DESC,
|
||||||
PROP_CONNECTION_SPEED,
|
PROP_CONNECTION_SPEED,
|
||||||
PROP_BUFFER_SIZE,
|
PROP_BUFFER_SIZE,
|
||||||
|
@ -505,7 +505,7 @@ enum
|
||||||
enum
|
enum
|
||||||
{
|
{
|
||||||
SIGNAL_ABOUT_TO_FINISH,
|
SIGNAL_ABOUT_TO_FINISH,
|
||||||
SIGNAL_CONVERT_FRAME,
|
SIGNAL_CONVERT_SAMPLE,
|
||||||
SIGNAL_VIDEO_CHANGED,
|
SIGNAL_VIDEO_CHANGED,
|
||||||
SIGNAL_AUDIO_CHANGED,
|
SIGNAL_AUDIO_CHANGED,
|
||||||
SIGNAL_TEXT_CHANGED,
|
SIGNAL_TEXT_CHANGED,
|
||||||
|
@ -544,7 +544,7 @@ static GstTagList *gst_play_bin_get_audio_tags (GstPlayBin * playbin,
|
||||||
static GstTagList *gst_play_bin_get_text_tags (GstPlayBin * playbin,
|
static GstTagList *gst_play_bin_get_text_tags (GstPlayBin * playbin,
|
||||||
gint stream);
|
gint stream);
|
||||||
|
|
||||||
static GstBuffer *gst_play_bin_convert_frame (GstPlayBin * playbin,
|
static GstSample *gst_play_bin_convert_sample (GstPlayBin * playbin,
|
||||||
GstCaps * caps);
|
GstCaps * caps);
|
||||||
|
|
||||||
static GstPad *gst_play_bin_get_video_pad (GstPlayBin * playbin, gint stream);
|
static GstPad *gst_play_bin_get_video_pad (GstPlayBin * playbin, gint stream);
|
||||||
|
@ -756,16 +756,17 @@ gst_play_bin_class_init (GstPlayBinClass * klass)
|
||||||
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* GstPlayBin:frame:
|
* GstPlayBin:sample:
|
||||||
* @playbin: a #GstPlayBin
|
* @playbin: a #GstPlayBin
|
||||||
*
|
*
|
||||||
* Get the currently rendered or prerolled frame in the video sink.
|
* Get the currently rendered or prerolled sample in the video sink.
|
||||||
* The #GstCaps on the buffer will describe the format of the buffer.
|
* The #GstCaps in the sample will describe the format of the buffer.
|
||||||
*/
|
*/
|
||||||
g_object_class_install_property (gobject_klass, PROP_FRAME,
|
g_object_class_install_property (gobject_klass, PROP_SAMPLE,
|
||||||
g_param_spec_boxed ("frame", "Frame",
|
g_param_spec_boxed ("sample", "Sample",
|
||||||
"The last frame (NULL = no video available)",
|
"The last sample (NULL = no video available)",
|
||||||
GST_TYPE_BUFFER, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
|
GST_TYPE_SAMPLE, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
|
||||||
|
|
||||||
g_object_class_install_property (gobject_klass, PROP_FONT_DESC,
|
g_object_class_install_property (gobject_klass, PROP_FONT_DESC,
|
||||||
g_param_spec_string ("subtitle-font-desc",
|
g_param_spec_string ("subtitle-font-desc",
|
||||||
"Subtitle font description",
|
"Subtitle font description",
|
||||||
|
@ -1019,7 +1020,7 @@ gst_play_bin_class_init (GstPlayBinClass * klass)
|
||||||
G_STRUCT_OFFSET (GstPlayBinClass, get_text_tags), NULL, NULL,
|
G_STRUCT_OFFSET (GstPlayBinClass, get_text_tags), NULL, NULL,
|
||||||
gst_play_marshal_BOXED__INT, GST_TYPE_TAG_LIST, 1, G_TYPE_INT);
|
gst_play_marshal_BOXED__INT, GST_TYPE_TAG_LIST, 1, G_TYPE_INT);
|
||||||
/**
|
/**
|
||||||
* GstPlayBin::convert-frame
|
* GstPlayBin::convert-sample
|
||||||
* @playbin: a #GstPlayBin
|
* @playbin: a #GstPlayBin
|
||||||
* @caps: the target format of the frame
|
* @caps: the target format of the frame
|
||||||
*
|
*
|
||||||
|
@ -1033,11 +1034,11 @@ gst_play_bin_class_init (GstPlayBinClass * klass)
|
||||||
* %NULL is returned when no current buffer can be retrieved or when the
|
* %NULL is returned when no current buffer can be retrieved or when the
|
||||||
* conversion failed.
|
* conversion failed.
|
||||||
*/
|
*/
|
||||||
gst_play_bin_signals[SIGNAL_CONVERT_FRAME] =
|
gst_play_bin_signals[SIGNAL_CONVERT_SAMPLE] =
|
||||||
g_signal_new ("convert-frame", G_TYPE_FROM_CLASS (klass),
|
g_signal_new ("convert-sample", G_TYPE_FROM_CLASS (klass),
|
||||||
G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
|
G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
|
||||||
G_STRUCT_OFFSET (GstPlayBinClass, convert_frame), NULL, NULL,
|
G_STRUCT_OFFSET (GstPlayBinClass, convert_sample), NULL, NULL,
|
||||||
gst_play_marshal_BUFFER__BOXED, GST_TYPE_BUFFER, 1, GST_TYPE_CAPS);
|
gst_play_marshal_SAMPLE__BOXED, GST_TYPE_SAMPLE, 1, GST_TYPE_CAPS);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* GstPlayBin::get-video-pad
|
* GstPlayBin::get-video-pad
|
||||||
|
@ -1095,7 +1096,7 @@ gst_play_bin_class_init (GstPlayBinClass * klass)
|
||||||
klass->get_audio_tags = gst_play_bin_get_audio_tags;
|
klass->get_audio_tags = gst_play_bin_get_audio_tags;
|
||||||
klass->get_text_tags = gst_play_bin_get_text_tags;
|
klass->get_text_tags = gst_play_bin_get_text_tags;
|
||||||
|
|
||||||
klass->convert_frame = gst_play_bin_convert_frame;
|
klass->convert_sample = gst_play_bin_convert_sample;
|
||||||
|
|
||||||
klass->get_video_pad = gst_play_bin_get_video_pad;
|
klass->get_video_pad = gst_play_bin_get_video_pad;
|
||||||
klass->get_audio_pad = gst_play_bin_get_audio_pad;
|
klass->get_audio_pad = gst_play_bin_get_audio_pad;
|
||||||
|
@ -1524,10 +1525,10 @@ gst_play_bin_get_text_tags (GstPlayBin * playbin, gint stream)
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
static GstBuffer *
|
static GstSample *
|
||||||
gst_play_bin_convert_frame (GstPlayBin * playbin, GstCaps * caps)
|
gst_play_bin_convert_sample (GstPlayBin * playbin, GstCaps * caps)
|
||||||
{
|
{
|
||||||
return gst_play_sink_convert_frame (playbin->playsink, caps);
|
return gst_play_sink_convert_sample (playbin->playsink, caps);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Returns current stream number, or -1 if none has been selected yet */
|
/* Returns current stream number, or -1 if none has been selected yet */
|
||||||
|
@ -2104,9 +2105,9 @@ gst_play_bin_get_property (GObject * object, guint prop_id, GValue * value,
|
||||||
case PROP_MUTE:
|
case PROP_MUTE:
|
||||||
g_value_set_boolean (value, gst_play_sink_get_mute (playbin->playsink));
|
g_value_set_boolean (value, gst_play_sink_get_mute (playbin->playsink));
|
||||||
break;
|
break;
|
||||||
case PROP_FRAME:
|
case PROP_SAMPLE:
|
||||||
gst_value_take_buffer (value,
|
gst_value_take_sample (value,
|
||||||
gst_play_sink_get_last_frame (playbin->playsink));
|
gst_play_sink_get_last_sample (playbin->playsink));
|
||||||
break;
|
break;
|
||||||
case PROP_FONT_DESC:
|
case PROP_FONT_DESC:
|
||||||
g_value_take_string (value,
|
g_value_take_string (value,
|
||||||
|
|
|
@ -210,7 +210,7 @@ struct _GstPlaySinkClass
|
||||||
|
|
||||||
gboolean (*reconfigure) (GstPlaySink * playsink);
|
gboolean (*reconfigure) (GstPlaySink * playsink);
|
||||||
|
|
||||||
GstBuffer *(*convert_frame) (GstPlaySink * playsink, GstCaps * caps);
|
GstSample *(*convert_sample) (GstPlaySink * playsink, GstCaps * caps);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -252,7 +252,7 @@ enum
|
||||||
PROP_FONT_DESC,
|
PROP_FONT_DESC,
|
||||||
PROP_SUBTITLE_ENCODING,
|
PROP_SUBTITLE_ENCODING,
|
||||||
PROP_VIS_PLUGIN,
|
PROP_VIS_PLUGIN,
|
||||||
PROP_FRAME,
|
PROP_SAMPLE,
|
||||||
PROP_AV_OFFSET,
|
PROP_AV_OFFSET,
|
||||||
PROP_VIDEO_SINK,
|
PROP_VIDEO_SINK,
|
||||||
PROP_AUDIO_SINK,
|
PROP_AUDIO_SINK,
|
||||||
|
@ -292,18 +292,18 @@ static void notify_mute_cb (GObject * object, GParamSpec * pspec,
|
||||||
static void update_av_offset (GstPlaySink * playsink);
|
static void update_av_offset (GstPlaySink * playsink);
|
||||||
|
|
||||||
void
|
void
|
||||||
gst_play_marshal_BUFFER__BOXED (GClosure * closure,
|
gst_play_marshal_SAMPLE__BOXED (GClosure * closure,
|
||||||
GValue * return_value G_GNUC_UNUSED,
|
GValue * return_value G_GNUC_UNUSED,
|
||||||
guint n_param_values,
|
guint n_param_values,
|
||||||
const GValue * param_values,
|
const GValue * param_values,
|
||||||
gpointer invocation_hint G_GNUC_UNUSED, gpointer marshal_data)
|
gpointer invocation_hint G_GNUC_UNUSED, gpointer marshal_data)
|
||||||
{
|
{
|
||||||
typedef GstBuffer *(*GMarshalFunc_OBJECT__BOXED) (gpointer data1,
|
typedef GstSample *(*GMarshalFunc_OBJECT__BOXED) (gpointer data1,
|
||||||
gpointer arg_1, gpointer data2);
|
gpointer arg_1, gpointer data2);
|
||||||
register GMarshalFunc_OBJECT__BOXED callback;
|
register GMarshalFunc_OBJECT__BOXED callback;
|
||||||
register GCClosure *cc = (GCClosure *) closure;
|
register GCClosure *cc = (GCClosure *) closure;
|
||||||
register gpointer data1, data2;
|
register gpointer data1, data2;
|
||||||
GstBuffer *v_return;
|
GstSample *v_return;
|
||||||
g_return_if_fail (return_value != NULL);
|
g_return_if_fail (return_value != NULL);
|
||||||
g_return_if_fail (n_param_values == 2);
|
g_return_if_fail (n_param_values == 2);
|
||||||
|
|
||||||
|
@ -319,7 +319,7 @@ gst_play_marshal_BUFFER__BOXED (GClosure * closure,
|
||||||
|
|
||||||
v_return = callback (data1, g_value_get_boxed (param_values + 1), data2);
|
v_return = callback (data1, g_value_get_boxed (param_values + 1), data2);
|
||||||
|
|
||||||
gst_value_take_buffer (return_value, v_return);
|
gst_value_take_sample (return_value, v_return);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* static guint gst_play_sink_signals[LAST_SIGNAL] = { 0 }; */
|
/* static guint gst_play_sink_signals[LAST_SIGNAL] = { 0 }; */
|
||||||
|
@ -386,17 +386,15 @@ gst_play_sink_class_init (GstPlaySinkClass * klass)
|
||||||
"the visualization element to use (NULL = default)",
|
"the visualization element to use (NULL = default)",
|
||||||
GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
||||||
/**
|
/**
|
||||||
* GstPlaySink:frame:
|
* GstPlaySink:sample:
|
||||||
*
|
*
|
||||||
* Get the currently rendered or prerolled frame in the video sink.
|
* Get the currently rendered or prerolled sample in the video sink.
|
||||||
* The #GstCaps on the buffer will describe the format of the buffer.
|
* The #GstCaps in the sample will describe the format of the buffer.
|
||||||
*
|
|
||||||
* Since: 0.10.30
|
|
||||||
*/
|
*/
|
||||||
g_object_class_install_property (gobject_klass, PROP_FRAME,
|
g_object_class_install_property (gobject_klass, PROP_SAMPLE,
|
||||||
g_param_spec_boxed ("frame", "Frame",
|
g_param_spec_boxed ("sample", "Sample",
|
||||||
"The last frame (NULL = no video available)",
|
"The last sample (NULL = no video available)",
|
||||||
GST_TYPE_BUFFER, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
|
GST_TYPE_SAMPLE, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
|
||||||
/**
|
/**
|
||||||
* GstPlaySink:av-offset:
|
* GstPlaySink:av-offset:
|
||||||
*
|
*
|
||||||
|
@ -455,26 +453,24 @@ gst_play_sink_class_init (GstPlaySinkClass * klass)
|
||||||
reconfigure), NULL, NULL, gst_marshal_BOOLEAN__VOID, G_TYPE_BOOLEAN,
|
reconfigure), NULL, NULL, gst_marshal_BOOLEAN__VOID, G_TYPE_BOOLEAN,
|
||||||
0, G_TYPE_NONE);
|
0, G_TYPE_NONE);
|
||||||
/**
|
/**
|
||||||
* GstPlaySink::convert-frame
|
* GstPlaySink::convert-sample
|
||||||
* @playsink: a #GstPlaySink
|
* @playsink: a #GstPlaySink
|
||||||
* @caps: the target format of the frame
|
* @caps: the target format of the sample
|
||||||
*
|
*
|
||||||
* Action signal to retrieve the currently playing video frame in the format
|
* Action signal to retrieve the currently playing video sample in the format
|
||||||
* specified by @caps.
|
* specified by @caps.
|
||||||
* If @caps is %NULL, no conversion will be performed and this function is
|
* If @caps is %NULL, no conversion will be performed and this function is
|
||||||
* equivalent to the #GstPlaySink::frame property.
|
* equivalent to the #GstPlaySink::sample property.
|
||||||
*
|
*
|
||||||
* Returns: a #GstBuffer of the current video frame converted to #caps.
|
* Returns: a #GstSample of the current video sample converted to #caps.
|
||||||
* The caps on the buffer will describe the final layout of the buffer data.
|
* The caps in the sample will describe the final layout of the buffer data.
|
||||||
* %NULL is returned when no current buffer can be retrieved or when the
|
* %NULL is returned when no current sample can be retrieved or when the
|
||||||
* conversion failed.
|
* conversion failed.
|
||||||
*
|
|
||||||
* Since: 0.10.30
|
|
||||||
*/
|
*/
|
||||||
g_signal_new ("convert-frame", G_TYPE_FROM_CLASS (klass),
|
g_signal_new ("convert-sample", G_TYPE_FROM_CLASS (klass),
|
||||||
G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
|
G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
|
||||||
G_STRUCT_OFFSET (GstPlaySinkClass, convert_frame), NULL, NULL,
|
G_STRUCT_OFFSET (GstPlaySinkClass, convert_sample), NULL, NULL,
|
||||||
gst_play_marshal_BUFFER__BOXED, GST_TYPE_BUFFER, 1, GST_TYPE_CAPS);
|
gst_play_marshal_SAMPLE__BOXED, GST_TYPE_SAMPLE, 1, GST_TYPE_CAPS);
|
||||||
|
|
||||||
gst_element_class_add_pad_template (gstelement_klass,
|
gst_element_class_add_pad_template (gstelement_klass,
|
||||||
gst_static_pad_template_get (&audiorawtemplate));
|
gst_static_pad_template_get (&audiorawtemplate));
|
||||||
|
@ -503,7 +499,7 @@ gst_play_sink_class_init (GstPlaySinkClass * klass)
|
||||||
GST_DEBUG_FUNCPTR (gst_play_sink_handle_message);
|
GST_DEBUG_FUNCPTR (gst_play_sink_handle_message);
|
||||||
|
|
||||||
klass->reconfigure = GST_DEBUG_FUNCPTR (gst_play_sink_reconfigure);
|
klass->reconfigure = GST_DEBUG_FUNCPTR (gst_play_sink_reconfigure);
|
||||||
klass->convert_frame = GST_DEBUG_FUNCPTR (gst_play_sink_convert_frame);
|
klass->convert_sample = GST_DEBUG_FUNCPTR (gst_play_sink_convert_sample);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
@ -2797,24 +2793,24 @@ gst_play_sink_get_av_offset (GstPlaySink * playsink)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* gst_play_sink_get_last_frame:
|
* gst_play_sink_get_last_sample:
|
||||||
* @playsink: a #GstPlaySink
|
* @playsink: a #GstPlaySink
|
||||||
*
|
*
|
||||||
* Get the last displayed frame from @playsink. This frame is in the native
|
* Get the last displayed sample from @playsink. This sample is in the native
|
||||||
* format of the sink element, the caps on the result buffer contain the format
|
* format of the sink element, the caps in the result sample contain the format
|
||||||
* of the frame data.
|
* of the frame data.
|
||||||
*
|
*
|
||||||
* Returns: a #GstBuffer with the frame data or %NULL when no video frame is
|
* Returns: a #GstSample with the frame data or %NULL when no video frame is
|
||||||
* available.
|
* available.
|
||||||
*/
|
*/
|
||||||
GstBuffer *
|
GstSample *
|
||||||
gst_play_sink_get_last_frame (GstPlaySink * playsink)
|
gst_play_sink_get_last_sample (GstPlaySink * playsink)
|
||||||
{
|
{
|
||||||
GstBuffer *result = NULL;
|
GstSample *result = NULL;
|
||||||
GstPlayVideoChain *chain;
|
GstPlayVideoChain *chain;
|
||||||
|
|
||||||
GST_PLAY_SINK_LOCK (playsink);
|
GST_PLAY_SINK_LOCK (playsink);
|
||||||
GST_DEBUG_OBJECT (playsink, "taking last frame");
|
GST_DEBUG_OBJECT (playsink, "taking last sample");
|
||||||
/* get the video chain if we can */
|
/* get the video chain if we can */
|
||||||
if ((chain = (GstPlayVideoChain *) playsink->videochain)) {
|
if ((chain = (GstPlayVideoChain *) playsink->videochain)) {
|
||||||
GST_DEBUG_OBJECT (playsink, "found video chain");
|
GST_DEBUG_OBJECT (playsink, "found video chain");
|
||||||
|
@ -2827,9 +2823,9 @@ gst_play_sink_get_last_frame (GstPlaySink * playsink)
|
||||||
/* find and get the last-buffer property now */
|
/* find and get the last-buffer property now */
|
||||||
if ((elem =
|
if ((elem =
|
||||||
gst_play_sink_find_property (playsink, chain->sink,
|
gst_play_sink_find_property (playsink, chain->sink,
|
||||||
"last-buffer", GST_TYPE_BUFFER))) {
|
"last-sample", GST_TYPE_SAMPLE))) {
|
||||||
GST_DEBUG_OBJECT (playsink, "getting last-buffer property");
|
GST_DEBUG_OBJECT (playsink, "getting last-sample property");
|
||||||
g_object_get (elem, "last-buffer", &result, NULL);
|
g_object_get (elem, "last-sample", &result, NULL);
|
||||||
gst_object_unref (elem);
|
gst_object_unref (elem);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2840,7 +2836,7 @@ gst_play_sink_get_last_frame (GstPlaySink * playsink)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* gst_play_sink_convert_frame:
|
* gst_play_sink_convert_sample:
|
||||||
* @playsink: a #GstPlaySink
|
* @playsink: a #GstPlaySink
|
||||||
* @caps: a #GstCaps
|
* @caps: a #GstCaps
|
||||||
*
|
*
|
||||||
|
@ -2852,28 +2848,36 @@ gst_play_sink_get_last_frame (GstPlaySink * playsink)
|
||||||
* Returns: a #GstBuffer with the frame data or %NULL when no video frame is
|
* Returns: a #GstBuffer with the frame data or %NULL when no video frame is
|
||||||
* available or when the conversion failed.
|
* available or when the conversion failed.
|
||||||
*/
|
*/
|
||||||
GstBuffer *
|
GstSample *
|
||||||
gst_play_sink_convert_frame (GstPlaySink * playsink, GstCaps * caps)
|
gst_play_sink_convert_sample (GstPlaySink * playsink, GstCaps * caps)
|
||||||
{
|
{
|
||||||
GstBuffer *result;
|
GstSample *result;
|
||||||
|
|
||||||
result = gst_play_sink_get_last_frame (playsink);
|
|
||||||
if (result != NULL && caps != NULL) {
|
|
||||||
GstBuffer *temp;
|
|
||||||
GError *err = NULL;
|
GError *err = NULL;
|
||||||
|
|
||||||
/* FIXME, need to get the input buffer caps */
|
result = gst_play_sink_get_last_sample (playsink);
|
||||||
temp = gst_video_convert_frame (result, NULL, caps, 25 * GST_SECOND, &err);
|
if (result != NULL && caps != NULL) {
|
||||||
gst_buffer_unref (result);
|
GstSample *temp;
|
||||||
if (temp == NULL && err) {
|
|
||||||
|
temp = gst_video_convert_sample (result, caps, 25 * GST_SECOND, &err);
|
||||||
|
if (temp == NULL && err)
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
gst_sample_unref (result);
|
||||||
|
result = temp;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
|
||||||
|
/* ERRORS */
|
||||||
|
error:
|
||||||
|
{
|
||||||
/* I'm really uncertain whether we should make playsink post an error
|
/* I'm really uncertain whether we should make playsink post an error
|
||||||
* on the bus or not. It's not like it's a critical issue regarding
|
* on the bus or not. It's not like it's a critical issue regarding
|
||||||
* playsink behaviour. */
|
* playsink behaviour. */
|
||||||
GST_ERROR ("Error converting frame: %s", err->message);
|
GST_ERROR ("Error converting frame: %s", err->message);
|
||||||
|
gst_sample_unref (result);
|
||||||
|
g_error_free (err);
|
||||||
|
return NULL;
|
||||||
}
|
}
|
||||||
result = temp;
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static gboolean
|
static gboolean
|
||||||
|
@ -3700,8 +3704,8 @@ gst_play_sink_get_property (GObject * object, guint prop_id,
|
||||||
case PROP_VIS_PLUGIN:
|
case PROP_VIS_PLUGIN:
|
||||||
g_value_take_object (value, gst_play_sink_get_vis_plugin (playsink));
|
g_value_take_object (value, gst_play_sink_get_vis_plugin (playsink));
|
||||||
break;
|
break;
|
||||||
case PROP_FRAME:
|
case PROP_SAMPLE:
|
||||||
gst_value_take_buffer (value, gst_play_sink_get_last_frame (playsink));
|
gst_value_take_sample (value, gst_play_sink_get_last_sample (playsink));
|
||||||
break;
|
break;
|
||||||
case PROP_AV_OFFSET:
|
case PROP_AV_OFFSET:
|
||||||
g_value_set_int64 (value, gst_play_sink_get_av_offset (playsink));
|
g_value_set_int64 (value, gst_play_sink_get_av_offset (playsink));
|
||||||
|
|
|
@ -94,15 +94,15 @@ gchar * gst_play_sink_get_subtitle_encoding (GstPlaySink *playsink);
|
||||||
void gst_play_sink_set_av_offset (GstPlaySink *playsink, gint64 av_offset);
|
void gst_play_sink_set_av_offset (GstPlaySink *playsink, gint64 av_offset);
|
||||||
gint64 gst_play_sink_get_av_offset (GstPlaySink *playsink);
|
gint64 gst_play_sink_get_av_offset (GstPlaySink *playsink);
|
||||||
|
|
||||||
GstBuffer * gst_play_sink_get_last_frame (GstPlaySink * playsink);
|
GstSample * gst_play_sink_get_last_sample (GstPlaySink * playsink);
|
||||||
GstBuffer * gst_play_sink_convert_frame (GstPlaySink * playsink, GstCaps * caps);
|
GstSample * gst_play_sink_convert_sample (GstPlaySink * playsink, GstCaps * caps);
|
||||||
|
|
||||||
gboolean gst_play_sink_reconfigure (GstPlaySink * playsink);
|
gboolean gst_play_sink_reconfigure (GstPlaySink * playsink);
|
||||||
|
|
||||||
gboolean gst_play_sink_plugin_init (GstPlugin * plugin);
|
gboolean gst_play_sink_plugin_init (GstPlugin * plugin);
|
||||||
|
|
||||||
void
|
void
|
||||||
gst_play_marshal_BUFFER__BOXED (GClosure * closure,
|
gst_play_marshal_SAMPLE__BOXED (GClosure * closure,
|
||||||
GValue * return_value G_GNUC_UNUSED,
|
GValue * return_value G_GNUC_UNUSED,
|
||||||
guint n_param_values,
|
guint n_param_values,
|
||||||
const GValue * param_values,
|
const GValue * param_values,
|
||||||
|
|
|
@ -667,7 +667,8 @@ GST_START_TEST (test_convert_frame)
|
||||||
{
|
{
|
||||||
GstVideoInfo vinfo;
|
GstVideoInfo vinfo;
|
||||||
GstCaps *from_caps, *to_caps;
|
GstCaps *from_caps, *to_caps;
|
||||||
GstBuffer *from_buffer, *to_buffer;
|
GstBuffer *from_buffer;
|
||||||
|
GstSample *from_sample, *to_sample;
|
||||||
GError *error = NULL;
|
GError *error = NULL;
|
||||||
gint i;
|
gint i;
|
||||||
guint8 *data;
|
guint8 *data;
|
||||||
|
@ -693,14 +694,16 @@ GST_START_TEST (test_convert_frame)
|
||||||
vinfo.par_d = 1;
|
vinfo.par_d = 1;
|
||||||
from_caps = gst_video_info_to_caps (&vinfo);
|
from_caps = gst_video_info_to_caps (&vinfo);
|
||||||
|
|
||||||
|
from_sample = gst_sample_new (from_buffer, from_caps, NULL, NULL);
|
||||||
|
|
||||||
to_caps =
|
to_caps =
|
||||||
gst_caps_from_string
|
gst_caps_from_string
|
||||||
("something/that, does=(string)not, exist=(boolean)FALSE");
|
("something/that, does=(string)not, exist=(boolean)FALSE");
|
||||||
|
|
||||||
to_buffer =
|
to_sample =
|
||||||
gst_video_convert_frame (from_buffer, from_caps, to_caps,
|
gst_video_convert_sample (from_sample, to_caps,
|
||||||
GST_CLOCK_TIME_NONE, &error);
|
GST_CLOCK_TIME_NONE, &error);
|
||||||
fail_if (to_buffer != NULL);
|
fail_if (to_sample != NULL);
|
||||||
fail_unless (error != NULL);
|
fail_unless (error != NULL);
|
||||||
g_error_free (error);
|
g_error_free (error);
|
||||||
error = NULL;
|
error = NULL;
|
||||||
|
@ -713,15 +716,16 @@ GST_START_TEST (test_convert_frame)
|
||||||
vinfo.par_d = 2;
|
vinfo.par_d = 2;
|
||||||
to_caps = gst_video_info_to_caps (&vinfo);
|
to_caps = gst_video_info_to_caps (&vinfo);
|
||||||
|
|
||||||
to_buffer =
|
to_sample =
|
||||||
gst_video_convert_frame (from_buffer, from_caps, to_caps,
|
gst_video_convert_sample (from_sample, to_caps,
|
||||||
GST_CLOCK_TIME_NONE, &error);
|
GST_CLOCK_TIME_NONE, &error);
|
||||||
fail_unless (to_buffer != NULL);
|
fail_unless (to_sample != NULL);
|
||||||
fail_unless (error == NULL);
|
fail_unless (error == NULL);
|
||||||
|
|
||||||
gst_buffer_unref (from_buffer);
|
gst_buffer_unref (from_buffer);
|
||||||
gst_caps_unref (from_caps);
|
gst_caps_unref (from_caps);
|
||||||
gst_buffer_unref (to_buffer);
|
gst_sample_unref (from_sample);
|
||||||
|
gst_sample_unref (to_sample);
|
||||||
gst_caps_unref (to_caps);
|
gst_caps_unref (to_caps);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -730,15 +734,15 @@ GST_END_TEST;
|
||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
GMainLoop *loop;
|
GMainLoop *loop;
|
||||||
GstBuffer *buffer;
|
GstSample *sample;
|
||||||
GError *error;
|
GError *error;
|
||||||
} ConvertFrameContext;
|
} ConvertFrameContext;
|
||||||
|
|
||||||
static void
|
static void
|
||||||
convert_frame_async_callback (GstBuffer * buf, GError * err,
|
convert_sample_async_callback (GstSample * sample, GError * err,
|
||||||
ConvertFrameContext * cf_data)
|
ConvertFrameContext * cf_data)
|
||||||
{
|
{
|
||||||
cf_data->buffer = buf;
|
cf_data->sample = sample;
|
||||||
cf_data->error = err;
|
cf_data->error = err;
|
||||||
|
|
||||||
g_main_loop_quit (cf_data->loop);
|
g_main_loop_quit (cf_data->loop);
|
||||||
|
@ -749,6 +753,7 @@ GST_START_TEST (test_convert_frame_async)
|
||||||
GstVideoInfo vinfo;
|
GstVideoInfo vinfo;
|
||||||
GstCaps *from_caps, *to_caps;
|
GstCaps *from_caps, *to_caps;
|
||||||
GstBuffer *from_buffer;
|
GstBuffer *from_buffer;
|
||||||
|
GstSample *from_sample;
|
||||||
gint i;
|
gint i;
|
||||||
guint8 *data;
|
guint8 *data;
|
||||||
GMainLoop *loop;
|
GMainLoop *loop;
|
||||||
|
@ -781,14 +786,18 @@ GST_START_TEST (test_convert_frame_async)
|
||||||
|
|
||||||
loop = cf_data.loop = g_main_loop_new (NULL, FALSE);
|
loop = cf_data.loop = g_main_loop_new (NULL, FALSE);
|
||||||
|
|
||||||
gst_video_convert_frame_async (from_buffer, from_caps, to_caps,
|
from_sample = gst_sample_new (from_buffer, from_caps, NULL, NULL);
|
||||||
|
gst_buffer_unref (from_buffer);
|
||||||
|
gst_caps_unref (from_caps);
|
||||||
|
|
||||||
|
gst_video_convert_sample_async (from_sample, to_caps,
|
||||||
GST_CLOCK_TIME_NONE,
|
GST_CLOCK_TIME_NONE,
|
||||||
(GstVideoConvertFrameCallback) convert_frame_async_callback, &cf_data,
|
(GstVideoConvertSampleCallback) convert_sample_async_callback, &cf_data,
|
||||||
NULL);
|
NULL);
|
||||||
|
|
||||||
g_main_loop_run (loop);
|
g_main_loop_run (loop);
|
||||||
|
|
||||||
fail_if (cf_data.buffer != NULL);
|
fail_if (cf_data.sample != NULL);
|
||||||
fail_unless (cf_data.error != NULL);
|
fail_unless (cf_data.error != NULL);
|
||||||
g_error_free (cf_data.error);
|
g_error_free (cf_data.error);
|
||||||
cf_data.error = NULL;
|
cf_data.error = NULL;
|
||||||
|
@ -801,17 +810,15 @@ GST_START_TEST (test_convert_frame_async)
|
||||||
vinfo.fps_n = 25;
|
vinfo.fps_n = 25;
|
||||||
vinfo.fps_d = 1;
|
vinfo.fps_d = 1;
|
||||||
to_caps = gst_video_info_to_caps (&vinfo);
|
to_caps = gst_video_info_to_caps (&vinfo);
|
||||||
gst_video_convert_frame_async (from_buffer, from_caps, to_caps,
|
gst_video_convert_sample_async (from_sample, to_caps,
|
||||||
GST_CLOCK_TIME_NONE,
|
GST_CLOCK_TIME_NONE,
|
||||||
(GstVideoConvertFrameCallback) convert_frame_async_callback, &cf_data,
|
(GstVideoConvertSampleCallback) convert_sample_async_callback, &cf_data,
|
||||||
NULL);
|
NULL);
|
||||||
g_main_loop_run (loop);
|
g_main_loop_run (loop);
|
||||||
fail_unless (cf_data.buffer != NULL);
|
fail_unless (cf_data.sample != NULL);
|
||||||
fail_unless (cf_data.error == NULL);
|
fail_unless (cf_data.error == NULL);
|
||||||
|
|
||||||
gst_buffer_unref (from_buffer);
|
gst_sample_unref (cf_data.sample);
|
||||||
gst_caps_unref (from_caps);
|
|
||||||
gst_buffer_unref (cf_data.buffer);
|
|
||||||
gst_caps_unref (to_caps);
|
gst_caps_unref (to_caps);
|
||||||
|
|
||||||
g_main_loop_unref (loop);
|
g_main_loop_unref (loop);
|
||||||
|
|
|
@ -19,25 +19,21 @@ typedef struct
|
||||||
/* called when the appsink notifies us that there is a new buffer ready for
|
/* called when the appsink notifies us that there is a new buffer ready for
|
||||||
* processing */
|
* processing */
|
||||||
static void
|
static void
|
||||||
on_new_buffer_from_source (GstElement * elt, ProgramData * data)
|
on_new_sample_from_sink (GstElement * elt, ProgramData * data)
|
||||||
{
|
{
|
||||||
guint size;
|
GstSample *sample;
|
||||||
GstBuffer *app_buffer, *buffer;
|
GstBuffer *app_buffer, *buffer;
|
||||||
GstElement *source;
|
GstElement *source;
|
||||||
|
|
||||||
/* get the buffer from appsink */
|
/* get the sample from appsink */
|
||||||
buffer = gst_app_sink_pull_buffer (GST_APP_SINK (elt));
|
sample = gst_app_sink_pull_sample (GST_APP_SINK (elt));
|
||||||
|
buffer = gst_sample_get_buffer (sample);
|
||||||
|
|
||||||
/* turn it into an app buffer, it's not really needed, we could simply push
|
/* make a copy */
|
||||||
* the retrieved buffer from appsink into appsrc just fine. */
|
app_buffer = gst_buffer_copy (buffer);
|
||||||
size = gst_buffer_get_size (buffer);
|
|
||||||
g_print ("Pushing a buffer of size %d\n", size);
|
|
||||||
app_buffer = gst_buffer_new_and_alloc (size);
|
|
||||||
|
|
||||||
gst_buffer_copy_into (app_buffer, buffer, GST_BUFFER_COPY_MEMORY, 0, size);
|
/* we don't need the appsink sample anymore */
|
||||||
|
gst_sample_unref (sample);
|
||||||
/* we don't need the appsink buffer anymore */
|
|
||||||
gst_buffer_unref (buffer);
|
|
||||||
|
|
||||||
/* get source an push new buffer */
|
/* get source an push new buffer */
|
||||||
source = gst_bin_get_by_name (GST_BIN (data->sink), "testsource");
|
source = gst_bin_get_by_name (GST_BIN (data->sink), "testsource");
|
||||||
|
@ -134,8 +130,8 @@ main (int argc, char *argv[])
|
||||||
* push as fast as it can, hence the sync=false */
|
* push as fast as it can, hence the sync=false */
|
||||||
testsink = gst_bin_get_by_name (GST_BIN (data->source), "testsink");
|
testsink = gst_bin_get_by_name (GST_BIN (data->source), "testsink");
|
||||||
g_object_set (G_OBJECT (testsink), "emit-signals", TRUE, "sync", FALSE, NULL);
|
g_object_set (G_OBJECT (testsink), "emit-signals", TRUE, "sync", FALSE, NULL);
|
||||||
g_signal_connect (testsink, "new-buffer",
|
g_signal_connect (testsink, "new-sample",
|
||||||
G_CALLBACK (on_new_buffer_from_source), data);
|
G_CALLBACK (on_new_sample_from_sink), data);
|
||||||
gst_object_unref (testsink);
|
gst_object_unref (testsink);
|
||||||
|
|
||||||
/* setting up sink pipeline, we push audio data into this pipeline that will
|
/* setting up sink pipeline, we push audio data into this pipeline that will
|
||||||
|
|
|
@ -71,14 +71,14 @@ main (int argc, char *argv[])
|
||||||
/* _is_eos() does not block and returns TRUE if there is not currently an EOS
|
/* _is_eos() does not block and returns TRUE if there is not currently an EOS
|
||||||
* to be retrieved */
|
* to be retrieved */
|
||||||
while (!gst_app_sink_is_eos (GST_APP_SINK (app->sink))) {
|
while (!gst_app_sink_is_eos (GST_APP_SINK (app->sink))) {
|
||||||
GstBuffer *buf;
|
GstSample *sample;
|
||||||
|
|
||||||
/* pull the next item, this can return NULL when there is no more data and
|
/* pull the next item, this can return NULL when there is no more data and
|
||||||
* EOS has been received */
|
* EOS has been received */
|
||||||
buf = gst_app_sink_pull_buffer (GST_APP_SINK (app->sink));
|
sample = gst_app_sink_pull_sample (GST_APP_SINK (app->sink));
|
||||||
printf ("retrieved buffer %p\n", buf);
|
printf ("retrieved sample %p\n", sample);
|
||||||
if (buf)
|
if (sample)
|
||||||
gst_buffer_unref (buf);
|
gst_sample_unref (sample);
|
||||||
}
|
}
|
||||||
gst_element_set_state (app->pipe, GST_STATE_NULL);
|
gst_element_set_state (app->pipe, GST_STATE_NULL);
|
||||||
|
|
||||||
|
|
|
@ -1143,26 +1143,24 @@ volume_notify_cb (GstElement * pipeline, GParamSpec * arg, gpointer user_dat)
|
||||||
static void
|
static void
|
||||||
shot_cb (GtkButton * button, gpointer data)
|
shot_cb (GtkButton * button, gpointer data)
|
||||||
{
|
{
|
||||||
GstBuffer *buffer;
|
GstSample *sample = NULL;
|
||||||
GstCaps *caps;
|
GstCaps *caps;
|
||||||
|
|
||||||
|
GST_DEBUG ("taking snapshot");
|
||||||
|
|
||||||
/* convert to our desired format (RGB24) */
|
/* convert to our desired format (RGB24) */
|
||||||
caps = gst_caps_new_simple ("video/x-raw-rgb",
|
caps = gst_caps_new_simple ("video/x-raw", "format", G_TYPE_STRING, "RGB",
|
||||||
"bpp", G_TYPE_INT, 24, "depth", G_TYPE_INT, 24,
|
|
||||||
/* Note: we don't ask for a specific width/height here, so that
|
/* Note: we don't ask for a specific width/height here, so that
|
||||||
* videoscale can adjust dimensions from a non-1/1 pixel aspect
|
* videoscale can adjust dimensions from a non-1/1 pixel aspect
|
||||||
* ratio to a 1/1 pixel-aspect-ratio */
|
* ratio to a 1/1 pixel-aspect-ratio */
|
||||||
"pixel-aspect-ratio", GST_TYPE_FRACTION, 1, 1,
|
"pixel-aspect-ratio", GST_TYPE_FRACTION, 1, 1, NULL);
|
||||||
"endianness", G_TYPE_INT, G_BIG_ENDIAN,
|
|
||||||
"red_mask", G_TYPE_INT, 0xff0000,
|
|
||||||
"green_mask", G_TYPE_INT, 0x00ff00,
|
|
||||||
"blue_mask", G_TYPE_INT, 0x0000ff, NULL);
|
|
||||||
|
|
||||||
/* convert the latest frame to the requested format */
|
/* convert the latest sample to the requested format */
|
||||||
g_signal_emit_by_name (pipeline, "convert-frame", caps, &buffer);
|
g_signal_emit_by_name (pipeline, "convert-sample", caps, &sample);
|
||||||
gst_caps_unref (caps);
|
gst_caps_unref (caps);
|
||||||
|
|
||||||
if (buffer) {
|
if (sample) {
|
||||||
|
GstBuffer *buffer;
|
||||||
GstCaps *caps;
|
GstCaps *caps;
|
||||||
GstStructure *s;
|
GstStructure *s;
|
||||||
gboolean res;
|
gboolean res;
|
||||||
|
@ -1176,11 +1174,7 @@ shot_cb (GtkButton * button, gpointer data)
|
||||||
* that it can only be an rgb buffer. The only thing we have not specified
|
* that it can only be an rgb buffer. The only thing we have not specified
|
||||||
* on the caps is the height, which is dependant on the pixel-aspect-ratio
|
* on the caps is the height, which is dependant on the pixel-aspect-ratio
|
||||||
* of the source material */
|
* of the source material */
|
||||||
#if 0
|
caps = gst_sample_get_caps (sample);
|
||||||
caps = GST_BUFFER_CAPS (buffer);
|
|
||||||
#endif
|
|
||||||
/* FIXME, need to get the caps of the buffer somehow */
|
|
||||||
caps = NULL;
|
|
||||||
if (!caps) {
|
if (!caps) {
|
||||||
g_warning ("could not get snapshot format\n");
|
g_warning ("could not get snapshot format\n");
|
||||||
goto done;
|
goto done;
|
||||||
|
@ -1197,6 +1191,7 @@ shot_cb (GtkButton * button, gpointer data)
|
||||||
|
|
||||||
/* create pixmap from buffer and save, gstreamer video buffers have a stride
|
/* create pixmap from buffer and save, gstreamer video buffers have a stride
|
||||||
* that is rounded up to the nearest multiple of 4 */
|
* that is rounded up to the nearest multiple of 4 */
|
||||||
|
buffer = gst_sample_get_buffer (sample);
|
||||||
data = gst_buffer_map (buffer, &size, NULL, GST_MAP_READ);
|
data = gst_buffer_map (buffer, &size, NULL, GST_MAP_READ);
|
||||||
pixbuf = gdk_pixbuf_new_from_data (data,
|
pixbuf = gdk_pixbuf_new_from_data (data,
|
||||||
GDK_COLORSPACE_RGB, FALSE, 8, width, height,
|
GDK_COLORSPACE_RGB, FALSE, 8, width, height,
|
||||||
|
@ -1207,7 +1202,7 @@ shot_cb (GtkButton * button, gpointer data)
|
||||||
gst_buffer_unmap (buffer, data, size);
|
gst_buffer_unmap (buffer, data, size);
|
||||||
|
|
||||||
done:
|
done:
|
||||||
gst_buffer_unref (buffer);
|
gst_sample_unref (sample);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue