mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2025-02-14 10:15:48 +00:00
Merge branch 'master' into 0.11
Conflicts: ext/celt/gstceltdec.c ext/opus/gstopusdec.c ext/opus/gstopusdec.h ext/opus/gstopusenc.c ext/opus/gstopusenc.h ext/opus/gstopusparse.c
This commit is contained in:
commit
bc6ed0bf97
26 changed files with 1157 additions and 1590 deletions
|
@ -1,12 +1,13 @@
|
||||||
plugin_LTLIBRARIES = libgstcelt.la
|
plugin_LTLIBRARIES = libgstcelt.la
|
||||||
|
|
||||||
libgstcelt_la_SOURCES = gstcelt.c gstceltdec.c gstceltenc.c
|
libgstcelt_la_SOURCES = gstcelt.c gstceltdec.c gstceltenc.c
|
||||||
libgstcelt_la_CFLAGS = \
|
libgstcelt_la_CFLAGS = -DGST_USE_UNSTABLE_API \
|
||||||
$(GST_PLUGINS_BASE_CFLAGS) \
|
$(GST_PLUGINS_BASE_CFLAGS) \
|
||||||
$(GST_CFLAGS) \
|
$(GST_CFLAGS) \
|
||||||
$(CELT_CFLAGS)
|
$(CELT_CFLAGS)
|
||||||
libgstcelt_la_LIBADD = \
|
libgstcelt_la_LIBADD = \
|
||||||
$(GST_PLUGINS_BASE_LIBS) -lgsttag-$(GST_MAJORMINOR) \
|
$(GST_PLUGINS_BASE_LIBS) \
|
||||||
|
-lgstaudio-$(GST_MAJORMINOR) -lgsttag-$(GST_MAJORMINOR) \
|
||||||
$(GST_BASE_LIBS) \
|
$(GST_BASE_LIBS) \
|
||||||
$(GST_LIBS) \
|
$(GST_LIBS) \
|
||||||
$(CELT_LIBS)
|
$(CELT_LIBS)
|
||||||
|
|
|
@ -68,38 +68,26 @@ GST_STATIC_PAD_TEMPLATE ("sink",
|
||||||
);
|
);
|
||||||
|
|
||||||
#define gst_celt_dec_parent_class parent_class
|
#define gst_celt_dec_parent_class parent_class
|
||||||
G_DEFINE_TYPE (GstCeltDec, gst_celt_dec, GST_TYPE_ELEMENT);
|
G_DEFINE_TYPE (GstCeltDec, gst_celt_dec, GST_TYPE_AUDIO_DECODER);
|
||||||
|
|
||||||
static gboolean celt_dec_sink_event (GstPad * pad, GstEvent * event);
|
static gboolean gst_celt_dec_start (GstAudioDecoder * dec);
|
||||||
static GstFlowReturn celt_dec_chain (GstPad * pad, GstBuffer * buf);
|
static gboolean gst_celt_dec_stop (GstAudioDecoder * dec);
|
||||||
static gboolean celt_dec_sink_setcaps (GstPad * pad, GstCaps * caps);
|
static gboolean gst_celt_dec_set_format (GstAudioDecoder * bdec,
|
||||||
static GstStateChangeReturn celt_dec_change_state (GstElement * element,
|
GstCaps * caps);
|
||||||
GstStateChange transition);
|
static GstFlowReturn gst_celt_dec_handle_frame (GstAudioDecoder * dec,
|
||||||
|
GstBuffer * buffer);
|
||||||
static gboolean celt_dec_src_event (GstPad * pad, GstEvent * event);
|
|
||||||
static gboolean celt_dec_src_query (GstPad * pad, GstQuery * query);
|
|
||||||
static gboolean celt_dec_sink_query (GstPad * pad, GstQuery * query);
|
|
||||||
static const GstQueryType *celt_get_src_query_types (GstPad * pad);
|
|
||||||
static const GstQueryType *celt_get_sink_query_types (GstPad * pad);
|
|
||||||
static gboolean celt_dec_convert (GstPad * pad,
|
|
||||||
GstFormat src_format, gint64 src_value,
|
|
||||||
GstFormat * dest_format, gint64 * dest_value);
|
|
||||||
|
|
||||||
static GstFlowReturn celt_dec_chain_parse_data (GstCeltDec * dec,
|
|
||||||
GstBuffer * buf, GstClockTime timestamp, GstClockTime duration);
|
|
||||||
static GstFlowReturn celt_dec_chain_parse_header (GstCeltDec * dec,
|
|
||||||
GstBuffer * buf);
|
|
||||||
static GstFlowReturn celt_dec_chain_parse_comments (GstCeltDec * dec,
|
|
||||||
GstBuffer * buf);
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
gst_celt_dec_class_init (GstCeltDecClass * klass)
|
gst_celt_dec_class_init (GstCeltDecClass * klass)
|
||||||
{
|
{
|
||||||
GstElementClass *gstelement_class;
|
GstAudioDecoderClass *gstbase_class;
|
||||||
|
|
||||||
gstelement_class = (GstElementClass *) klass;
|
gstbase_class = (GstAudioDecoderClass *) klass;
|
||||||
|
|
||||||
gstelement_class->change_state = GST_DEBUG_FUNCPTR (celt_dec_change_state);
|
gstbase_class->start = GST_DEBUG_FUNCPTR (gst_celt_dec_start);
|
||||||
|
gstbase_class->stop = GST_DEBUG_FUNCPTR (gst_celt_dec_stop);
|
||||||
|
gstbase_class->set_format = GST_DEBUG_FUNCPTR (gst_celt_dec_set_format);
|
||||||
|
gstbase_class->handle_frame = GST_DEBUG_FUNCPTR (gst_celt_dec_handle_frame);
|
||||||
|
|
||||||
gst_element_class_add_pad_template (gstelement_class,
|
gst_element_class_add_pad_template (gstelement_class,
|
||||||
gst_static_pad_template_get (&celt_dec_src_factory));
|
gst_static_pad_template_get (&celt_dec_src_factory));
|
||||||
|
@ -118,11 +106,8 @@ gst_celt_dec_class_init (GstCeltDecClass * klass)
|
||||||
static void
|
static void
|
||||||
gst_celt_dec_reset (GstCeltDec * dec)
|
gst_celt_dec_reset (GstCeltDec * dec)
|
||||||
{
|
{
|
||||||
gst_segment_init (&dec->segment, GST_FORMAT_UNDEFINED);
|
|
||||||
dec->granulepos = -1;
|
|
||||||
dec->packetno = 0;
|
dec->packetno = 0;
|
||||||
dec->frame_size = 0;
|
dec->frame_size = 0;
|
||||||
dec->frame_duration = 0;
|
|
||||||
if (dec->state) {
|
if (dec->state) {
|
||||||
celt_decoder_destroy (dec->state);
|
celt_decoder_destroy (dec->state);
|
||||||
dec->state = NULL;
|
dec->state = NULL;
|
||||||
|
@ -145,411 +130,36 @@ gst_celt_dec_reset (GstCeltDec * dec)
|
||||||
static void
|
static void
|
||||||
gst_celt_dec_init (GstCeltDec * dec)
|
gst_celt_dec_init (GstCeltDec * dec)
|
||||||
{
|
{
|
||||||
dec->sinkpad =
|
|
||||||
gst_pad_new_from_static_template (&celt_dec_sink_factory, "sink");
|
|
||||||
gst_pad_set_chain_function (dec->sinkpad, GST_DEBUG_FUNCPTR (celt_dec_chain));
|
|
||||||
gst_pad_set_event_function (dec->sinkpad,
|
|
||||||
GST_DEBUG_FUNCPTR (celt_dec_sink_event));
|
|
||||||
gst_pad_set_query_type_function (dec->sinkpad,
|
|
||||||
GST_DEBUG_FUNCPTR (celt_get_sink_query_types));
|
|
||||||
gst_pad_set_query_function (dec->sinkpad,
|
|
||||||
GST_DEBUG_FUNCPTR (celt_dec_sink_query));
|
|
||||||
gst_pad_set_setcaps_function (dec->sinkpad,
|
|
||||||
GST_DEBUG_FUNCPTR (celt_dec_sink_setcaps));
|
|
||||||
gst_element_add_pad (GST_ELEMENT (dec), dec->sinkpad);
|
|
||||||
|
|
||||||
dec->srcpad = gst_pad_new_from_static_template (&celt_dec_src_factory, "src");
|
|
||||||
gst_pad_use_fixed_caps (dec->srcpad);
|
|
||||||
gst_pad_set_event_function (dec->srcpad,
|
|
||||||
GST_DEBUG_FUNCPTR (celt_dec_src_event));
|
|
||||||
gst_pad_set_query_type_function (dec->srcpad,
|
|
||||||
GST_DEBUG_FUNCPTR (celt_get_src_query_types));
|
|
||||||
gst_pad_set_query_function (dec->srcpad,
|
|
||||||
GST_DEBUG_FUNCPTR (celt_dec_src_query));
|
|
||||||
gst_element_add_pad (GST_ELEMENT (dec), dec->srcpad);
|
|
||||||
|
|
||||||
gst_celt_dec_reset (dec);
|
gst_celt_dec_reset (dec);
|
||||||
}
|
}
|
||||||
|
|
||||||
static gboolean
|
static gboolean
|
||||||
celt_dec_sink_setcaps (GstPad * pad, GstCaps * caps)
|
gst_celt_dec_start (GstAudioDecoder * dec)
|
||||||
{
|
{
|
||||||
GstCeltDec *dec = GST_CELT_DEC (gst_pad_get_parent (pad));
|
GstCeltDec *cd = GST_CELT_DEC (dec);
|
||||||
gboolean ret = TRUE;
|
|
||||||
GstStructure *s;
|
|
||||||
const GValue *streamheader;
|
|
||||||
|
|
||||||
s = gst_caps_get_structure (caps, 0);
|
GST_DEBUG_OBJECT (dec, "start");
|
||||||
if ((streamheader = gst_structure_get_value (s, "streamheader")) &&
|
gst_celt_dec_reset (cd);
|
||||||
G_VALUE_HOLDS (streamheader, GST_TYPE_ARRAY) &&
|
|
||||||
gst_value_array_get_size (streamheader) >= 2) {
|
|
||||||
const GValue *header, *vorbiscomment;
|
|
||||||
GstBuffer *buf;
|
|
||||||
GstFlowReturn res = GST_FLOW_OK;
|
|
||||||
|
|
||||||
header = gst_value_array_get_value (streamheader, 0);
|
/* we know about concealment */
|
||||||
if (header && G_VALUE_HOLDS (header, GST_TYPE_BUFFER)) {
|
gst_audio_decoder_set_plc_aware (dec, TRUE);
|
||||||
buf = gst_value_get_buffer (header);
|
|
||||||
res = celt_dec_chain_parse_header (dec, buf);
|
|
||||||
if (res != GST_FLOW_OK)
|
|
||||||
goto done;
|
|
||||||
gst_buffer_replace (&dec->streamheader, buf);
|
|
||||||
}
|
|
||||||
|
|
||||||
vorbiscomment = gst_value_array_get_value (streamheader, 1);
|
return TRUE;
|
||||||
if (vorbiscomment && G_VALUE_HOLDS (vorbiscomment, GST_TYPE_BUFFER)) {
|
|
||||||
buf = gst_value_get_buffer (vorbiscomment);
|
|
||||||
res = celt_dec_chain_parse_comments (dec, buf);
|
|
||||||
if (res != GST_FLOW_OK)
|
|
||||||
goto done;
|
|
||||||
gst_buffer_replace (&dec->vorbiscomment, buf);
|
|
||||||
}
|
|
||||||
|
|
||||||
g_list_foreach (dec->extra_headers, (GFunc) gst_mini_object_unref, NULL);
|
|
||||||
g_list_free (dec->extra_headers);
|
|
||||||
dec->extra_headers = NULL;
|
|
||||||
|
|
||||||
if (gst_value_array_get_size (streamheader) > 2) {
|
|
||||||
gint i, n;
|
|
||||||
|
|
||||||
n = gst_value_array_get_size (streamheader);
|
|
||||||
for (i = 2; i < n; i++) {
|
|
||||||
header = gst_value_array_get_value (streamheader, i);
|
|
||||||
buf = gst_value_get_buffer (header);
|
|
||||||
dec->extra_headers =
|
|
||||||
g_list_prepend (dec->extra_headers, gst_buffer_ref (buf));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
done:
|
|
||||||
gst_object_unref (dec);
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static gboolean
|
static gboolean
|
||||||
celt_dec_convert (GstPad * pad,
|
gst_celt_dec_stop (GstAudioDecoder * dec)
|
||||||
GstFormat src_format, gint64 src_value,
|
|
||||||
GstFormat * dest_format, gint64 * dest_value)
|
|
||||||
{
|
{
|
||||||
gboolean res = TRUE;
|
GstCeltDec *cd = GST_CELT_DEC (dec);
|
||||||
GstCeltDec *dec;
|
|
||||||
guint64 scale = 1;
|
|
||||||
|
|
||||||
dec = GST_CELT_DEC (gst_pad_get_parent (pad));
|
GST_DEBUG_OBJECT (dec, "stop");
|
||||||
|
gst_celt_dec_reset (cd);
|
||||||
|
|
||||||
if (dec->packetno < 1) {
|
return TRUE;
|
||||||
res = FALSE;
|
|
||||||
goto cleanup;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (src_format == *dest_format) {
|
|
||||||
*dest_value = src_value;
|
|
||||||
res = TRUE;
|
|
||||||
goto cleanup;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (pad == dec->sinkpad &&
|
|
||||||
(src_format == GST_FORMAT_BYTES || *dest_format == GST_FORMAT_BYTES)) {
|
|
||||||
res = FALSE;
|
|
||||||
goto cleanup;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (src_format) {
|
|
||||||
case GST_FORMAT_TIME:
|
|
||||||
switch (*dest_format) {
|
|
||||||
case GST_FORMAT_BYTES:
|
|
||||||
scale = sizeof (gint16) * dec->header.nb_channels;
|
|
||||||
case GST_FORMAT_DEFAULT:
|
|
||||||
*dest_value =
|
|
||||||
gst_util_uint64_scale_int (scale * src_value,
|
|
||||||
dec->header.sample_rate, GST_SECOND);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
res = FALSE;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case GST_FORMAT_DEFAULT:
|
|
||||||
switch (*dest_format) {
|
|
||||||
case GST_FORMAT_BYTES:
|
|
||||||
*dest_value = src_value * sizeof (gint16) * dec->header.nb_channels;
|
|
||||||
break;
|
|
||||||
case GST_FORMAT_TIME:
|
|
||||||
*dest_value =
|
|
||||||
gst_util_uint64_scale_int (src_value, GST_SECOND,
|
|
||||||
dec->header.sample_rate);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
res = FALSE;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case GST_FORMAT_BYTES:
|
|
||||||
switch (*dest_format) {
|
|
||||||
case GST_FORMAT_DEFAULT:
|
|
||||||
*dest_value = src_value / (sizeof (gint16) * dec->header.nb_channels);
|
|
||||||
break;
|
|
||||||
case GST_FORMAT_TIME:
|
|
||||||
*dest_value = gst_util_uint64_scale_int (src_value, GST_SECOND,
|
|
||||||
dec->header.sample_rate * sizeof (gint16) *
|
|
||||||
dec->header.nb_channels);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
res = FALSE;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
res = FALSE;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
cleanup:
|
|
||||||
gst_object_unref (dec);
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
static const GstQueryType *
|
|
||||||
celt_get_sink_query_types (GstPad * pad)
|
|
||||||
{
|
|
||||||
static const GstQueryType celt_dec_sink_query_types[] = {
|
|
||||||
GST_QUERY_CONVERT,
|
|
||||||
0
|
|
||||||
};
|
|
||||||
|
|
||||||
return celt_dec_sink_query_types;
|
|
||||||
}
|
|
||||||
|
|
||||||
static gboolean
|
|
||||||
celt_dec_sink_query (GstPad * pad, GstQuery * query)
|
|
||||||
{
|
|
||||||
GstCeltDec *dec;
|
|
||||||
gboolean res;
|
|
||||||
|
|
||||||
dec = GST_CELT_DEC (gst_pad_get_parent (pad));
|
|
||||||
|
|
||||||
switch (GST_QUERY_TYPE (query)) {
|
|
||||||
case GST_QUERY_CONVERT:
|
|
||||||
{
|
|
||||||
GstFormat src_fmt, dest_fmt;
|
|
||||||
gint64 src_val, dest_val;
|
|
||||||
|
|
||||||
gst_query_parse_convert (query, &src_fmt, &src_val, &dest_fmt, &dest_val);
|
|
||||||
res = celt_dec_convert (pad, src_fmt, src_val, &dest_fmt, &dest_val);
|
|
||||||
if (res) {
|
|
||||||
gst_query_set_convert (query, src_fmt, src_val, dest_fmt, dest_val);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
res = gst_pad_query_default (pad, query);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
gst_object_unref (dec);
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
static const GstQueryType *
|
|
||||||
celt_get_src_query_types (GstPad * pad)
|
|
||||||
{
|
|
||||||
static const GstQueryType celt_dec_src_query_types[] = {
|
|
||||||
GST_QUERY_POSITION,
|
|
||||||
GST_QUERY_DURATION,
|
|
||||||
0
|
|
||||||
};
|
|
||||||
|
|
||||||
return celt_dec_src_query_types;
|
|
||||||
}
|
|
||||||
|
|
||||||
static gboolean
|
|
||||||
celt_dec_src_query (GstPad * pad, GstQuery * query)
|
|
||||||
{
|
|
||||||
GstCeltDec *dec;
|
|
||||||
gboolean res = FALSE;
|
|
||||||
|
|
||||||
dec = GST_CELT_DEC (gst_pad_get_parent (pad));
|
|
||||||
|
|
||||||
switch (GST_QUERY_TYPE (query)) {
|
|
||||||
case GST_QUERY_POSITION:{
|
|
||||||
GstSegment segment;
|
|
||||||
GstFormat format;
|
|
||||||
gint64 cur;
|
|
||||||
|
|
||||||
gst_query_parse_position (query, &format, NULL);
|
|
||||||
|
|
||||||
GST_PAD_STREAM_LOCK (dec->sinkpad);
|
|
||||||
segment = dec->segment;
|
|
||||||
GST_PAD_STREAM_UNLOCK (dec->sinkpad);
|
|
||||||
|
|
||||||
if (segment.format != GST_FORMAT_TIME) {
|
|
||||||
GST_DEBUG_OBJECT (dec, "segment not initialised yet");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((res = celt_dec_convert (dec->srcpad, GST_FORMAT_TIME,
|
|
||||||
segment.last_stop, &format, &cur))) {
|
|
||||||
gst_query_set_position (query, format, cur);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case GST_QUERY_DURATION:{
|
|
||||||
GstFormat format = GST_FORMAT_TIME;
|
|
||||||
gint64 dur;
|
|
||||||
|
|
||||||
/* get duration from demuxer */
|
|
||||||
if (!gst_pad_query_peer_duration (dec->sinkpad, &format, &dur))
|
|
||||||
break;
|
|
||||||
|
|
||||||
gst_query_parse_duration (query, &format, NULL);
|
|
||||||
|
|
||||||
/* and convert it into the requested format */
|
|
||||||
if ((res = celt_dec_convert (dec->srcpad, GST_FORMAT_TIME,
|
|
||||||
dur, &format, &dur))) {
|
|
||||||
gst_query_set_duration (query, format, dur);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
res = gst_pad_query_default (pad, query);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
gst_object_unref (dec);
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
static gboolean
|
|
||||||
celt_dec_src_event (GstPad * pad, GstEvent * event)
|
|
||||||
{
|
|
||||||
gboolean res = FALSE;
|
|
||||||
GstCeltDec *dec = GST_CELT_DEC (gst_pad_get_parent (pad));
|
|
||||||
|
|
||||||
GST_LOG_OBJECT (dec, "handling %s event", GST_EVENT_TYPE_NAME (event));
|
|
||||||
|
|
||||||
switch (GST_EVENT_TYPE (event)) {
|
|
||||||
case GST_EVENT_SEEK:{
|
|
||||||
GstFormat format, tformat;
|
|
||||||
gdouble rate;
|
|
||||||
GstEvent *real_seek;
|
|
||||||
GstSeekFlags flags;
|
|
||||||
GstSeekType cur_type, stop_type;
|
|
||||||
gint64 cur, stop;
|
|
||||||
gint64 tcur, tstop;
|
|
||||||
|
|
||||||
gst_event_parse_seek (event, &rate, &format, &flags, &cur_type, &cur,
|
|
||||||
&stop_type, &stop);
|
|
||||||
|
|
||||||
/* we have to ask our peer to seek to time here as we know
|
|
||||||
* nothing about how to generate a granulepos from the src
|
|
||||||
* formats or anything.
|
|
||||||
*
|
|
||||||
* First bring the requested format to time
|
|
||||||
*/
|
|
||||||
tformat = GST_FORMAT_TIME;
|
|
||||||
if (!(res = celt_dec_convert (pad, format, cur, &tformat, &tcur)))
|
|
||||||
break;
|
|
||||||
if (!(res = celt_dec_convert (pad, format, stop, &tformat, &tstop)))
|
|
||||||
break;
|
|
||||||
|
|
||||||
/* then seek with time on the peer */
|
|
||||||
real_seek = gst_event_new_seek (rate, GST_FORMAT_TIME,
|
|
||||||
flags, cur_type, tcur, stop_type, tstop);
|
|
||||||
|
|
||||||
GST_LOG_OBJECT (dec, "seek to %" GST_TIME_FORMAT, GST_TIME_ARGS (tcur));
|
|
||||||
|
|
||||||
res = gst_pad_push_event (dec->sinkpad, real_seek);
|
|
||||||
gst_event_unref (event);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
res = gst_pad_event_default (pad, event);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
gst_object_unref (dec);
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
static gboolean
|
|
||||||
celt_dec_sink_event (GstPad * pad, GstEvent * event)
|
|
||||||
{
|
|
||||||
GstCeltDec *dec;
|
|
||||||
gboolean ret = FALSE;
|
|
||||||
|
|
||||||
dec = GST_CELT_DEC (gst_pad_get_parent (pad));
|
|
||||||
|
|
||||||
GST_LOG_OBJECT (dec, "handling %s event", GST_EVENT_TYPE_NAME (event));
|
|
||||||
|
|
||||||
switch (GST_EVENT_TYPE (event)) {
|
|
||||||
case GST_EVENT_NEWSEGMENT:{
|
|
||||||
GstFormat format;
|
|
||||||
gdouble rate, arate;
|
|
||||||
gint64 start, stop, time;
|
|
||||||
gboolean update;
|
|
||||||
|
|
||||||
gst_event_parse_new_segment_full (event, &update, &rate, &arate, &format,
|
|
||||||
&start, &stop, &time);
|
|
||||||
|
|
||||||
if (format != GST_FORMAT_TIME)
|
|
||||||
goto newseg_wrong_format;
|
|
||||||
|
|
||||||
if (rate <= 0.0)
|
|
||||||
goto newseg_wrong_rate;
|
|
||||||
|
|
||||||
if (update) {
|
|
||||||
/* time progressed without data, see if we can fill the gap with
|
|
||||||
* some concealment data */
|
|
||||||
if (dec->segment.last_stop < start) {
|
|
||||||
GstClockTime duration;
|
|
||||||
|
|
||||||
duration = start - dec->segment.last_stop;
|
|
||||||
celt_dec_chain_parse_data (dec, NULL, dec->segment.last_stop,
|
|
||||||
duration);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* now configure the values */
|
|
||||||
gst_segment_set_newsegment_full (&dec->segment, update,
|
|
||||||
rate, arate, GST_FORMAT_TIME, start, stop, time);
|
|
||||||
|
|
||||||
dec->granulepos = -1;
|
|
||||||
|
|
||||||
GST_DEBUG_OBJECT (dec, "segment now: cur = %" GST_TIME_FORMAT " [%"
|
|
||||||
GST_TIME_FORMAT " - %" GST_TIME_FORMAT "]",
|
|
||||||
GST_TIME_ARGS (dec->segment.last_stop),
|
|
||||||
GST_TIME_ARGS (dec->segment.start),
|
|
||||||
GST_TIME_ARGS (dec->segment.stop));
|
|
||||||
|
|
||||||
ret = gst_pad_push_event (dec->srcpad, event);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
ret = gst_pad_event_default (pad, event);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
gst_object_unref (dec);
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
/* ERRORS */
|
|
||||||
newseg_wrong_format:
|
|
||||||
{
|
|
||||||
GST_DEBUG_OBJECT (dec, "received non TIME newsegment");
|
|
||||||
gst_object_unref (dec);
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
newseg_wrong_rate:
|
|
||||||
{
|
|
||||||
GST_DEBUG_OBJECT (dec, "negative rates not supported yet");
|
|
||||||
gst_object_unref (dec);
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static GstFlowReturn
|
static GstFlowReturn
|
||||||
celt_dec_chain_parse_header (GstCeltDec * dec, GstBuffer * buf)
|
gst_celt_dec_parse_header (GstCeltDec * dec, GstBuffer * buf)
|
||||||
{
|
{
|
||||||
GstCaps *caps;
|
GstCaps *caps;
|
||||||
gint error = CELT_OK;
|
gint error = CELT_OK;
|
||||||
|
@ -596,9 +206,6 @@ celt_dec_chain_parse_header (GstCeltDec * dec, GstBuffer * buf)
|
||||||
celt_mode_info (dec->mode, CELT_GET_FRAME_SIZE, &dec->frame_size);
|
celt_mode_info (dec->mode, CELT_GET_FRAME_SIZE, &dec->frame_size);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
dec->frame_duration = gst_util_uint64_scale_int (dec->frame_size,
|
|
||||||
GST_SECOND, dec->header.sample_rate);
|
|
||||||
|
|
||||||
/* set caps */
|
/* set caps */
|
||||||
caps = gst_caps_new_simple ("audio/x-raw-int",
|
caps = gst_caps_new_simple ("audio/x-raw-int",
|
||||||
"rate", G_TYPE_INT, dec->header.sample_rate,
|
"rate", G_TYPE_INT, dec->header.sample_rate,
|
||||||
|
@ -610,7 +217,7 @@ celt_dec_chain_parse_header (GstCeltDec * dec, GstBuffer * buf)
|
||||||
GST_DEBUG_OBJECT (dec, "rate=%d channels=%d frame-size=%d",
|
GST_DEBUG_OBJECT (dec, "rate=%d channels=%d frame-size=%d",
|
||||||
dec->header.sample_rate, dec->header.nb_channels, dec->frame_size);
|
dec->header.sample_rate, dec->header.nb_channels, dec->frame_size);
|
||||||
|
|
||||||
if (!gst_pad_set_caps (dec->srcpad, caps))
|
if (!gst_pad_set_caps (GST_AUDIO_DECODER_SRC_PAD (dec), caps))
|
||||||
goto nego_failed;
|
goto nego_failed;
|
||||||
|
|
||||||
gst_caps_unref (caps);
|
gst_caps_unref (caps);
|
||||||
|
@ -650,7 +257,7 @@ nego_failed:
|
||||||
}
|
}
|
||||||
|
|
||||||
static GstFlowReturn
|
static GstFlowReturn
|
||||||
celt_dec_chain_parse_comments (GstCeltDec * dec, GstBuffer * buf)
|
gst_celt_dec_parse_comments (GstCeltDec * dec, GstBuffer * buf)
|
||||||
{
|
{
|
||||||
GstTagList *list;
|
GstTagList *list;
|
||||||
gchar *ver, *encoder = NULL;
|
gchar *ver, *encoder = NULL;
|
||||||
|
@ -685,7 +292,8 @@ celt_dec_chain_parse_comments (GstCeltDec * dec, GstBuffer * buf)
|
||||||
|
|
||||||
GST_INFO_OBJECT (dec, "tags: %" GST_PTR_FORMAT, list);
|
GST_INFO_OBJECT (dec, "tags: %" GST_PTR_FORMAT, list);
|
||||||
|
|
||||||
gst_element_found_tags_for_pad (GST_ELEMENT (dec), dec->srcpad, list);
|
gst_element_found_tags_for_pad (GST_ELEMENT (dec),
|
||||||
|
GST_AUDIO_DECODER_SRC_PAD (dec), list);
|
||||||
|
|
||||||
g_free (encoder);
|
g_free (encoder);
|
||||||
g_free (ver);
|
g_free (ver);
|
||||||
|
@ -694,8 +302,7 @@ celt_dec_chain_parse_comments (GstCeltDec * dec, GstBuffer * buf)
|
||||||
}
|
}
|
||||||
|
|
||||||
static GstFlowReturn
|
static GstFlowReturn
|
||||||
celt_dec_chain_parse_data (GstCeltDec * dec, GstBuffer * buf,
|
gst_celt_dec_parse_data (GstCeltDec * dec, GstBuffer * buf)
|
||||||
GstClockTime timestamp, GstClockTime duration)
|
|
||||||
{
|
{
|
||||||
GstFlowReturn res = GST_FLOW_OK;
|
GstFlowReturn res = GST_FLOW_OK;
|
||||||
gint size;
|
gint size;
|
||||||
|
@ -705,33 +312,23 @@ celt_dec_chain_parse_data (GstCeltDec * dec, GstBuffer * buf,
|
||||||
gint error = CELT_OK;
|
gint error = CELT_OK;
|
||||||
int skip = 0;
|
int skip = 0;
|
||||||
|
|
||||||
if (timestamp != -1) {
|
if (!dec->frame_size)
|
||||||
dec->segment.last_stop = timestamp;
|
goto not_negotiated;
|
||||||
dec->granulepos = -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (buf) {
|
if (G_LIKELY (GST_BUFFER_SIZE (buf))) {
|
||||||
data = GST_BUFFER_DATA (buf);
|
data = GST_BUFFER_DATA (buf);
|
||||||
size = GST_BUFFER_SIZE (buf);
|
size = GST_BUFFER_SIZE (buf);
|
||||||
|
|
||||||
GST_DEBUG_OBJECT (dec, "received buffer of size %u", size);
|
|
||||||
if (!GST_BUFFER_TIMESTAMP_IS_VALID (buf)
|
|
||||||
&& GST_BUFFER_OFFSET_END_IS_VALID (buf)) {
|
|
||||||
dec->granulepos = GST_BUFFER_OFFSET_END (buf);
|
|
||||||
GST_DEBUG_OBJECT (dec,
|
|
||||||
"Taking granulepos from upstream: %" G_GUINT64_FORMAT,
|
|
||||||
dec->granulepos);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* copy timestamp */
|
|
||||||
} else {
|
} else {
|
||||||
|
/* FIXME ? actually consider how much concealment is needed */
|
||||||
/* concealment data, pass NULL as the bits parameters */
|
/* concealment data, pass NULL as the bits parameters */
|
||||||
GST_DEBUG_OBJECT (dec, "creating concealment data");
|
GST_DEBUG_OBJECT (dec, "creating concealment data");
|
||||||
data = NULL;
|
data = NULL;
|
||||||
size = 0;
|
size = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (dec->discont) {
|
/* FIXME really needed ?; this might lead to skipping samples below
|
||||||
|
* which kind of messes with subsequent timestamping */
|
||||||
|
if (G_UNLIKELY (GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_DISCONT))) {
|
||||||
#ifdef CELT_GET_LOOKAHEAD_REQUEST
|
#ifdef CELT_GET_LOOKAHEAD_REQUEST
|
||||||
/* what will be 0.11.5, I guess, but no versioning yet in git */
|
/* what will be 0.11.5, I guess, but no versioning yet in git */
|
||||||
celt_decoder_ctl (dec->state, CELT_GET_LOOKAHEAD_REQUEST, &skip);
|
celt_decoder_ctl (dec->state, CELT_GET_LOOKAHEAD_REQUEST, &skip);
|
||||||
|
@ -740,9 +337,9 @@ celt_dec_chain_parse_data (GstCeltDec * dec, GstBuffer * buf,
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
res = gst_pad_alloc_buffer_and_set_caps (dec->srcpad,
|
res = gst_pad_alloc_buffer_and_set_caps (GST_AUDIO_DECODER_SRC_PAD (dec),
|
||||||
GST_BUFFER_OFFSET_NONE, dec->frame_size * dec->header.nb_channels * 2,
|
GST_BUFFER_OFFSET_NONE, dec->frame_size * dec->header.nb_channels * 2,
|
||||||
GST_PAD_CAPS (dec->srcpad), &outbuf);
|
GST_PAD_CAPS (GST_AUDIO_DECODER_SRC_PAD (dec)), &outbuf);
|
||||||
|
|
||||||
if (res != GST_FLOW_OK) {
|
if (res != GST_FLOW_OK) {
|
||||||
GST_DEBUG_OBJECT (dec, "buf alloc flow: %s", gst_flow_get_name (res));
|
GST_DEBUG_OBJECT (dec, "buf alloc flow: %s", gst_flow_get_name (res));
|
||||||
|
@ -775,59 +372,88 @@ celt_dec_chain_parse_data (GstCeltDec * dec, GstBuffer * buf,
|
||||||
skip * dec->header.nb_channels * 2;
|
skip * dec->header.nb_channels * 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (dec->granulepos == -1) {
|
res = gst_audio_decoder_finish_frame (GST_AUDIO_DECODER (dec), outbuf, 1);
|
||||||
if (dec->segment.format != GST_FORMAT_TIME) {
|
|
||||||
GST_WARNING_OBJECT (dec, "segment not initialized or not TIME format");
|
|
||||||
dec->granulepos = dec->frame_size;
|
|
||||||
} else {
|
|
||||||
dec->granulepos = gst_util_uint64_scale_int (dec->segment.last_stop,
|
|
||||||
dec->header.sample_rate, GST_SECOND) + dec->frame_size;
|
|
||||||
}
|
|
||||||
GST_DEBUG_OBJECT (dec, "granulepos=%" G_GINT64_FORMAT, dec->granulepos);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!GST_CLOCK_TIME_IS_VALID (timestamp))
|
|
||||||
timestamp = gst_util_uint64_scale_int (dec->granulepos - dec->frame_size,
|
|
||||||
GST_SECOND, dec->header.sample_rate);
|
|
||||||
|
|
||||||
GST_DEBUG_OBJECT (dec, "timestamp=%" GST_TIME_FORMAT,
|
|
||||||
GST_TIME_ARGS (timestamp));
|
|
||||||
|
|
||||||
GST_BUFFER_OFFSET (outbuf) = dec->granulepos - dec->frame_size;
|
|
||||||
GST_BUFFER_OFFSET_END (outbuf) = dec->granulepos;
|
|
||||||
GST_BUFFER_TIMESTAMP (outbuf) = timestamp;
|
|
||||||
GST_BUFFER_DURATION (outbuf) = dec->frame_duration;
|
|
||||||
if (dec->discont) {
|
|
||||||
GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_DISCONT);
|
|
||||||
dec->discont = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
dec->granulepos += dec->frame_size;
|
|
||||||
dec->segment.last_stop += dec->frame_duration;
|
|
||||||
|
|
||||||
GST_LOG_OBJECT (dec, "pushing buffer with ts=%" GST_TIME_FORMAT ", dur=%"
|
|
||||||
GST_TIME_FORMAT, GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (outbuf)),
|
|
||||||
GST_TIME_ARGS (dec->frame_duration));
|
|
||||||
|
|
||||||
res = gst_pad_push (dec->srcpad, outbuf);
|
|
||||||
|
|
||||||
if (res != GST_FLOW_OK)
|
if (res != GST_FLOW_OK)
|
||||||
GST_DEBUG_OBJECT (dec, "flow: %s", gst_flow_get_name (res));
|
GST_DEBUG_OBJECT (dec, "flow: %s", gst_flow_get_name (res));
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
|
|
||||||
|
/* ERRORS */
|
||||||
|
not_negotiated:
|
||||||
|
{
|
||||||
|
GST_ELEMENT_ERROR (dec, CORE, NEGOTIATION, (NULL),
|
||||||
|
("decoder not initialized"));
|
||||||
|
return GST_FLOW_NOT_NEGOTIATED;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
gst_celt_dec_set_format (GstAudioDecoder * bdec, GstCaps * caps)
|
||||||
|
{
|
||||||
|
GstCeltDec *dec = GST_CELT_DEC (bdec);
|
||||||
|
gboolean ret = TRUE;
|
||||||
|
GstStructure *s;
|
||||||
|
const GValue *streamheader;
|
||||||
|
|
||||||
|
s = gst_caps_get_structure (caps, 0);
|
||||||
|
if ((streamheader = gst_structure_get_value (s, "streamheader")) &&
|
||||||
|
G_VALUE_HOLDS (streamheader, GST_TYPE_ARRAY) &&
|
||||||
|
gst_value_array_get_size (streamheader) >= 2) {
|
||||||
|
const GValue *header, *vorbiscomment;
|
||||||
|
GstBuffer *buf;
|
||||||
|
GstFlowReturn res = GST_FLOW_OK;
|
||||||
|
|
||||||
|
header = gst_value_array_get_value (streamheader, 0);
|
||||||
|
if (header && G_VALUE_HOLDS (header, GST_TYPE_BUFFER)) {
|
||||||
|
buf = gst_value_get_buffer (header);
|
||||||
|
res = gst_celt_dec_parse_header (dec, buf);
|
||||||
|
if (res != GST_FLOW_OK)
|
||||||
|
goto done;
|
||||||
|
gst_buffer_replace (&dec->streamheader, buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
vorbiscomment = gst_value_array_get_value (streamheader, 1);
|
||||||
|
if (vorbiscomment && G_VALUE_HOLDS (vorbiscomment, GST_TYPE_BUFFER)) {
|
||||||
|
buf = gst_value_get_buffer (vorbiscomment);
|
||||||
|
res = gst_celt_dec_parse_comments (dec, buf);
|
||||||
|
if (res != GST_FLOW_OK)
|
||||||
|
goto done;
|
||||||
|
gst_buffer_replace (&dec->vorbiscomment, buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
g_list_foreach (dec->extra_headers, (GFunc) gst_mini_object_unref, NULL);
|
||||||
|
g_list_free (dec->extra_headers);
|
||||||
|
dec->extra_headers = NULL;
|
||||||
|
|
||||||
|
if (gst_value_array_get_size (streamheader) > 2) {
|
||||||
|
gint i, n;
|
||||||
|
|
||||||
|
n = gst_value_array_get_size (streamheader);
|
||||||
|
for (i = 2; i < n; i++) {
|
||||||
|
header = gst_value_array_get_value (streamheader, i);
|
||||||
|
buf = gst_value_get_buffer (header);
|
||||||
|
dec->extra_headers =
|
||||||
|
g_list_prepend (dec->extra_headers, gst_buffer_ref (buf));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
done:
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static GstFlowReturn
|
static GstFlowReturn
|
||||||
celt_dec_chain (GstPad * pad, GstBuffer * buf)
|
gst_celt_dec_handle_frame (GstAudioDecoder * bdec, GstBuffer * buf)
|
||||||
{
|
{
|
||||||
GstFlowReturn res;
|
GstFlowReturn res;
|
||||||
GstCeltDec *dec;
|
GstCeltDec *dec;
|
||||||
|
|
||||||
dec = GST_CELT_DEC (gst_pad_get_parent (pad));
|
dec = GST_CELT_DEC (bdec);
|
||||||
|
|
||||||
if (GST_BUFFER_IS_DISCONT (buf)) {
|
/* no fancy draining */
|
||||||
dec->discont = TRUE;
|
if (G_UNLIKELY (!buf))
|
||||||
}
|
return GST_FLOW_OK;
|
||||||
|
|
||||||
/* If we have the streamheader and vorbiscomment from the caps already
|
/* If we have the streamheader and vorbiscomment from the caps already
|
||||||
* ignore them here */
|
* ignore them here */
|
||||||
|
@ -835,10 +461,14 @@ celt_dec_chain (GstPad * pad, GstBuffer * buf)
|
||||||
if (GST_BUFFER_SIZE (dec->streamheader) == GST_BUFFER_SIZE (buf)
|
if (GST_BUFFER_SIZE (dec->streamheader) == GST_BUFFER_SIZE (buf)
|
||||||
&& memcmp (GST_BUFFER_DATA (dec->streamheader), GST_BUFFER_DATA (buf),
|
&& memcmp (GST_BUFFER_DATA (dec->streamheader), GST_BUFFER_DATA (buf),
|
||||||
GST_BUFFER_SIZE (buf)) == 0) {
|
GST_BUFFER_SIZE (buf)) == 0) {
|
||||||
|
GST_DEBUG_OBJECT (dec, "found streamheader");
|
||||||
|
gst_audio_decoder_finish_frame (bdec, NULL, 1);
|
||||||
res = GST_FLOW_OK;
|
res = GST_FLOW_OK;
|
||||||
} else if (GST_BUFFER_SIZE (dec->vorbiscomment) == GST_BUFFER_SIZE (buf)
|
} else if (GST_BUFFER_SIZE (dec->vorbiscomment) == GST_BUFFER_SIZE (buf)
|
||||||
&& memcmp (GST_BUFFER_DATA (dec->vorbiscomment), GST_BUFFER_DATA (buf),
|
&& memcmp (GST_BUFFER_DATA (dec->vorbiscomment), GST_BUFFER_DATA (buf),
|
||||||
GST_BUFFER_SIZE (buf)) == 0) {
|
GST_BUFFER_SIZE (buf)) == 0) {
|
||||||
|
GST_DEBUG_OBJECT (dec, "found vorbiscomments");
|
||||||
|
gst_audio_decoder_finish_frame (bdec, NULL, 1);
|
||||||
res = GST_FLOW_OK;
|
res = GST_FLOW_OK;
|
||||||
} else {
|
} else {
|
||||||
GList *l;
|
GList *l;
|
||||||
|
@ -848,66 +478,36 @@ celt_dec_chain (GstPad * pad, GstBuffer * buf)
|
||||||
if (GST_BUFFER_SIZE (header) == GST_BUFFER_SIZE (buf) &&
|
if (GST_BUFFER_SIZE (header) == GST_BUFFER_SIZE (buf) &&
|
||||||
memcmp (GST_BUFFER_DATA (header), GST_BUFFER_DATA (buf),
|
memcmp (GST_BUFFER_DATA (header), GST_BUFFER_DATA (buf),
|
||||||
GST_BUFFER_SIZE (buf)) == 0) {
|
GST_BUFFER_SIZE (buf)) == 0) {
|
||||||
|
GST_DEBUG_OBJECT (dec, "found extra header buffer");
|
||||||
|
gst_audio_decoder_finish_frame (bdec, NULL, 1);
|
||||||
res = GST_FLOW_OK;
|
res = GST_FLOW_OK;
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
res =
|
res = gst_celt_dec_parse_data (dec, buf);
|
||||||
celt_dec_chain_parse_data (dec, buf, GST_BUFFER_TIMESTAMP (buf),
|
|
||||||
GST_BUFFER_DURATION (buf));
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
/* Otherwise fall back to packet counting and assume that the
|
/* Otherwise fall back to packet counting and assume that the
|
||||||
* first two packets are the headers. */
|
* first two packets are the headers. */
|
||||||
if (dec->packetno == 0)
|
if (dec->packetno == 0) {
|
||||||
res = celt_dec_chain_parse_header (dec, buf);
|
GST_DEBUG_OBJECT (dec, "counted streamheader");
|
||||||
else if (dec->packetno == 1)
|
res = gst_celt_dec_parse_header (dec, buf);
|
||||||
res = celt_dec_chain_parse_comments (dec, buf);
|
gst_audio_decoder_finish_frame (bdec, NULL, 1);
|
||||||
else if (dec->packetno <= 1 + dec->header.extra_headers)
|
} else if (dec->packetno == 1) {
|
||||||
|
GST_DEBUG_OBJECT (dec, "counted vorbiscomments");
|
||||||
|
res = gst_celt_dec_parse_comments (dec, buf);
|
||||||
|
gst_audio_decoder_finish_frame (bdec, NULL, 1);
|
||||||
|
} else if (dec->packetno <= 1 + dec->header.extra_headers) {
|
||||||
|
GST_DEBUG_OBJECT (dec, "counted extra header");
|
||||||
|
gst_audio_decoder_finish_frame (bdec, NULL, 1);
|
||||||
res = GST_FLOW_OK;
|
res = GST_FLOW_OK;
|
||||||
else
|
} else {
|
||||||
res = celt_dec_chain_parse_data (dec, buf, GST_BUFFER_TIMESTAMP (buf),
|
res = gst_celt_dec_parse_data (dec, buf);
|
||||||
GST_BUFFER_DURATION (buf));
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
done:
|
done:
|
||||||
dec->packetno++;
|
dec->packetno++;
|
||||||
|
|
||||||
gst_buffer_unref (buf);
|
|
||||||
gst_object_unref (dec);
|
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
static GstStateChangeReturn
|
|
||||||
celt_dec_change_state (GstElement * element, GstStateChange transition)
|
|
||||||
{
|
|
||||||
GstStateChangeReturn ret;
|
|
||||||
GstCeltDec *dec = GST_CELT_DEC (element);
|
|
||||||
|
|
||||||
switch (transition) {
|
|
||||||
case GST_STATE_CHANGE_NULL_TO_READY:
|
|
||||||
case GST_STATE_CHANGE_READY_TO_PAUSED:
|
|
||||||
case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = parent_class->change_state (element, transition);
|
|
||||||
if (ret != GST_STATE_CHANGE_SUCCESS)
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
switch (transition) {
|
|
||||||
case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
|
|
||||||
break;
|
|
||||||
case GST_STATE_CHANGE_PAUSED_TO_READY:
|
|
||||||
gst_celt_dec_reset (dec);
|
|
||||||
break;
|
|
||||||
case GST_STATE_CHANGE_READY_TO_NULL:
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
|
@ -22,6 +22,7 @@
|
||||||
#define __GST_CELT_DEC_H__
|
#define __GST_CELT_DEC_H__
|
||||||
|
|
||||||
#include <gst/gst.h>
|
#include <gst/gst.h>
|
||||||
|
#include <gst/audio/gstaudiodecoder.h>
|
||||||
#include <celt/celt.h>
|
#include <celt/celt.h>
|
||||||
#include <celt/celt_header.h>
|
#include <celt/celt_header.h>
|
||||||
|
|
||||||
|
@ -42,22 +43,15 @@ typedef struct _GstCeltDec GstCeltDec;
|
||||||
typedef struct _GstCeltDecClass GstCeltDecClass;
|
typedef struct _GstCeltDecClass GstCeltDecClass;
|
||||||
|
|
||||||
struct _GstCeltDec {
|
struct _GstCeltDec {
|
||||||
GstElement element;
|
GstAudioDecoder element;
|
||||||
|
|
||||||
/* pads */
|
|
||||||
GstPad *sinkpad;
|
|
||||||
GstPad *srcpad;
|
|
||||||
|
|
||||||
CELTDecoder *state;
|
CELTDecoder *state;
|
||||||
CELTMode *mode;
|
CELTMode *mode;
|
||||||
CELTHeader header;
|
CELTHeader header;
|
||||||
|
|
||||||
gint frame_size;
|
gint frame_size;
|
||||||
GstClockTime frame_duration;
|
|
||||||
guint64 packetno;
|
guint64 packetno;
|
||||||
|
|
||||||
GstSegment segment; /* STREAM LOCK */
|
|
||||||
gint64 granulepos; /* -1 = needs to be set from current time */
|
|
||||||
gboolean discont;
|
gboolean discont;
|
||||||
|
|
||||||
GstBuffer *streamheader;
|
GstBuffer *streamheader;
|
||||||
|
@ -66,7 +60,7 @@ struct _GstCeltDec {
|
||||||
};
|
};
|
||||||
|
|
||||||
struct _GstCeltDecClass {
|
struct _GstCeltDecClass {
|
||||||
GstElementClass parent_class;
|
GstAudioDecoderClass parent_class;
|
||||||
};
|
};
|
||||||
|
|
||||||
GType gst_celt_dec_get_type (void);
|
GType gst_celt_dec_get_type (void);
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -24,7 +24,7 @@
|
||||||
|
|
||||||
|
|
||||||
#include <gst/gst.h>
|
#include <gst/gst.h>
|
||||||
#include <gst/base/gstadapter.h>
|
#include <gst/audio/gstaudioencoder.h>
|
||||||
|
|
||||||
#include <celt/celt.h>
|
#include <celt/celt.h>
|
||||||
#include <celt/celt_header.h>
|
#include <celt/celt_header.h>
|
||||||
|
@ -49,16 +49,11 @@ typedef struct _GstCeltEnc GstCeltEnc;
|
||||||
typedef struct _GstCeltEncClass GstCeltEncClass;
|
typedef struct _GstCeltEncClass GstCeltEncClass;
|
||||||
|
|
||||||
struct _GstCeltEnc {
|
struct _GstCeltEnc {
|
||||||
GstElement element;
|
GstAudioEncoder element;
|
||||||
|
|
||||||
/* pads */
|
|
||||||
GstPad *sinkpad;
|
|
||||||
GstPad *srcpad;
|
|
||||||
|
|
||||||
CELTHeader header;
|
CELTHeader header;
|
||||||
CELTMode *mode;
|
CELTMode *mode;
|
||||||
CELTEncoder *state;
|
CELTEncoder *state;
|
||||||
GstAdapter *adapter;
|
|
||||||
|
|
||||||
gint bitrate;
|
gint bitrate;
|
||||||
gint frame_size;
|
gint frame_size;
|
||||||
|
@ -72,26 +67,12 @@ struct _GstCeltEnc {
|
||||||
gint channels;
|
gint channels;
|
||||||
gint rate;
|
gint rate;
|
||||||
|
|
||||||
gboolean setup;
|
|
||||||
gboolean header_sent;
|
gboolean header_sent;
|
||||||
gboolean eos;
|
GSList *headers;
|
||||||
|
|
||||||
guint64 samples_in;
|
|
||||||
guint64 bytes_out;
|
|
||||||
|
|
||||||
guint64 frameno;
|
|
||||||
guint64 frameno_out;
|
|
||||||
|
|
||||||
GstClockTime start_ts;
|
|
||||||
GstClockTime next_ts;
|
|
||||||
guint64 granulepos_offset;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct _GstCeltEncClass {
|
struct _GstCeltEncClass {
|
||||||
GstElementClass parent_class;
|
GstAudioEncoderClass parent_class;
|
||||||
|
|
||||||
/* signals */
|
|
||||||
void (*frame_encoded) (GstElement *element);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
GType gst_celt_enc_get_type (void);
|
GType gst_celt_enc_get_type (void);
|
||||||
|
|
|
@ -5,5 +5,6 @@ libgstmimic_la_SOURCES = gstmimic.c gstmimdec.c gstmimenc.c
|
||||||
libgstmimic_la_CFLAGS = $(GST_BASE_CFLAGS) $(GST_CFLAGS) $(MIMIC_CFLAGS)
|
libgstmimic_la_CFLAGS = $(GST_BASE_CFLAGS) $(GST_CFLAGS) $(MIMIC_CFLAGS)
|
||||||
libgstmimic_la_LIBADD = $(GST_BASE_LIBS) $(GST_LIBS) $(MIMIC_LIBS)
|
libgstmimic_la_LIBADD = $(GST_BASE_LIBS) $(GST_LIBS) $(MIMIC_LIBS)
|
||||||
libgstmimic_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS)
|
libgstmimic_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS)
|
||||||
|
libgstmimic_la_LIBTOOLFLAGS = --tag=disable-static
|
||||||
|
|
||||||
noinst_HEADERS = gstmimdec.h gstmimenc.h
|
noinst_HEADERS = gstmimdec.h gstmimenc.h
|
||||||
|
|
|
@ -36,6 +36,7 @@ libgstopencv_la_LIBADD = $(GST_LIBS) $(GST_BASE_LIBS) $(OPENCV_LIBS) \
|
||||||
$(GSTPB_BASE_LIBS) -lgstvideo-$(GST_MAJORMINOR)
|
$(GSTPB_BASE_LIBS) -lgstvideo-$(GST_MAJORMINOR)
|
||||||
|
|
||||||
libgstopencv_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS)
|
libgstopencv_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS)
|
||||||
|
libgstopencv_la_LIBTOOLFLAGS = --tag=disable-static
|
||||||
|
|
||||||
# headers we need but don't want installed
|
# headers we need but don't want installed
|
||||||
noinst_HEADERS = gstopencvvideofilter.h gstopencvutils.h \
|
noinst_HEADERS = gstopencvvideofilter.h gstopencvutils.h \
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
* Copyright (C) 2005 Thomas Vander Stichele <thomas@apestaart.org>
|
* Copyright (C) 2005 Thomas Vander Stichele <thomas@apestaart.org>
|
||||||
* Copyright (C) 2005 Ronald S. Bultje <rbultje@ronald.bitfreak.net>
|
* Copyright (C) 2005 Ronald S. Bultje <rbultje@ronald.bitfreak.net>
|
||||||
* Copyright (C) 2008 Michael Sheldon <mike@mikeasoft.com>
|
* Copyright (C) 2008 Michael Sheldon <mike@mikeasoft.com>
|
||||||
|
* Copyright (C) 2011 Stefan Sauer <ensonic@users.sf.net>
|
||||||
*
|
*
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||||
* copy of this software and associated documentation files (the "Software"),
|
* copy of this software and associated documentation files (the "Software"),
|
||||||
|
@ -48,14 +49,27 @@
|
||||||
*
|
*
|
||||||
* Performs face detection on videos and images.
|
* Performs face detection on videos and images.
|
||||||
*
|
*
|
||||||
|
* The image is scaled down multiple times using the GstFacedetect::scale-factor
|
||||||
|
* until the size is <= GstFacedetect::min-size-width or
|
||||||
|
* GstFacedetect::min-size-height.
|
||||||
|
*
|
||||||
* <refsect2>
|
* <refsect2>
|
||||||
* <title>Example launch line</title>
|
* <title>Example launch line</title>
|
||||||
* |[
|
* |[
|
||||||
* gst-launch-0.10 videotestsrc ! decodebin ! ffmpegcolorspace ! facedetect ! ffmpegcolorspace ! xvimagesink
|
* gst-launch-0.10 autovideosrc ! decodebin2 ! colorspace ! facedetect ! colorspace ! xvimagesink
|
||||||
* ]|
|
* ]| Detect and show faces
|
||||||
|
* |[
|
||||||
|
* gst-launch-0.10 autovideosrc ! video/x-raw-yuv,width=320,height=240 ! colorspace ! facedetect min-size-width=60 min-size-height=60 ! colorspace ! xvimagesink
|
||||||
|
* ]| Detect large faces on a smaller image
|
||||||
|
*
|
||||||
* </refsect2>
|
* </refsect2>
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/* FIXME: development version of OpenCV has CV_HAAR_FIND_BIGGEST_OBJECT which
|
||||||
|
* we might want to use if available
|
||||||
|
* see https://code.ros.org/svn/opencv/trunk/opencv/modules/objdetect/src/haar.cpp
|
||||||
|
*/
|
||||||
|
|
||||||
#ifdef HAVE_CONFIG_H
|
#ifdef HAVE_CONFIG_H
|
||||||
# include <config.h>
|
# include <config.h>
|
||||||
#endif
|
#endif
|
||||||
|
@ -67,7 +81,10 @@
|
||||||
GST_DEBUG_CATEGORY_STATIC (gst_facedetect_debug);
|
GST_DEBUG_CATEGORY_STATIC (gst_facedetect_debug);
|
||||||
#define GST_CAT_DEFAULT gst_facedetect_debug
|
#define GST_CAT_DEFAULT gst_facedetect_debug
|
||||||
|
|
||||||
#define DEFAULT_PROFILE "/usr/share/opencv/haarcascades/haarcascade_frontalface_default.xml"
|
#define DEFAULT_FACE_PROFILE "/usr/share/opencv/haarcascades/haarcascade_frontalface_default.xml"
|
||||||
|
#define DEFAULT_NOSE_PROFILE "/usr/share/opencv/haarcascades/haarcascade_mcs_nose.xml"
|
||||||
|
#define DEFAULT_MOUTH_PROFILE "/usr/share/opencv/haarcascades/haarcascade_mcs_mouth.xml"
|
||||||
|
#define DEFAULT_EYES_PROFILE "/usr/share/opencv/haarcascades/haarcascade_mcs_eyepair_small.xml"
|
||||||
#define DEFAULT_SCALE_FACTOR 1.1
|
#define DEFAULT_SCALE_FACTOR 1.1
|
||||||
#define DEFAULT_FLAGS 0
|
#define DEFAULT_FLAGS 0
|
||||||
#define DEFAULT_MIN_NEIGHBORS 3
|
#define DEFAULT_MIN_NEIGHBORS 3
|
||||||
|
@ -85,7 +102,10 @@ enum
|
||||||
{
|
{
|
||||||
PROP_0,
|
PROP_0,
|
||||||
PROP_DISPLAY,
|
PROP_DISPLAY,
|
||||||
PROP_PROFILE,
|
PROP_FACE_PROFILE,
|
||||||
|
PROP_NOSE_PROFILE,
|
||||||
|
PROP_MOUTH_PROFILE,
|
||||||
|
PROP_EYES_PROFILE,
|
||||||
PROP_SCALE_FACTOR,
|
PROP_SCALE_FACTOR,
|
||||||
PROP_MIN_NEIGHBORS,
|
PROP_MIN_NEIGHBORS,
|
||||||
PROP_FLAGS,
|
PROP_FLAGS,
|
||||||
|
@ -141,7 +161,7 @@ static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src",
|
||||||
GST_STATIC_CAPS ("video/x-raw-rgb")
|
GST_STATIC_CAPS ("video/x-raw-rgb")
|
||||||
);
|
);
|
||||||
|
|
||||||
GST_BOILERPLATE (Gstfacedetect, gst_facedetect, GstOpencvVideoFilter,
|
GST_BOILERPLATE (GstFacedetect, gst_facedetect, GstOpencvVideoFilter,
|
||||||
GST_TYPE_OPENCV_VIDEO_FILTER);
|
GST_TYPE_OPENCV_VIDEO_FILTER);
|
||||||
|
|
||||||
static void gst_facedetect_set_property (GObject * object, guint prop_id,
|
static void gst_facedetect_set_property (GObject * object, guint prop_id,
|
||||||
|
@ -155,22 +175,33 @@ static gboolean gst_facedetect_set_caps (GstOpencvVideoFilter * transform,
|
||||||
static GstFlowReturn gst_facedetect_transform_ip (GstOpencvVideoFilter * base,
|
static GstFlowReturn gst_facedetect_transform_ip (GstOpencvVideoFilter * base,
|
||||||
GstBuffer * buf, IplImage * img);
|
GstBuffer * buf, IplImage * img);
|
||||||
|
|
||||||
static void gst_facedetect_load_profile (Gstfacedetect * filter);
|
static CvHaarClassifierCascade *gst_facedetect_load_profile (GstFacedetect *
|
||||||
|
filter, gchar * profile);
|
||||||
|
|
||||||
/* Clean up */
|
/* Clean up */
|
||||||
static void
|
static void
|
||||||
gst_facedetect_finalize (GObject * obj)
|
gst_facedetect_finalize (GObject * obj)
|
||||||
{
|
{
|
||||||
Gstfacedetect *filter = GST_FACEDETECT (obj);
|
GstFacedetect *filter = GST_FACEDETECT (obj);
|
||||||
|
|
||||||
if (filter->cvGray) {
|
if (filter->cvGray)
|
||||||
cvReleaseImage (&filter->cvGray);
|
cvReleaseImage (&filter->cvGray);
|
||||||
}
|
if (filter->cvStorage)
|
||||||
if (filter->cvStorage) {
|
|
||||||
cvReleaseMemStorage (&filter->cvStorage);
|
cvReleaseMemStorage (&filter->cvStorage);
|
||||||
}
|
|
||||||
|
|
||||||
g_free (filter->profile);
|
g_free (filter->face_profile);
|
||||||
|
g_free (filter->nose_profile);
|
||||||
|
g_free (filter->mouth_profile);
|
||||||
|
g_free (filter->eyes_profile);
|
||||||
|
|
||||||
|
if (filter->cvFaceDetect)
|
||||||
|
cvReleaseHaarClassifierCascade (&filter->cvFaceDetect);
|
||||||
|
if (filter->cvNoseDetect)
|
||||||
|
cvReleaseHaarClassifierCascade (&filter->cvNoseDetect);
|
||||||
|
if (filter->cvMouthDetect)
|
||||||
|
cvReleaseHaarClassifierCascade (&filter->cvMouthDetect);
|
||||||
|
if (filter->cvEyesDetect)
|
||||||
|
cvReleaseHaarClassifierCascade (&filter->cvEyesDetect);
|
||||||
|
|
||||||
G_OBJECT_CLASS (parent_class)->finalize (obj);
|
G_OBJECT_CLASS (parent_class)->finalize (obj);
|
||||||
}
|
}
|
||||||
|
@ -196,7 +227,7 @@ gst_facedetect_base_init (gpointer gclass)
|
||||||
|
|
||||||
/* initialize the facedetect's class */
|
/* initialize the facedetect's class */
|
||||||
static void
|
static void
|
||||||
gst_facedetect_class_init (GstfacedetectClass * klass)
|
gst_facedetect_class_init (GstFacedetectClass * klass)
|
||||||
{
|
{
|
||||||
GObjectClass *gobject_class;
|
GObjectClass *gobject_class;
|
||||||
GstOpencvVideoFilterClass *gstopencvbasefilter_class;
|
GstOpencvVideoFilterClass *gstopencvbasefilter_class;
|
||||||
|
@ -215,17 +246,31 @@ gst_facedetect_class_init (GstfacedetectClass * klass)
|
||||||
g_param_spec_boolean ("display", "Display",
|
g_param_spec_boolean ("display", "Display",
|
||||||
"Sets whether the detected faces should be highlighted in the output",
|
"Sets whether the detected faces should be highlighted in the output",
|
||||||
TRUE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
TRUE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
||||||
g_object_class_install_property (gobject_class, PROP_PROFILE,
|
|
||||||
g_param_spec_string ("profile", "Profile",
|
g_object_class_install_property (gobject_class, PROP_FACE_PROFILE,
|
||||||
|
g_param_spec_string ("profile", "Face profile",
|
||||||
"Location of Haar cascade file to use for face detection",
|
"Location of Haar cascade file to use for face detection",
|
||||||
DEFAULT_PROFILE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
DEFAULT_FACE_PROFILE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
||||||
|
g_object_class_install_property (gobject_class, PROP_NOSE_PROFILE,
|
||||||
|
g_param_spec_string ("nose-profile", "Nose profile",
|
||||||
|
"Location of Haar cascade file to use for nose detection",
|
||||||
|
DEFAULT_NOSE_PROFILE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
||||||
|
g_object_class_install_property (gobject_class, PROP_MOUTH_PROFILE,
|
||||||
|
g_param_spec_string ("mouth-profile", "Mouth profile",
|
||||||
|
"Location of Haar cascade file to use for mouth detection",
|
||||||
|
DEFAULT_MOUTH_PROFILE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
||||||
|
g_object_class_install_property (gobject_class, PROP_EYES_PROFILE,
|
||||||
|
g_param_spec_string ("eyes-profile", "Eyes profile",
|
||||||
|
"Location of Haar cascade file to use for eye-pair detection",
|
||||||
|
DEFAULT_EYES_PROFILE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
||||||
|
|
||||||
g_object_class_install_property (gobject_class, PROP_FLAGS,
|
g_object_class_install_property (gobject_class, PROP_FLAGS,
|
||||||
g_param_spec_flags ("flags", "Flags", "Flags to cvHaarDetectObjects",
|
g_param_spec_flags ("flags", "Flags", "Flags to cvHaarDetectObjects",
|
||||||
GST_TYPE_OPENCV_FACE_DETECT_FLAGS, DEFAULT_FLAGS,
|
GST_TYPE_OPENCV_FACE_DETECT_FLAGS, DEFAULT_FLAGS,
|
||||||
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
||||||
g_object_class_install_property (gobject_class, PROP_SCALE_FACTOR,
|
g_object_class_install_property (gobject_class, PROP_SCALE_FACTOR,
|
||||||
g_param_spec_double ("scale-factor", "Scale factor",
|
g_param_spec_double ("scale-factor", "Scale factor",
|
||||||
"Factor by which the windows is scaled after each scan",
|
"Factor by which the frame is scaled after each object scan",
|
||||||
1.1, 10.0, DEFAULT_SCALE_FACTOR,
|
1.1, 10.0, DEFAULT_SCALE_FACTOR,
|
||||||
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
||||||
g_object_class_install_property (gobject_class, PROP_MIN_NEIGHBORS,
|
g_object_class_install_property (gobject_class, PROP_MIN_NEIGHBORS,
|
||||||
|
@ -234,31 +279,39 @@ gst_facedetect_class_init (GstfacedetectClass * klass)
|
||||||
"an object", 0, G_MAXINT, DEFAULT_MIN_NEIGHBORS,
|
"an object", 0, G_MAXINT, DEFAULT_MIN_NEIGHBORS,
|
||||||
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
||||||
g_object_class_install_property (gobject_class, PROP_MIN_SIZE_WIDTH,
|
g_object_class_install_property (gobject_class, PROP_MIN_SIZE_WIDTH,
|
||||||
g_param_spec_int ("min-size-width", "Minimum size width",
|
g_param_spec_int ("min-size-width", "Minimum face width",
|
||||||
"Minimum window width size", 0, G_MAXINT, DEFAULT_MIN_SIZE_WIDTH,
|
"Minimum area width to be recognized as a face", 0, G_MAXINT,
|
||||||
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
DEFAULT_MIN_SIZE_WIDTH, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
||||||
g_object_class_install_property (gobject_class, PROP_MIN_SIZE_HEIGHT,
|
g_object_class_install_property (gobject_class, PROP_MIN_SIZE_HEIGHT,
|
||||||
g_param_spec_int ("min-size-height", "Minimum size height",
|
g_param_spec_int ("min-size-height", "Minimum face height",
|
||||||
"Minimum window height size", 0, G_MAXINT, DEFAULT_MIN_SIZE_HEIGHT,
|
"Minimum area height to be recognized as a face", 0, G_MAXINT,
|
||||||
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
DEFAULT_MIN_SIZE_HEIGHT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
||||||
}
|
}
|
||||||
|
|
||||||
/* initialize the new element
|
/* initialize the new element
|
||||||
* instantiate pads and add them to element
|
|
||||||
* set pad calback functions
|
|
||||||
* initialize instance structure
|
* initialize instance structure
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
gst_facedetect_init (Gstfacedetect * filter, GstfacedetectClass * gclass)
|
gst_facedetect_init (GstFacedetect * filter, GstFacedetectClass * gclass)
|
||||||
{
|
{
|
||||||
filter->profile = g_strdup (DEFAULT_PROFILE);
|
filter->face_profile = g_strdup (DEFAULT_FACE_PROFILE);
|
||||||
|
filter->nose_profile = g_strdup (DEFAULT_NOSE_PROFILE);
|
||||||
|
filter->mouth_profile = g_strdup (DEFAULT_MOUTH_PROFILE);
|
||||||
|
filter->eyes_profile = g_strdup (DEFAULT_EYES_PROFILE);
|
||||||
filter->display = TRUE;
|
filter->display = TRUE;
|
||||||
filter->scale_factor = DEFAULT_SCALE_FACTOR;
|
filter->scale_factor = DEFAULT_SCALE_FACTOR;
|
||||||
filter->min_neighbors = DEFAULT_MIN_NEIGHBORS;
|
filter->min_neighbors = DEFAULT_MIN_NEIGHBORS;
|
||||||
filter->flags = DEFAULT_FLAGS;
|
filter->flags = DEFAULT_FLAGS;
|
||||||
filter->min_size_width = DEFAULT_MIN_SIZE_WIDTH;
|
filter->min_size_width = DEFAULT_MIN_SIZE_WIDTH;
|
||||||
filter->min_size_height = DEFAULT_MIN_SIZE_HEIGHT;
|
filter->min_size_height = DEFAULT_MIN_SIZE_HEIGHT;
|
||||||
gst_facedetect_load_profile (filter);
|
filter->cvFaceDetect =
|
||||||
|
gst_facedetect_load_profile (filter, filter->face_profile);
|
||||||
|
filter->cvNoseDetect =
|
||||||
|
gst_facedetect_load_profile (filter, filter->nose_profile);
|
||||||
|
filter->cvMouthDetect =
|
||||||
|
gst_facedetect_load_profile (filter, filter->mouth_profile);
|
||||||
|
filter->cvEyesDetect =
|
||||||
|
gst_facedetect_load_profile (filter, filter->eyes_profile);
|
||||||
|
|
||||||
gst_opencv_video_filter_set_in_place (GST_OPENCV_VIDEO_FILTER_CAST (filter),
|
gst_opencv_video_filter_set_in_place (GST_OPENCV_VIDEO_FILTER_CAST (filter),
|
||||||
TRUE);
|
TRUE);
|
||||||
|
@ -268,13 +321,40 @@ static void
|
||||||
gst_facedetect_set_property (GObject * object, guint prop_id,
|
gst_facedetect_set_property (GObject * object, guint prop_id,
|
||||||
const GValue * value, GParamSpec * pspec)
|
const GValue * value, GParamSpec * pspec)
|
||||||
{
|
{
|
||||||
Gstfacedetect *filter = GST_FACEDETECT (object);
|
GstFacedetect *filter = GST_FACEDETECT (object);
|
||||||
|
|
||||||
switch (prop_id) {
|
switch (prop_id) {
|
||||||
case PROP_PROFILE:
|
case PROP_FACE_PROFILE:
|
||||||
g_free (filter->profile);
|
g_free (filter->face_profile);
|
||||||
filter->profile = g_value_dup_string (value);
|
if (filter->cvFaceDetect)
|
||||||
gst_facedetect_load_profile (filter);
|
cvReleaseHaarClassifierCascade (&filter->cvFaceDetect);
|
||||||
|
filter->face_profile = g_value_dup_string (value);
|
||||||
|
filter->cvFaceDetect =
|
||||||
|
gst_facedetect_load_profile (filter, filter->face_profile);
|
||||||
|
break;
|
||||||
|
case PROP_NOSE_PROFILE:
|
||||||
|
g_free (filter->nose_profile);
|
||||||
|
if (filter->cvNoseDetect)
|
||||||
|
cvReleaseHaarClassifierCascade (&filter->cvNoseDetect);
|
||||||
|
filter->nose_profile = g_value_dup_string (value);
|
||||||
|
filter->cvNoseDetect =
|
||||||
|
gst_facedetect_load_profile (filter, filter->nose_profile);
|
||||||
|
break;
|
||||||
|
case PROP_MOUTH_PROFILE:
|
||||||
|
g_free (filter->mouth_profile);
|
||||||
|
if (filter->cvMouthDetect)
|
||||||
|
cvReleaseHaarClassifierCascade (&filter->cvMouthDetect);
|
||||||
|
filter->mouth_profile = g_value_dup_string (value);
|
||||||
|
filter->cvMouthDetect =
|
||||||
|
gst_facedetect_load_profile (filter, filter->mouth_profile);
|
||||||
|
break;
|
||||||
|
case PROP_EYES_PROFILE:
|
||||||
|
g_free (filter->eyes_profile);
|
||||||
|
if (filter->cvEyesDetect)
|
||||||
|
cvReleaseHaarClassifierCascade (&filter->cvEyesDetect);
|
||||||
|
filter->eyes_profile = g_value_dup_string (value);
|
||||||
|
filter->cvEyesDetect =
|
||||||
|
gst_facedetect_load_profile (filter, filter->eyes_profile);
|
||||||
break;
|
break;
|
||||||
case PROP_DISPLAY:
|
case PROP_DISPLAY:
|
||||||
filter->display = g_value_get_boolean (value);
|
filter->display = g_value_get_boolean (value);
|
||||||
|
@ -304,11 +384,20 @@ static void
|
||||||
gst_facedetect_get_property (GObject * object, guint prop_id,
|
gst_facedetect_get_property (GObject * object, guint prop_id,
|
||||||
GValue * value, GParamSpec * pspec)
|
GValue * value, GParamSpec * pspec)
|
||||||
{
|
{
|
||||||
Gstfacedetect *filter = GST_FACEDETECT (object);
|
GstFacedetect *filter = GST_FACEDETECT (object);
|
||||||
|
|
||||||
switch (prop_id) {
|
switch (prop_id) {
|
||||||
case PROP_PROFILE:
|
case PROP_FACE_PROFILE:
|
||||||
g_value_set_string (value, filter->profile);
|
g_value_set_string (value, filter->face_profile);
|
||||||
|
break;
|
||||||
|
case PROP_NOSE_PROFILE:
|
||||||
|
g_value_set_string (value, filter->nose_profile);
|
||||||
|
break;
|
||||||
|
case PROP_MOUTH_PROFILE:
|
||||||
|
g_value_set_string (value, filter->mouth_profile);
|
||||||
|
break;
|
||||||
|
case PROP_EYES_PROFILE:
|
||||||
|
g_value_set_string (value, filter->eyes_profile);
|
||||||
break;
|
break;
|
||||||
case PROP_DISPLAY:
|
case PROP_DISPLAY:
|
||||||
g_value_set_boolean (value, filter->display);
|
g_value_set_boolean (value, filter->display);
|
||||||
|
@ -342,7 +431,7 @@ gst_facedetect_set_caps (GstOpencvVideoFilter * transform, gint in_width,
|
||||||
gint in_height, gint in_depth, gint in_channels,
|
gint in_height, gint in_depth, gint in_channels,
|
||||||
gint out_width, gint out_height, gint out_depth, gint out_channels)
|
gint out_width, gint out_height, gint out_depth, gint out_channels)
|
||||||
{
|
{
|
||||||
Gstfacedetect *filter;
|
GstFacedetect *filter;
|
||||||
|
|
||||||
filter = GST_FACEDETECT (transform);
|
filter = GST_FACEDETECT (transform);
|
||||||
|
|
||||||
|
@ -361,7 +450,7 @@ gst_facedetect_set_caps (GstOpencvVideoFilter * transform, gint in_width,
|
||||||
}
|
}
|
||||||
|
|
||||||
static GstMessage *
|
static GstMessage *
|
||||||
gst_facedetect_message_new (Gstfacedetect * filter, GstBuffer * buf)
|
gst_facedetect_message_new (GstFacedetect * filter, GstBuffer * buf)
|
||||||
{
|
{
|
||||||
GstBaseTransform *trans = GST_BASE_TRANSFORM_CAST (filter);
|
GstBaseTransform *trans = GST_BASE_TRANSFORM_CAST (filter);
|
||||||
GstStructure *s;
|
GstStructure *s;
|
||||||
|
@ -389,21 +478,29 @@ static GstFlowReturn
|
||||||
gst_facedetect_transform_ip (GstOpencvVideoFilter * base, GstBuffer * buf,
|
gst_facedetect_transform_ip (GstOpencvVideoFilter * base, GstBuffer * buf,
|
||||||
IplImage * img)
|
IplImage * img)
|
||||||
{
|
{
|
||||||
Gstfacedetect *filter;
|
GstFacedetect *filter = GST_FACEDETECT (base);
|
||||||
CvSeq *faces;
|
|
||||||
int i;
|
|
||||||
|
|
||||||
filter = GST_FACEDETECT (base);
|
if (filter->cvFaceDetect) {
|
||||||
|
|
||||||
cvCvtColor (img, filter->cvGray, CV_RGB2GRAY);
|
|
||||||
cvClearMemStorage (filter->cvStorage);
|
|
||||||
|
|
||||||
if (filter->cvCascade) {
|
|
||||||
GstMessage *msg = NULL;
|
GstMessage *msg = NULL;
|
||||||
GValue facelist = { 0 };
|
GValue facelist = { 0 };
|
||||||
|
CvSeq *faces;
|
||||||
|
CvSeq *mouth, *nose, *eyes;
|
||||||
|
gint i;
|
||||||
|
gboolean do_display = FALSE;
|
||||||
|
|
||||||
|
if (filter->display) {
|
||||||
|
if (gst_buffer_is_writable (buf)) {
|
||||||
|
do_display = TRUE;
|
||||||
|
} else {
|
||||||
|
GST_LOG_OBJECT (filter, "Buffer is not writable, not drawing faces.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cvCvtColor (img, filter->cvGray, CV_RGB2GRAY);
|
||||||
|
cvClearMemStorage (filter->cvStorage);
|
||||||
|
|
||||||
faces =
|
faces =
|
||||||
cvHaarDetectObjects (filter->cvGray, filter->cvCascade,
|
cvHaarDetectObjects (filter->cvGray, filter->cvFaceDetect,
|
||||||
filter->cvStorage, filter->scale_factor, filter->min_neighbors,
|
filter->cvStorage, filter->scale_factor, filter->min_neighbors,
|
||||||
filter->flags, cvSize (filter->min_size_width, filter->min_size_height)
|
filter->flags, cvSize (filter->min_size_width, filter->min_size_height)
|
||||||
#if (CV_MAJOR_VERSION >= 2) && (CV_MINOR_VERSION >= 2)
|
#if (CV_MAJOR_VERSION >= 2) && (CV_MINOR_VERSION >= 2)
|
||||||
|
@ -419,36 +516,168 @@ gst_facedetect_transform_ip (GstOpencvVideoFilter * base, GstBuffer * buf,
|
||||||
for (i = 0; i < (faces ? faces->total : 0); i++) {
|
for (i = 0; i < (faces ? faces->total : 0); i++) {
|
||||||
CvRect *r = (CvRect *) cvGetSeqElem (faces, i);
|
CvRect *r = (CvRect *) cvGetSeqElem (faces, i);
|
||||||
GValue value = { 0 };
|
GValue value = { 0 };
|
||||||
|
GstStructure *s;
|
||||||
|
guint mw = filter->min_size_width / 8;
|
||||||
|
guint mh = filter->min_size_height / 8;
|
||||||
|
guint rnx, rny, rnw, rnh;
|
||||||
|
guint rmx, rmy, rmw, rmh;
|
||||||
|
guint rex, rey, rew, reh;
|
||||||
|
gboolean have_nose, have_mouth, have_eyes;
|
||||||
|
|
||||||
GstStructure *s = gst_structure_new ("face",
|
/* detect face features */
|
||||||
|
|
||||||
|
rnx = r->x + r->width / 4;
|
||||||
|
rny = r->y + r->height / 4;
|
||||||
|
rnw = r->width / 2;
|
||||||
|
rnh = r->height / 2;
|
||||||
|
cvSetImageROI (filter->cvGray, cvRect (rnx, rny, rnw, rnh));
|
||||||
|
nose =
|
||||||
|
cvHaarDetectObjects (filter->cvGray, filter->cvNoseDetect,
|
||||||
|
filter->cvStorage, filter->scale_factor, filter->min_neighbors,
|
||||||
|
filter->flags, cvSize (mw, mh)
|
||||||
|
#if (CV_MAJOR_VERSION >= 2) && (CV_MINOR_VERSION >= 2)
|
||||||
|
, cvSize (mw + 2, mh + 2)
|
||||||
|
#endif
|
||||||
|
);
|
||||||
|
have_nose = (nose && nose->total);
|
||||||
|
cvResetImageROI (filter->cvGray);
|
||||||
|
|
||||||
|
rmx = r->x;
|
||||||
|
rmy = r->y + r->height / 2;
|
||||||
|
rmw = r->width;
|
||||||
|
rmh = r->height / 2;
|
||||||
|
cvSetImageROI (filter->cvGray, cvRect (rmx, rmy, rmw, rmh));
|
||||||
|
mouth =
|
||||||
|
cvHaarDetectObjects (filter->cvGray, filter->cvMouthDetect,
|
||||||
|
filter->cvStorage, filter->scale_factor, filter->min_neighbors,
|
||||||
|
filter->flags, cvSize (mw, mh)
|
||||||
|
#if (CV_MAJOR_VERSION >= 2) && (CV_MINOR_VERSION >= 2)
|
||||||
|
, cvSize (mw + 2, mh + 2)
|
||||||
|
#endif
|
||||||
|
);
|
||||||
|
have_mouth = (mouth && mouth->total);
|
||||||
|
cvResetImageROI (filter->cvGray);
|
||||||
|
|
||||||
|
rex = r->x;
|
||||||
|
rey = r->y;
|
||||||
|
rew = r->width;
|
||||||
|
reh = r->height / 2;
|
||||||
|
cvSetImageROI (filter->cvGray, cvRect (rex, rey, rew, reh));
|
||||||
|
eyes =
|
||||||
|
cvHaarDetectObjects (filter->cvGray, filter->cvEyesDetect,
|
||||||
|
filter->cvStorage, filter->scale_factor, filter->min_neighbors,
|
||||||
|
filter->flags, cvSize (mw, mh)
|
||||||
|
#if (CV_MAJOR_VERSION >= 2) && (CV_MINOR_VERSION >= 2)
|
||||||
|
, cvSize (mw + 2, mh + 2)
|
||||||
|
#endif
|
||||||
|
);
|
||||||
|
have_eyes = (eyes && eyes->total);
|
||||||
|
cvResetImageROI (filter->cvGray);
|
||||||
|
|
||||||
|
GST_LOG_OBJECT (filter,
|
||||||
|
"%2d/%2d: x,y = %4u,%4u: w.h = %4u,%4u : features(e,n,m) = %d,%d,%d",
|
||||||
|
i, faces->total, r->x, r->y, r->width, r->height,
|
||||||
|
have_eyes, have_nose, have_mouth);
|
||||||
|
|
||||||
|
/* ignore 'face' where we don't fix mount/nose/eyes ? */
|
||||||
|
if (!(have_eyes && have_nose && have_mouth))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
s = gst_structure_new ("face",
|
||||||
"x", G_TYPE_UINT, r->x,
|
"x", G_TYPE_UINT, r->x,
|
||||||
"y", G_TYPE_UINT, r->y,
|
"y", G_TYPE_UINT, r->y,
|
||||||
"width", G_TYPE_UINT, r->width,
|
"width", G_TYPE_UINT, r->width,
|
||||||
"height", G_TYPE_UINT, r->height, NULL);
|
"height", G_TYPE_UINT, r->height, NULL);
|
||||||
|
if (nose && nose->total) {
|
||||||
GstMessage *m = gst_message_new_element (GST_OBJECT (filter), s);
|
CvRect *sr = (CvRect *) cvGetSeqElem (nose, 0);
|
||||||
|
GST_LOG_OBJECT (filter, "nose/%d: x,y = %4u,%4u: w.h = %4u,%4u",
|
||||||
|
nose->total, rnx + sr->x, rny + sr->y, sr->width, sr->height);
|
||||||
|
gst_structure_set (s,
|
||||||
|
"nose->x", G_TYPE_UINT, rnx + sr->x,
|
||||||
|
"nose->y", G_TYPE_UINT, rny + sr->y,
|
||||||
|
"nose->width", G_TYPE_UINT, sr->width,
|
||||||
|
"nose->height", G_TYPE_UINT, sr->height, NULL);
|
||||||
|
}
|
||||||
|
if (mouth && mouth->total) {
|
||||||
|
CvRect *sr = (CvRect *) cvGetSeqElem (mouth, 0);
|
||||||
|
GST_LOG_OBJECT (filter, "mouth/%d: x,y = %4u,%4u: w.h = %4u,%4u",
|
||||||
|
mouth->total, rmx + sr->x, rmy + sr->y, sr->width, sr->height);
|
||||||
|
gst_structure_set (s,
|
||||||
|
"mouth->x", G_TYPE_UINT, rmx + sr->x,
|
||||||
|
"mouth->y", G_TYPE_UINT, rmy + sr->y,
|
||||||
|
"mouth->width", G_TYPE_UINT, sr->width,
|
||||||
|
"mouth->height", G_TYPE_UINT, sr->height, NULL);
|
||||||
|
}
|
||||||
|
if (eyes && eyes->total) {
|
||||||
|
CvRect *sr = (CvRect *) cvGetSeqElem (eyes, 0);
|
||||||
|
GST_LOG_OBJECT (filter, "eyes/%d: x,y = %4u,%4u: w.h = %4u,%4u",
|
||||||
|
eyes->total, rex + sr->x, rey + sr->y, sr->width, sr->height);
|
||||||
|
gst_structure_set (s,
|
||||||
|
"eyes->x", G_TYPE_UINT, rex + sr->x,
|
||||||
|
"eyes->y", G_TYPE_UINT, rey + sr->y,
|
||||||
|
"eyes->width", G_TYPE_UINT, sr->width,
|
||||||
|
"eyes->height", G_TYPE_UINT, sr->height, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
g_value_init (&value, GST_TYPE_STRUCTURE);
|
g_value_init (&value, GST_TYPE_STRUCTURE);
|
||||||
gst_value_set_structure (&value, s);
|
gst_value_set_structure (&value, s);
|
||||||
gst_value_list_append_value (&facelist, &value);
|
gst_value_list_append_value (&facelist, &value);
|
||||||
g_value_unset (&value);
|
g_value_unset (&value);
|
||||||
|
|
||||||
gst_element_post_message (GST_ELEMENT (filter), m);
|
if (do_display) {
|
||||||
|
CvPoint center;
|
||||||
|
CvSize axes;
|
||||||
|
gdouble w, h;
|
||||||
|
gint cb = 255 - ((i & 3) << 7);
|
||||||
|
gint cg = 255 - ((i & 12) << 5);
|
||||||
|
gint cr = 255 - ((i & 48) << 3);
|
||||||
|
|
||||||
if (filter->display) {
|
w = r->width / 2;
|
||||||
if (gst_buffer_is_writable (buf)) {
|
h = r->height / 2;
|
||||||
CvPoint center;
|
center.x = cvRound ((r->x + w));
|
||||||
int radius;
|
center.y = cvRound ((r->y + h));
|
||||||
center.x = cvRound ((r->x + r->width * 0.5));
|
axes.width = w;
|
||||||
center.y = cvRound ((r->y + r->height * 0.5));
|
axes.height = h * 1.25; /* tweak for face form */
|
||||||
radius = cvRound ((r->width + r->height) * 0.25);
|
cvEllipse (img, center, axes, 0.0, 0.0, 360.0, CV_RGB (cr, cg, cb),
|
||||||
cvCircle (img, center, radius, CV_RGB (255, 32, 32), 3, 8, 0);
|
3, 8, 0);
|
||||||
} else {
|
|
||||||
GST_DEBUG_OBJECT (filter, "Buffer is not writable, not drawing "
|
if (nose && nose->total) {
|
||||||
"circles for faces");
|
CvRect *sr = (CvRect *) cvGetSeqElem (nose, 0);
|
||||||
|
|
||||||
|
w = sr->width / 2;
|
||||||
|
h = sr->height / 2;
|
||||||
|
center.x = cvRound ((rnx + sr->x + w));
|
||||||
|
center.y = cvRound ((rny + sr->y + h));
|
||||||
|
axes.width = w;
|
||||||
|
axes.height = h * 1.25; /* tweak for nose form */
|
||||||
|
cvEllipse (img, center, axes, 0.0, 0.0, 360.0, CV_RGB (cr, cg, cb),
|
||||||
|
1, 8, 0);
|
||||||
|
}
|
||||||
|
if (mouth && mouth->total) {
|
||||||
|
CvRect *sr = (CvRect *) cvGetSeqElem (mouth, 0);
|
||||||
|
|
||||||
|
w = sr->width / 2;
|
||||||
|
h = sr->height / 2;
|
||||||
|
center.x = cvRound ((rmx + sr->x + w));
|
||||||
|
center.y = cvRound ((rmy + sr->y + h));
|
||||||
|
axes.width = w * 1.5; /* tweak for mouth form */
|
||||||
|
axes.height = h;
|
||||||
|
cvEllipse (img, center, axes, 0.0, 0.0, 360.0, CV_RGB (cr, cg, cb),
|
||||||
|
1, 8, 0);
|
||||||
|
}
|
||||||
|
if (eyes && eyes->total) {
|
||||||
|
CvRect *sr = (CvRect *) cvGetSeqElem (eyes, 0);
|
||||||
|
|
||||||
|
w = sr->width / 2;
|
||||||
|
h = sr->height / 2;
|
||||||
|
center.x = cvRound ((rex + sr->x + w));
|
||||||
|
center.y = cvRound ((rey + sr->y + h));
|
||||||
|
axes.width = w * 1.5; /* tweak for eyes form */
|
||||||
|
axes.height = h;
|
||||||
|
cvEllipse (img, center, axes, 0.0, 0.0, 360.0, CV_RGB (cr, cg, cb),
|
||||||
|
1, 8, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (msg) {
|
if (msg) {
|
||||||
|
@ -462,14 +691,16 @@ gst_facedetect_transform_ip (GstOpencvVideoFilter * base, GstBuffer * buf,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static void
|
static CvHaarClassifierCascade *
|
||||||
gst_facedetect_load_profile (Gstfacedetect * filter)
|
gst_facedetect_load_profile (GstFacedetect * filter, gchar * profile)
|
||||||
{
|
{
|
||||||
filter->cvCascade =
|
CvHaarClassifierCascade *cascade;
|
||||||
(CvHaarClassifierCascade *) cvLoad (filter->profile, 0, 0, 0);
|
|
||||||
if (!filter->cvCascade) {
|
if (!(cascade = (CvHaarClassifierCascade *) cvLoad (profile, 0, 0, 0))) {
|
||||||
GST_WARNING ("Couldn't load Haar classifier cascade: %s.", filter->profile);
|
GST_WARNING_OBJECT (filter, "Couldn't load Haar classifier cascade: %s.",
|
||||||
|
profile);
|
||||||
}
|
}
|
||||||
|
return cascade;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
* Copyright (C) 2005 Thomas Vander Stichele <thomas@apestaart.org>
|
* Copyright (C) 2005 Thomas Vander Stichele <thomas@apestaart.org>
|
||||||
* Copyright (C) 2005 Ronald S. Bultje <rbultje@ronald.bitfreak.net>
|
* Copyright (C) 2005 Ronald S. Bultje <rbultje@ronald.bitfreak.net>
|
||||||
* Copyright (C) 2008 Michael Sheldon <mike@mikeasoft.com>
|
* Copyright (C) 2008 Michael Sheldon <mike@mikeasoft.com>
|
||||||
|
* Copyright (C) 2011 Stefan Sauer <ensonic@users.sf.net>
|
||||||
*
|
*
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||||
* copy of this software and associated documentation files (the "Software"),
|
* copy of this software and associated documentation files (the "Software"),
|
||||||
|
@ -59,23 +60,26 @@ G_BEGIN_DECLS
|
||||||
#define GST_TYPE_FACEDETECT \
|
#define GST_TYPE_FACEDETECT \
|
||||||
(gst_facedetect_get_type())
|
(gst_facedetect_get_type())
|
||||||
#define GST_FACEDETECT(obj) \
|
#define GST_FACEDETECT(obj) \
|
||||||
(G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_FACEDETECT,Gstfacedetect))
|
(G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_FACEDETECT,GstFacedetect))
|
||||||
#define GST_FACEDETECT_CLASS(klass) \
|
#define GST_FACEDETECT_CLASS(klass) \
|
||||||
(G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_FACEDETECT,GstfacedetectClass))
|
(G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_FACEDETECT,GstFacedetectClass))
|
||||||
#define GST_IS_FACEDETECT(obj) \
|
#define GST_IS_FACEDETECT(obj) \
|
||||||
(G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_FACEDETECT))
|
(G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_FACEDETECT))
|
||||||
#define GST_IS_FACEDETECT_CLASS(klass) \
|
#define GST_IS_FACEDETECT_CLASS(klass) \
|
||||||
(G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_FACEDETECT))
|
(G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_FACEDETECT))
|
||||||
typedef struct _Gstfacedetect Gstfacedetect;
|
typedef struct _GstFacedetect GstFacedetect;
|
||||||
typedef struct _GstfacedetectClass GstfacedetectClass;
|
typedef struct _GstFacedetectClass GstFacedetectClass;
|
||||||
|
|
||||||
struct _Gstfacedetect
|
struct _GstFacedetect
|
||||||
{
|
{
|
||||||
GstOpencvVideoFilter element;
|
GstOpencvVideoFilter element;
|
||||||
|
|
||||||
gboolean display;
|
gboolean display;
|
||||||
|
|
||||||
gchar *profile;
|
gchar *face_profile;
|
||||||
|
gchar *nose_profile;
|
||||||
|
gchar *mouth_profile;
|
||||||
|
gchar *eyes_profile;
|
||||||
gdouble scale_factor;
|
gdouble scale_factor;
|
||||||
gint min_neighbors;
|
gint min_neighbors;
|
||||||
gint flags;
|
gint flags;
|
||||||
|
@ -83,11 +87,14 @@ struct _Gstfacedetect
|
||||||
gint min_size_height;
|
gint min_size_height;
|
||||||
|
|
||||||
IplImage *cvGray;
|
IplImage *cvGray;
|
||||||
CvHaarClassifierCascade *cvCascade;
|
CvHaarClassifierCascade *cvFaceDetect;
|
||||||
|
CvHaarClassifierCascade *cvNoseDetect;
|
||||||
|
CvHaarClassifierCascade *cvMouthDetect;
|
||||||
|
CvHaarClassifierCascade *cvEyesDetect;
|
||||||
CvMemStorage *cvStorage;
|
CvMemStorage *cvStorage;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct _GstfacedetectClass
|
struct _GstFacedetectClass
|
||||||
{
|
{
|
||||||
GstOpencvVideoFilterClass parent_class;
|
GstOpencvVideoFilterClass parent_class;
|
||||||
};
|
};
|
||||||
|
|
|
@ -67,14 +67,14 @@ GST_STATIC_PAD_TEMPLATE ("sink",
|
||||||
|
|
||||||
G_DEFINE_TYPE (GstOpusDec, gst_opus_dec, GST_TYPE_AUDIO_DECODER);
|
G_DEFINE_TYPE (GstOpusDec, gst_opus_dec, GST_TYPE_AUDIO_DECODER);
|
||||||
|
|
||||||
|
static GstFlowReturn gst_opus_dec_parse_header (GstOpusDec * dec,
|
||||||
|
GstBuffer * buf);
|
||||||
static gboolean gst_opus_dec_start (GstAudioDecoder * dec);
|
static gboolean gst_opus_dec_start (GstAudioDecoder * dec);
|
||||||
static gboolean gst_opus_dec_stop (GstAudioDecoder * dec);
|
static gboolean gst_opus_dec_stop (GstAudioDecoder * dec);
|
||||||
static GstFlowReturn gst_opus_dec_handle_frame (GstAudioDecoder * dec,
|
static GstFlowReturn gst_opus_dec_handle_frame (GstAudioDecoder * dec,
|
||||||
GstBuffer * buffer);
|
GstBuffer * buffer);
|
||||||
static gboolean gst_opus_dec_set_format (GstAudioDecoder * bdec,
|
static gboolean gst_opus_dec_set_format (GstAudioDecoder * bdec,
|
||||||
GstCaps * caps);
|
GstCaps * caps);
|
||||||
static GstFlowReturn opus_dec_chain_parse_data (GstOpusDec * dec,
|
|
||||||
GstBuffer * buf, GstClockTime timestamp, GstClockTime duration);
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
gst_opus_dec_class_init (GstOpusDecClass * klass)
|
gst_opus_dec_class_init (GstOpusDecClass * klass)
|
||||||
|
@ -112,8 +112,6 @@ gst_opus_dec_reset (GstOpusDec * dec)
|
||||||
dec->state = NULL;
|
dec->state = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
dec->next_ts = 0;
|
|
||||||
|
|
||||||
gst_buffer_replace (&dec->streamheader, NULL);
|
gst_buffer_replace (&dec->streamheader, NULL);
|
||||||
gst_buffer_replace (&dec->vorbiscomment, NULL);
|
gst_buffer_replace (&dec->vorbiscomment, NULL);
|
||||||
}
|
}
|
||||||
|
@ -167,17 +165,19 @@ gst_opus_dec_setup_from_peer_caps (GstOpusDec * dec)
|
||||||
{
|
{
|
||||||
GstPad *srcpad, *peer;
|
GstPad *srcpad, *peer;
|
||||||
GstStructure *s;
|
GstStructure *s;
|
||||||
GstCaps *caps, *template_caps, *peer_caps;
|
GstCaps *caps;
|
||||||
|
const GstCaps *template_caps;
|
||||||
|
const GstCaps *peer_caps;
|
||||||
|
|
||||||
srcpad = GST_AUDIO_DECODER_SRC_PAD (dec);
|
srcpad = GST_AUDIO_DECODER_SRC_PAD (dec);
|
||||||
peer = gst_pad_get_peer (srcpad);
|
peer = gst_pad_get_peer (srcpad);
|
||||||
|
|
||||||
if (peer) {
|
if (peer) {
|
||||||
template_caps = gst_pad_get_pad_template_caps (srcpad);
|
template_caps = gst_pad_get_pad_template_caps (srcpad);
|
||||||
peer_caps = gst_pad_get_caps (peer, NULL);
|
peer_caps = gst_pad_get_caps (peer);
|
||||||
GST_DEBUG_OBJECT (dec, "Peer caps: %" GST_PTR_FORMAT, peer_caps);
|
GST_DEBUG_OBJECT (dec, "Peer caps: %" GST_PTR_FORMAT, peer_caps);
|
||||||
caps = gst_caps_intersect (template_caps, peer_caps);
|
caps = gst_caps_intersect (template_caps, peer_caps);
|
||||||
gst_caps_fixate (caps);
|
gst_pad_fixate_caps (peer, caps);
|
||||||
GST_DEBUG_OBJECT (dec, "Fixated caps: %" GST_PTR_FORMAT, caps);
|
GST_DEBUG_OBJECT (dec, "Fixated caps: %" GST_PTR_FORMAT, caps);
|
||||||
|
|
||||||
s = gst_caps_get_structure (caps, 0);
|
s = gst_caps_get_structure (caps, 0);
|
||||||
|
@ -203,8 +203,7 @@ gst_opus_dec_setup_from_peer_caps (GstOpusDec * dec)
|
||||||
}
|
}
|
||||||
|
|
||||||
static GstFlowReturn
|
static GstFlowReturn
|
||||||
opus_dec_chain_parse_data (GstOpusDec * dec, GstBuffer * buf,
|
opus_dec_chain_parse_data (GstOpusDec * dec, GstBuffer * buf)
|
||||||
GstClockTime timestamp, GstClockTime duration)
|
|
||||||
{
|
{
|
||||||
GstFlowReturn res = GST_FLOW_OK;
|
GstFlowReturn res = GST_FLOW_OK;
|
||||||
gsize size, out_size;
|
gsize size, out_size;
|
||||||
|
@ -218,6 +217,8 @@ opus_dec_chain_parse_data (GstOpusDec * dec, GstBuffer * buf,
|
||||||
if (dec->state == NULL) {
|
if (dec->state == NULL) {
|
||||||
gst_opus_dec_setup_from_peer_caps (dec);
|
gst_opus_dec_setup_from_peer_caps (dec);
|
||||||
|
|
||||||
|
GST_DEBUG_OBJECT (dec, "Creating decoder with %d channels, %d Hz",
|
||||||
|
dec->n_channels, dec->sample_rate);
|
||||||
dec->state = opus_decoder_create (dec->sample_rate, dec->n_channels, &err);
|
dec->state = opus_decoder_create (dec->sample_rate, dec->n_channels, &err);
|
||||||
if (!dec->state || err != OPUS_OK)
|
if (!dec->state || err != OPUS_OK)
|
||||||
goto creation_failed;
|
goto creation_failed;
|
||||||
|
@ -227,8 +228,6 @@ opus_dec_chain_parse_data (GstOpusDec * dec, GstBuffer * buf,
|
||||||
data = gst_buffer_map (buf, &size, NULL, GST_MAP_READ);
|
data = gst_buffer_map (buf, &size, NULL, GST_MAP_READ);
|
||||||
|
|
||||||
GST_DEBUG_OBJECT (dec, "received buffer of size %u", size);
|
GST_DEBUG_OBJECT (dec, "received buffer of size %u", size);
|
||||||
|
|
||||||
/* copy timestamp */
|
|
||||||
} else {
|
} else {
|
||||||
/* concealment data, pass NULL as the bits parameters */
|
/* concealment data, pass NULL as the bits parameters */
|
||||||
GST_DEBUG_OBJECT (dec, "creating concealment data");
|
GST_DEBUG_OBJECT (dec, "creating concealment data");
|
||||||
|
@ -261,20 +260,6 @@ opus_dec_chain_parse_data (GstOpusDec * dec, GstBuffer * buf,
|
||||||
}
|
}
|
||||||
GST_DEBUG_OBJECT (dec, "decoded %d samples", n);
|
GST_DEBUG_OBJECT (dec, "decoded %d samples", n);
|
||||||
|
|
||||||
if (GST_CLOCK_TIME_IS_VALID (timestamp)) {
|
|
||||||
GST_BUFFER_TIMESTAMP (outbuf) = timestamp;
|
|
||||||
} else {
|
|
||||||
GST_BUFFER_TIMESTAMP (outbuf) = dec->next_ts;
|
|
||||||
}
|
|
||||||
|
|
||||||
GST_BUFFER_DURATION (outbuf) =
|
|
||||||
gst_util_uint64_scale (n, GST_SECOND, dec->sample_rate);
|
|
||||||
dec->next_ts = GST_BUFFER_TIMESTAMP (outbuf) + GST_BUFFER_DURATION (outbuf);
|
|
||||||
|
|
||||||
GST_LOG_OBJECT (dec, "pushing buffer with ts=%" GST_TIME_FORMAT ", dur=%"
|
|
||||||
GST_TIME_FORMAT, GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (outbuf)),
|
|
||||||
GST_TIME_ARGS (GST_BUFFER_DURATION (outbuf)));
|
|
||||||
|
|
||||||
res = gst_audio_decoder_finish_frame (GST_AUDIO_DECODER (dec), outbuf, 1);
|
res = gst_audio_decoder_finish_frame (GST_AUDIO_DECODER (dec), outbuf, 1);
|
||||||
|
|
||||||
if (res != GST_FLOW_OK)
|
if (res != GST_FLOW_OK)
|
||||||
|
@ -395,8 +380,7 @@ gst_opus_dec_handle_frame (GstAudioDecoder * adec, GstBuffer * buf)
|
||||||
gst_audio_decoder_finish_frame (adec, NULL, 1);
|
gst_audio_decoder_finish_frame (adec, NULL, 1);
|
||||||
res = GST_FLOW_OK;
|
res = GST_FLOW_OK;
|
||||||
} else {
|
} else {
|
||||||
res = opus_dec_chain_parse_data (dec, buf, GST_BUFFER_TIMESTAMP (buf),
|
res = opus_dec_chain_parse_data (dec, buf);
|
||||||
GST_BUFFER_DURATION (buf));
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
/* Otherwise fall back to packet counting and assume that the
|
/* Otherwise fall back to packet counting and assume that the
|
||||||
|
@ -408,8 +392,7 @@ gst_opus_dec_handle_frame (GstAudioDecoder * adec, GstBuffer * buf)
|
||||||
res = gst_opus_dec_parse_header (dec, buf);
|
res = gst_opus_dec_parse_header (dec, buf);
|
||||||
gst_audio_decoder_finish_frame (adec, NULL, 1);
|
gst_audio_decoder_finish_frame (adec, NULL, 1);
|
||||||
} else {
|
} else {
|
||||||
res = opus_dec_chain_parse_data (dec, buf, GST_BUFFER_TIMESTAMP (buf),
|
res = opus_dec_chain_parse_data (dec, buf);
|
||||||
GST_BUFFER_DURATION (buf));
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 1:
|
case 1:
|
||||||
|
@ -418,14 +401,12 @@ gst_opus_dec_handle_frame (GstAudioDecoder * adec, GstBuffer * buf)
|
||||||
res = gst_opus_dec_parse_comments (dec, buf);
|
res = gst_opus_dec_parse_comments (dec, buf);
|
||||||
gst_audio_decoder_finish_frame (adec, NULL, 1);
|
gst_audio_decoder_finish_frame (adec, NULL, 1);
|
||||||
} else {
|
} else {
|
||||||
res = opus_dec_chain_parse_data (dec, buf, GST_BUFFER_TIMESTAMP (buf),
|
res = opus_dec_chain_parse_data (dec, buf);
|
||||||
GST_BUFFER_DURATION (buf));
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
{
|
{
|
||||||
res = opus_dec_chain_parse_data (dec, buf, GST_BUFFER_TIMESTAMP (buf),
|
res = opus_dec_chain_parse_data (dec, buf);
|
||||||
GST_BUFFER_DURATION (buf));
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -47,7 +47,6 @@ struct _GstOpusDec {
|
||||||
OpusDecoder *state;
|
OpusDecoder *state;
|
||||||
|
|
||||||
guint64 packetno;
|
guint64 packetno;
|
||||||
GstClockTime next_ts;
|
|
||||||
|
|
||||||
GstBuffer *streamheader;
|
GstBuffer *streamheader;
|
||||||
GstBuffer *vorbiscomment;
|
GstBuffer *vorbiscomment;
|
||||||
|
|
|
@ -94,9 +94,7 @@ static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE ("sink",
|
||||||
static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src",
|
static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src",
|
||||||
GST_PAD_SRC,
|
GST_PAD_SRC,
|
||||||
GST_PAD_ALWAYS,
|
GST_PAD_ALWAYS,
|
||||||
GST_STATIC_CAPS ("audio/x-opus, "
|
GST_STATIC_CAPS ("audio/x-opus")
|
||||||
"rate = (int) { 8000, 12000, 16000, 24000, 48000 }, "
|
|
||||||
"channels = (int) [ 1, 2 ], " "frame-size = (int) [ 2, 60 ]")
|
|
||||||
);
|
);
|
||||||
|
|
||||||
#define DEFAULT_AUDIO TRUE
|
#define DEFAULT_AUDIO TRUE
|
||||||
|
@ -144,6 +142,9 @@ static GstFlowReturn gst_opus_enc_handle_frame (GstAudioEncoder * benc,
|
||||||
GstBuffer * buf);
|
GstBuffer * buf);
|
||||||
static GstFlowReturn gst_opus_enc_pre_push (GstAudioEncoder * benc,
|
static GstFlowReturn gst_opus_enc_pre_push (GstAudioEncoder * benc,
|
||||||
GstBuffer ** buffer);
|
GstBuffer ** buffer);
|
||||||
|
static gint64 gst_opus_enc_get_latency (GstOpusEnc * enc);
|
||||||
|
|
||||||
|
static GstFlowReturn gst_opus_enc_encode (GstOpusEnc * enc, GstBuffer * buffer);
|
||||||
|
|
||||||
static GstFlowReturn gst_opus_enc_encode (GstOpusEnc * enc, GstBuffer * buf);
|
static GstFlowReturn gst_opus_enc_encode (GstOpusEnc * enc, GstBuffer * buf);
|
||||||
static gint64 gst_opus_enc_get_latency (GstOpusEnc * enc);
|
static gint64 gst_opus_enc_get_latency (GstOpusEnc * enc);
|
||||||
|
@ -157,13 +158,16 @@ static void
|
||||||
gst_opus_enc_class_init (GstOpusEncClass * klass)
|
gst_opus_enc_class_init (GstOpusEncClass * klass)
|
||||||
{
|
{
|
||||||
GObjectClass *gobject_class;
|
GObjectClass *gobject_class;
|
||||||
GstElementClass *element_class;
|
GstElementClass *gstelement_class;
|
||||||
GstAudioEncoderClass *base_class;
|
GstAudioEncoderClass *base_class;
|
||||||
|
|
||||||
gobject_class = (GObjectClass *) klass;
|
gobject_class = (GObjectClass *) klass;
|
||||||
element_class = (GstElementClass *) klass;
|
gstelement_class = (GstElementClass *) klass;
|
||||||
base_class = (GstAudioEncoderClass *) klass;
|
base_class = (GstAudioEncoderClass *) klass;
|
||||||
|
|
||||||
|
gobject_class->set_property = gst_opus_enc_set_property;
|
||||||
|
gobject_class->get_property = gst_opus_enc_get_property;
|
||||||
|
|
||||||
gst_element_class_add_pad_template (element_class,
|
gst_element_class_add_pad_template (element_class,
|
||||||
gst_static_pad_template_get (&src_factory));
|
gst_static_pad_template_get (&src_factory));
|
||||||
gst_element_class_add_pad_template (element_class,
|
gst_element_class_add_pad_template (element_class,
|
||||||
|
@ -180,9 +184,6 @@ gst_opus_enc_class_init (GstOpusEncClass * klass)
|
||||||
base_class->pre_push = GST_DEBUG_FUNCPTR (gst_opus_enc_pre_push);
|
base_class->pre_push = GST_DEBUG_FUNCPTR (gst_opus_enc_pre_push);
|
||||||
base_class->event = GST_DEBUG_FUNCPTR (gst_opus_enc_sink_event);
|
base_class->event = GST_DEBUG_FUNCPTR (gst_opus_enc_sink_event);
|
||||||
|
|
||||||
gobject_class->set_property = gst_opus_enc_set_property;
|
|
||||||
gobject_class->get_property = gst_opus_enc_get_property;
|
|
||||||
|
|
||||||
g_object_class_install_property (gobject_class, PROP_AUDIO,
|
g_object_class_install_property (gobject_class, PROP_AUDIO,
|
||||||
g_param_spec_boolean ("audio", "Audio or voice",
|
g_param_spec_boolean ("audio", "Audio or voice",
|
||||||
"Audio or voice", DEFAULT_AUDIO,
|
"Audio or voice", DEFAULT_AUDIO,
|
||||||
|
@ -205,7 +206,7 @@ gst_opus_enc_class_init (GstOpusEncClass * klass)
|
||||||
"Constant bit rate", DEFAULT_CBR,
|
"Constant bit rate", DEFAULT_CBR,
|
||||||
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
||||||
g_object_class_install_property (gobject_class, PROP_CONSTRAINED_VBR,
|
g_object_class_install_property (gobject_class, PROP_CONSTRAINED_VBR,
|
||||||
g_param_spec_boolean ("constrained-cbr", "Constrained VBR",
|
g_param_spec_boolean ("constrained-vbr", "Constrained VBR",
|
||||||
"Constrained VBR", DEFAULT_CONSTRAINED_VBR,
|
"Constrained VBR", DEFAULT_CONSTRAINED_VBR,
|
||||||
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
||||||
g_object_class_install_property (gobject_class, PROP_COMPLEXITY,
|
g_object_class_install_property (gobject_class, PROP_COMPLEXITY,
|
||||||
|
@ -237,12 +238,11 @@ gst_opus_enc_finalize (GObject * object)
|
||||||
|
|
||||||
enc = GST_OPUS_ENC (object);
|
enc = GST_OPUS_ENC (object);
|
||||||
|
|
||||||
GST_DEBUG_OBJECT (enc, "finalize");
|
|
||||||
G_OBJECT_CLASS (parent_class)->finalize (object);
|
G_OBJECT_CLASS (parent_class)->finalize (object);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
gst_opus_enc_init (GstOpusEnc * enc)
|
gst_opus_enc_init (GstOpusEnc * enc, GstOpusEncClass * klass)
|
||||||
{
|
{
|
||||||
GstAudioEncoder *benc = GST_AUDIO_ENCODER (enc);
|
GstAudioEncoder *benc = GST_AUDIO_ENCODER (enc);
|
||||||
|
|
||||||
|
@ -273,7 +273,7 @@ gst_opus_enc_start (GstAudioEncoder * benc)
|
||||||
GstOpusEnc *enc = GST_OPUS_ENC (benc);
|
GstOpusEnc *enc = GST_OPUS_ENC (benc);
|
||||||
|
|
||||||
GST_DEBUG_OBJECT (enc, "start");
|
GST_DEBUG_OBJECT (enc, "start");
|
||||||
enc->tags = gst_tag_list_new_empty ();
|
enc->tags = gst_tag_list_new ();
|
||||||
enc->header_sent = FALSE;
|
enc->header_sent = FALSE;
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
@ -297,6 +297,15 @@ gst_opus_enc_stop (GstAudioEncoder * benc)
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static gint64
|
||||||
|
gst_opus_enc_get_latency (GstOpusEnc * enc)
|
||||||
|
{
|
||||||
|
gint64 latency = gst_util_uint64_scale (enc->frame_samples, GST_SECOND,
|
||||||
|
enc->sample_rate);
|
||||||
|
GST_DEBUG_OBJECT (enc, "Latency: %" GST_TIME_FORMAT, GST_TIME_ARGS (latency));
|
||||||
|
return latency;
|
||||||
|
}
|
||||||
|
|
||||||
static gint
|
static gint
|
||||||
gst_opus_enc_get_frame_samples (GstOpusEnc * enc)
|
gst_opus_enc_get_frame_samples (GstOpusEnc * enc)
|
||||||
{
|
{
|
||||||
|
@ -345,7 +354,6 @@ gst_opus_enc_set_format (GstAudioEncoder * benc, GstAudioInfo * info)
|
||||||
opus_encoder_destroy (enc->state);
|
opus_encoder_destroy (enc->state);
|
||||||
enc->state = NULL;
|
enc->state = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!gst_opus_enc_setup (enc))
|
if (!gst_opus_enc_setup (enc))
|
||||||
return FALSE;
|
return FALSE;
|
||||||
|
|
||||||
|
@ -354,7 +362,6 @@ gst_opus_enc_set_format (GstAudioEncoder * benc, GstAudioInfo * info)
|
||||||
/* feedback to base class */
|
/* feedback to base class */
|
||||||
gst_audio_encoder_set_latency (benc,
|
gst_audio_encoder_set_latency (benc,
|
||||||
gst_opus_enc_get_latency (enc), gst_opus_enc_get_latency (enc));
|
gst_opus_enc_get_latency (enc), gst_opus_enc_get_latency (enc));
|
||||||
|
|
||||||
gst_audio_encoder_set_frame_samples_min (benc,
|
gst_audio_encoder_set_frame_samples_min (benc,
|
||||||
enc->frame_samples * enc->n_channels * 2);
|
enc->frame_samples * enc->n_channels * 2);
|
||||||
gst_audio_encoder_set_frame_samples_max (benc,
|
gst_audio_encoder_set_frame_samples_max (benc,
|
||||||
|
@ -364,15 +371,6 @@ gst_opus_enc_set_format (GstAudioEncoder * benc, GstAudioInfo * info)
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
static gint64
|
|
||||||
gst_opus_enc_get_latency (GstOpusEnc * enc)
|
|
||||||
{
|
|
||||||
gint64 latency = gst_util_uint64_scale (enc->frame_samples, GST_SECOND,
|
|
||||||
enc->sample_rate);
|
|
||||||
GST_DEBUG_OBJECT (enc, "Latency: %" GST_TIME_FORMAT, GST_TIME_ARGS (latency));
|
|
||||||
return latency;
|
|
||||||
}
|
|
||||||
|
|
||||||
static GstBuffer *
|
static GstBuffer *
|
||||||
gst_opus_enc_create_id_buffer (GstOpusEnc * enc)
|
gst_opus_enc_create_id_buffer (GstOpusEnc * enc)
|
||||||
{
|
{
|
||||||
|
@ -495,8 +493,8 @@ gst_opus_enc_sink_event (GstAudioEncoder * benc, GstEvent * event)
|
||||||
static GstFlowReturn
|
static GstFlowReturn
|
||||||
gst_opus_enc_pre_push (GstAudioEncoder * benc, GstBuffer ** buffer)
|
gst_opus_enc_pre_push (GstAudioEncoder * benc, GstBuffer ** buffer)
|
||||||
{
|
{
|
||||||
GstOpusEnc *enc;
|
|
||||||
GstFlowReturn ret = GST_FLOW_OK;
|
GstFlowReturn ret = GST_FLOW_OK;
|
||||||
|
GstOpusEnc *enc;
|
||||||
|
|
||||||
enc = GST_OPUS_ENC (benc);
|
enc = GST_OPUS_ENC (benc);
|
||||||
|
|
||||||
|
@ -522,6 +520,39 @@ gst_opus_enc_pre_push (GstAudioEncoder * benc, GstBuffer ** buffer)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static GstFlowReturn
|
||||||
|
gst_opus_enc_encode (GstOpusEnc * enc, GstBuffer * buf)
|
||||||
|
{
|
||||||
|
guint8 *bdata, *data, *mdata = NULL;
|
||||||
|
gsize bsize, size;
|
||||||
|
gsize bytes = enc->frame_samples * enc->n_channels * 2;
|
||||||
|
gsize bytes_per_packet =
|
||||||
|
(enc->bitrate * enc->frame_samples / enc->sample_rate + 4) / 8;
|
||||||
|
gint ret = GST_FLOW_OK;
|
||||||
|
|
||||||
|
if (G_LIKELY (buf)) {
|
||||||
|
bdata = GST_BUFFER_DATA (buf);
|
||||||
|
bsize = GST_BUFFER_SIZE (buf);
|
||||||
|
if (G_UNLIKELY (bsize % bytes)) {
|
||||||
|
GST_DEBUG_OBJECT (enc, "draining; adding silence samples");
|
||||||
|
|
||||||
|
size = ((bsize / bytes) + 1) * bytes;
|
||||||
|
mdata = g_malloc0 (size);
|
||||||
|
memcpy (mdata, bdata, bsize);
|
||||||
|
bdata = NULL;
|
||||||
|
data = mdata;
|
||||||
|
} else {
|
||||||
|
data = bdata;
|
||||||
|
size = bsize;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
GST_DEBUG_OBJECT (enc, "nothing to drain");
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
static GstFlowReturn
|
static GstFlowReturn
|
||||||
gst_opus_enc_encode (GstOpusEnc * enc, GstBuffer * buf)
|
gst_opus_enc_encode (GstOpusEnc * enc, GstBuffer * buf)
|
||||||
{
|
{
|
||||||
|
@ -669,7 +700,6 @@ gst_opus_enc_handle_frame (GstAudioEncoder * benc, GstBuffer * buf)
|
||||||
GstFlowReturn ret = GST_FLOW_OK;
|
GstFlowReturn ret = GST_FLOW_OK;
|
||||||
|
|
||||||
enc = GST_OPUS_ENC (benc);
|
enc = GST_OPUS_ENC (benc);
|
||||||
|
|
||||||
GST_DEBUG_OBJECT (enc, "handle_frame");
|
GST_DEBUG_OBJECT (enc, "handle_frame");
|
||||||
|
|
||||||
if (!enc->header_sent) {
|
if (!enc->header_sent) {
|
||||||
|
@ -684,17 +714,13 @@ gst_opus_enc_handle_frame (GstAudioEncoder * benc, GstBuffer * buf)
|
||||||
buf2 = gst_opus_enc_create_metadata_buffer (enc);
|
buf2 = gst_opus_enc_create_metadata_buffer (enc);
|
||||||
|
|
||||||
/* mark and put on caps */
|
/* mark and put on caps */
|
||||||
caps =
|
caps = gst_caps_from_string ("audio/x-opus");
|
||||||
gst_caps_new_simple ("audio/x-opus", "rate", G_TYPE_INT,
|
|
||||||
enc->sample_rate, "channels", G_TYPE_INT, enc->n_channels, "frame-size",
|
|
||||||
G_TYPE_INT, enc->frame_size, NULL);
|
|
||||||
caps = _gst_caps_set_buffer_array (caps, "streamheader", buf1, buf2, NULL);
|
caps = _gst_caps_set_buffer_array (caps, "streamheader", buf1, buf2, NULL);
|
||||||
|
|
||||||
/* negotiate with these caps */
|
/* negotiate with these caps */
|
||||||
GST_DEBUG_OBJECT (enc, "here are the caps: %" GST_PTR_FORMAT, caps);
|
GST_DEBUG_OBJECT (enc, "here are the caps: %" GST_PTR_FORMAT, caps);
|
||||||
|
|
||||||
gst_pad_set_caps (GST_AUDIO_ENCODER_SRC_PAD (enc), caps);
|
gst_pad_set_caps (GST_AUDIO_ENCODER_SRC_PAD (enc), caps);
|
||||||
gst_caps_unref (caps);
|
|
||||||
|
|
||||||
/* push out buffers */
|
/* push out buffers */
|
||||||
/* store buffers for later pre_push sending */
|
/* store buffers for later pre_push sending */
|
||||||
|
@ -703,12 +729,11 @@ gst_opus_enc_handle_frame (GstAudioEncoder * benc, GstBuffer * buf)
|
||||||
GST_DEBUG_OBJECT (enc, "storing header buffers");
|
GST_DEBUG_OBJECT (enc, "storing header buffers");
|
||||||
enc->headers = g_slist_prepend (enc->headers, buf2);
|
enc->headers = g_slist_prepend (enc->headers, buf2);
|
||||||
enc->headers = g_slist_prepend (enc->headers, buf1);
|
enc->headers = g_slist_prepend (enc->headers, buf1);
|
||||||
|
|
||||||
enc->header_sent = TRUE;
|
enc->header_sent = TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
GST_DEBUG_OBJECT (enc, "received buffer %p of %u bytes", buf,
|
GST_DEBUG_OBJECT (enc, "received buffer %p of %u bytes", buf,
|
||||||
buf ? gst_buffer_get_size (buf) : 0);
|
buf ? GST_BUFFER_SIZE (buf) : 0);
|
||||||
|
|
||||||
ret = gst_opus_enc_encode (enc, buf);
|
ret = gst_opus_enc_encode (enc, buf);
|
||||||
|
|
||||||
|
|
|
@ -48,11 +48,7 @@ typedef struct _GstOpusEnc GstOpusEnc;
|
||||||
typedef struct _GstOpusEncClass GstOpusEncClass;
|
typedef struct _GstOpusEncClass GstOpusEncClass;
|
||||||
|
|
||||||
struct _GstOpusEnc {
|
struct _GstOpusEnc {
|
||||||
GstAudioEncoder element;
|
GstAudioEncoder element;
|
||||||
|
|
||||||
/* pads */
|
|
||||||
GstPad *sinkpad;
|
|
||||||
GstPad *srcpad;
|
|
||||||
|
|
||||||
OpusEncoder *state;
|
OpusEncoder *state;
|
||||||
|
|
||||||
|
@ -74,7 +70,8 @@ struct _GstOpusEnc {
|
||||||
|
|
||||||
gboolean setup;
|
gboolean setup;
|
||||||
gboolean header_sent;
|
gboolean header_sent;
|
||||||
GSList *headers;
|
|
||||||
|
GSList *headers;
|
||||||
|
|
||||||
GstTagList *tags;
|
GstTagList *tags;
|
||||||
};
|
};
|
||||||
|
|
|
@ -18,6 +18,7 @@ libgstvp8_la_LIBADD = \
|
||||||
$(GST_PLUGINS_BASE_LIBS) -lgsttag-@GST_MAJORMINOR@ -lgstvideo-@GST_MAJORMINOR@ \
|
$(GST_PLUGINS_BASE_LIBS) -lgsttag-@GST_MAJORMINOR@ -lgstvideo-@GST_MAJORMINOR@ \
|
||||||
$(GST_BASE_LIBS) $(GST_LIBS) $(VPX_LIBS)
|
$(GST_BASE_LIBS) $(GST_LIBS) $(VPX_LIBS)
|
||||||
libgstvp8_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS)
|
libgstvp8_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS)
|
||||||
|
libgstvp8_la_LIBTOOLFLAGS = --tag=disable-static
|
||||||
|
|
||||||
noinst_HEADERS = \
|
noinst_HEADERS = \
|
||||||
gstvp8dec.h \
|
gstvp8dec.h \
|
||||||
|
|
|
@ -681,8 +681,8 @@ gst_mpeg_video_parse_picture_header (GstMpegVideoPictureHdr * hdr,
|
||||||
if (hdr->pic_type == 0 || hdr->pic_type > 4)
|
if (hdr->pic_type == 0 || hdr->pic_type > 4)
|
||||||
goto failed; /* Corrupted picture packet */
|
goto failed; /* Corrupted picture packet */
|
||||||
|
|
||||||
/* skype VBV delay */
|
/* skip VBV delay */
|
||||||
if (!gst_bit_reader_skip (&br, 8))
|
if (!gst_bit_reader_skip (&br, 16))
|
||||||
goto failed;
|
goto failed;
|
||||||
|
|
||||||
if (hdr->pic_type == GST_MPEG_VIDEO_PICTURE_TYPE_P
|
if (hdr->pic_type == GST_MPEG_VIDEO_PICTURE_TYPE_P
|
||||||
|
|
|
@ -38,7 +38,7 @@ G_BEGIN_DECLS
|
||||||
/**
|
/**
|
||||||
* GstMpegVideoPacketTypeCode:
|
* GstMpegVideoPacketTypeCode:
|
||||||
* @GST_MPEG_VIDEO_PACKET_PICTURE: Picture packet starting code
|
* @GST_MPEG_VIDEO_PACKET_PICTURE: Picture packet starting code
|
||||||
* @GST_MPEG_VIDEO_PACKET_SLICE_MIN: Picture packet starting code
|
* @GST_MPEG_VIDEO_PACKET_SLICE_MIN: Slice min packet starting code
|
||||||
* @GST_MPEG_VIDEO_PACKET_SLICE_MAX: Slice max packet starting code
|
* @GST_MPEG_VIDEO_PACKET_SLICE_MAX: Slice max packet starting code
|
||||||
* @GST_MPEG_VIDEO_PACKET_USER_DATA: User data packet starting code
|
* @GST_MPEG_VIDEO_PACKET_USER_DATA: User data packet starting code
|
||||||
* @GST_MPEG_VIDEO_PACKET_SEQUENCE : Sequence packet starting code
|
* @GST_MPEG_VIDEO_PACKET_SEQUENCE : Sequence packet starting code
|
||||||
|
@ -186,7 +186,7 @@ typedef struct _GstMpegVideoTypeOffsetSize GstMpegVideoTypeOffsetSize;
|
||||||
* @width: Width of each frame
|
* @width: Width of each frame
|
||||||
* @height: Height of each frame
|
* @height: Height of each frame
|
||||||
* @par_w: Calculated Pixel Aspect Ratio width
|
* @par_w: Calculated Pixel Aspect Ratio width
|
||||||
* @par_h: Pixel Aspect Ratio height
|
* @par_h: Calculated Pixel Aspect Ratio height
|
||||||
* @fps_n: Calculated Framrate nominator
|
* @fps_n: Calculated Framrate nominator
|
||||||
* @fps_d: Calculated Framerate denominator
|
* @fps_d: Calculated Framerate denominator
|
||||||
* @bitrate_value: Value of the bitrate as is in the stream (400bps unit)
|
* @bitrate_value: Value of the bitrate as is in the stream (400bps unit)
|
||||||
|
|
|
@ -84,6 +84,8 @@
|
||||||
* unreffed or replaced by a new user set element. Initially only elements
|
* unreffed or replaced by a new user set element. Initially only elements
|
||||||
* needed for view finder mode are created to speed up startup. Image bin and
|
* needed for view finder mode are created to speed up startup. Image bin and
|
||||||
* video bin elements are created when setting the mode or starting capture.
|
* video bin elements are created when setting the mode or starting capture.
|
||||||
|
* GstCameraBin must be in the PLAYING state before #GstCameraBin::capture-start
|
||||||
|
* is called.
|
||||||
* </para>
|
* </para>
|
||||||
* </refsect2>
|
* </refsect2>
|
||||||
* <refsect2>
|
* <refsect2>
|
||||||
|
|
|
@ -183,8 +183,10 @@
|
||||||
|
|
||||||
#define GST_CAMERA_BIN2_PROCESSING_DEC(c) \
|
#define GST_CAMERA_BIN2_PROCESSING_DEC(c) \
|
||||||
{ \
|
{ \
|
||||||
if (g_atomic_int_dec_and_test (&c->processing_counter)) \
|
if (g_atomic_int_dec_and_test (&c->processing_counter)) { \
|
||||||
g_object_notify (G_OBJECT (c), "idle"); \
|
g_object_notify (G_OBJECT (c), "idle"); \
|
||||||
|
GST_DEBUG_OBJECT ((c), "Camerabin now idle"); \
|
||||||
|
} \
|
||||||
GST_DEBUG_OBJECT ((c), "Processing counter decremented"); \
|
GST_DEBUG_OBJECT ((c), "Processing counter decremented"); \
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -368,11 +370,24 @@ gst_camera_bin_start_capture (GstCameraBin2 * camerabin)
|
||||||
GST_DEBUG_OBJECT (camerabin, "Received start-capture");
|
GST_DEBUG_OBJECT (camerabin, "Received start-capture");
|
||||||
|
|
||||||
/* check that we have a valid location */
|
/* check that we have a valid location */
|
||||||
if (camerabin->mode == MODE_VIDEO && camerabin->location == NULL) {
|
if (camerabin->mode == MODE_VIDEO) {
|
||||||
GST_ELEMENT_ERROR (camerabin, RESOURCE, OPEN_WRITE,
|
if (camerabin->location == NULL) {
|
||||||
(_("File location is set to NULL, please set it to a valid filename")),
|
GST_ELEMENT_ERROR (camerabin, RESOURCE, OPEN_WRITE,
|
||||||
(NULL));
|
(_("File location is set to NULL, please set it to a valid filename")), (NULL));
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
g_mutex_lock (camerabin->video_capture_mutex);
|
||||||
|
while (camerabin->video_state == GST_CAMERA_BIN_VIDEO_FINISHING) {
|
||||||
|
g_cond_wait (camerabin->video_state_cond, camerabin->video_capture_mutex);
|
||||||
|
}
|
||||||
|
if (camerabin->video_state != GST_CAMERA_BIN_VIDEO_IDLE) {
|
||||||
|
GST_WARNING_OBJECT (camerabin, "Another video recording is ongoing"
|
||||||
|
" (state %d), cannot start a new one", camerabin->video_state);
|
||||||
|
g_mutex_unlock (camerabin->video_capture_mutex);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
camerabin->video_state = GST_CAMERA_BIN_VIDEO_STARTING;
|
||||||
}
|
}
|
||||||
|
|
||||||
GST_CAMERA_BIN2_PROCESSING_INC (camerabin);
|
GST_CAMERA_BIN2_PROCESSING_INC (camerabin);
|
||||||
|
@ -384,12 +399,6 @@ gst_camera_bin_start_capture (GstCameraBin2 * camerabin)
|
||||||
if (camerabin->audio_src) {
|
if (camerabin->audio_src) {
|
||||||
GstClock *clock = gst_pipeline_get_clock (GST_PIPELINE_CAST (camerabin));
|
GstClock *clock = gst_pipeline_get_clock (GST_PIPELINE_CAST (camerabin));
|
||||||
|
|
||||||
/* need to reset eos status (pads could be flushing) */
|
|
||||||
gst_element_set_state (camerabin->audio_capsfilter, GST_STATE_READY);
|
|
||||||
gst_element_set_state (camerabin->audio_volume, GST_STATE_READY);
|
|
||||||
|
|
||||||
gst_element_sync_state_with_parent (camerabin->audio_capsfilter);
|
|
||||||
gst_element_sync_state_with_parent (camerabin->audio_volume);
|
|
||||||
gst_element_set_state (camerabin->audio_src, GST_STATE_PAUSED);
|
gst_element_set_state (camerabin->audio_src, GST_STATE_PAUSED);
|
||||||
|
|
||||||
gst_element_set_base_time (camerabin->audio_src,
|
gst_element_set_base_time (camerabin->audio_src,
|
||||||
|
@ -420,8 +429,13 @@ gst_camera_bin_start_capture (GstCameraBin2 * camerabin)
|
||||||
}
|
}
|
||||||
|
|
||||||
g_signal_emit_by_name (camerabin->src, "start-capture", NULL);
|
g_signal_emit_by_name (camerabin->src, "start-capture", NULL);
|
||||||
if (camerabin->mode == MODE_VIDEO && camerabin->audio_src)
|
if (camerabin->mode == MODE_VIDEO) {
|
||||||
gst_element_set_state (camerabin->audio_src, GST_STATE_PLAYING);
|
if (camerabin->audio_src)
|
||||||
|
gst_element_set_state (camerabin->audio_src, GST_STATE_PLAYING);
|
||||||
|
|
||||||
|
camerabin->video_state = GST_CAMERA_BIN_VIDEO_RECORDING;
|
||||||
|
g_mutex_unlock (camerabin->video_capture_mutex);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* We have to push tags after start capture because the video elements
|
* We have to push tags after start capture because the video elements
|
||||||
|
@ -458,12 +472,19 @@ static void
|
||||||
gst_camera_bin_stop_capture (GstCameraBin2 * camerabin)
|
gst_camera_bin_stop_capture (GstCameraBin2 * camerabin)
|
||||||
{
|
{
|
||||||
GST_DEBUG_OBJECT (camerabin, "Received stop-capture");
|
GST_DEBUG_OBJECT (camerabin, "Received stop-capture");
|
||||||
if (camerabin->src)
|
if (camerabin->mode == MODE_VIDEO) {
|
||||||
g_signal_emit_by_name (camerabin->src, "stop-capture", NULL);
|
g_mutex_lock (camerabin->video_capture_mutex);
|
||||||
|
if (camerabin->video_state == GST_CAMERA_BIN_VIDEO_RECORDING) {
|
||||||
|
if (camerabin->src)
|
||||||
|
g_signal_emit_by_name (camerabin->src, "stop-capture", NULL);
|
||||||
|
|
||||||
if (camerabin->mode == MODE_VIDEO && camerabin->audio_src) {
|
camerabin->video_state = GST_CAMERA_BIN_VIDEO_FINISHING;
|
||||||
camerabin->audio_drop_eos = FALSE;
|
if (camerabin->audio_src) {
|
||||||
gst_element_send_event (camerabin->audio_src, gst_event_new_eos ());
|
camerabin->audio_drop_eos = FALSE;
|
||||||
|
gst_element_send_event (camerabin->audio_src, gst_event_new_eos ());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
g_mutex_unlock (camerabin->video_capture_mutex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -494,11 +515,8 @@ gst_camera_bin_src_notify_readyforcapture (GObject * obj, GParamSpec * pspec,
|
||||||
gchar *location = NULL;
|
gchar *location = NULL;
|
||||||
|
|
||||||
if (camera->mode == MODE_VIDEO) {
|
if (camera->mode == MODE_VIDEO) {
|
||||||
/* a video recording is about to start, we reset the videobin to clear eos/flushing state
|
/* a video recording is about to start, change the filesink location */
|
||||||
* also need to clean the queue ! capsfilter before it */
|
|
||||||
gst_element_set_state (camera->videosink, GST_STATE_NULL);
|
gst_element_set_state (camera->videosink, GST_STATE_NULL);
|
||||||
gst_element_set_state (camera->video_encodebin, GST_STATE_NULL);
|
|
||||||
gst_element_set_state (camera->videobin_capsfilter, GST_STATE_NULL);
|
|
||||||
location = g_strdup_printf (camera->location, camera->capture_index);
|
location = g_strdup_printf (camera->location, camera->capture_index);
|
||||||
GST_DEBUG_OBJECT (camera, "Switching videobin location to %s", location);
|
GST_DEBUG_OBJECT (camera, "Switching videobin location to %s", location);
|
||||||
g_object_set (camera->videosink, "location", location, NULL);
|
g_object_set (camera->videosink, "location", location, NULL);
|
||||||
|
@ -509,21 +527,9 @@ gst_camera_bin_src_notify_readyforcapture (GObject * obj, GParamSpec * pspec,
|
||||||
* and could cause problems in a camerabin2 state change */
|
* and could cause problems in a camerabin2 state change */
|
||||||
gst_element_set_state (camera->videosink, GST_STATE_NULL);
|
gst_element_set_state (camera->videosink, GST_STATE_NULL);
|
||||||
}
|
}
|
||||||
gst_element_set_state (camera->video_encodebin, GST_STATE_PLAYING);
|
|
||||||
gst_element_set_state (camera->videobin_capsfilter, GST_STATE_PLAYING);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
camera->capture_index++;
|
camera->capture_index++;
|
||||||
} else {
|
|
||||||
if (camera->mode == MODE_VIDEO && camera->audio_src) {
|
|
||||||
/* FIXME We need to set audiosrc to null to make it resync the ringbuffer
|
|
||||||
* while bug https://bugzilla.gnome.org/show_bug.cgi?id=648359 isn't
|
|
||||||
* fixed.
|
|
||||||
*
|
|
||||||
* Also, we set to NULL here to stop capturing audio through to the next
|
|
||||||
* video mode start capture and to clear EOS. */
|
|
||||||
gst_element_set_state (camera->audio_src, GST_STATE_NULL);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -535,6 +541,8 @@ gst_camera_bin_dispose (GObject * object)
|
||||||
g_free (camerabin->location);
|
g_free (camerabin->location);
|
||||||
g_mutex_free (camerabin->preview_list_mutex);
|
g_mutex_free (camerabin->preview_list_mutex);
|
||||||
g_mutex_free (camerabin->image_capture_mutex);
|
g_mutex_free (camerabin->image_capture_mutex);
|
||||||
|
g_mutex_free (camerabin->video_capture_mutex);
|
||||||
|
g_cond_free (camerabin->video_state_cond);
|
||||||
|
|
||||||
if (camerabin->src_capture_notify_id)
|
if (camerabin->src_capture_notify_id)
|
||||||
g_signal_handler_disconnect (camerabin->src,
|
g_signal_handler_disconnect (camerabin->src,
|
||||||
|
@ -892,6 +900,8 @@ gst_camera_bin_init (GstCameraBin2 * camera)
|
||||||
camera->flags = DEFAULT_FLAGS;
|
camera->flags = DEFAULT_FLAGS;
|
||||||
camera->preview_list_mutex = g_mutex_new ();
|
camera->preview_list_mutex = g_mutex_new ();
|
||||||
camera->image_capture_mutex = g_mutex_new ();
|
camera->image_capture_mutex = g_mutex_new ();
|
||||||
|
camera->video_capture_mutex = g_mutex_new ();
|
||||||
|
camera->video_state_cond = g_cond_new ();
|
||||||
|
|
||||||
/* capsfilters are created here as we proxy their caps properties and
|
/* capsfilters are created here as we proxy their caps properties and
|
||||||
* this way we avoid having to store the caps while on NULL state to
|
* this way we avoid having to store the caps while on NULL state to
|
||||||
|
@ -963,6 +973,46 @@ gst_camera_bin_skip_next_preview (GstCameraBin2 * camerabin)
|
||||||
g_mutex_unlock (camerabin->preview_list_mutex);
|
g_mutex_unlock (camerabin->preview_list_mutex);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static gpointer
|
||||||
|
gst_camera_bin_video_reset_elements (gpointer u_data)
|
||||||
|
{
|
||||||
|
GstCameraBin2 *camerabin = GST_CAMERA_BIN2_CAST (u_data);
|
||||||
|
|
||||||
|
GST_DEBUG_OBJECT (camerabin, "Resetting video elements state");
|
||||||
|
g_mutex_lock (camerabin->video_capture_mutex);
|
||||||
|
|
||||||
|
/* reset element states to clear eos/flushing pads */
|
||||||
|
gst_element_set_state (camerabin->video_encodebin, GST_STATE_READY);
|
||||||
|
gst_element_set_state (camerabin->videobin_capsfilter, GST_STATE_READY);
|
||||||
|
gst_element_sync_state_with_parent (camerabin->videobin_capsfilter);
|
||||||
|
gst_element_sync_state_with_parent (camerabin->video_encodebin);
|
||||||
|
|
||||||
|
if (camerabin->audio_src) {
|
||||||
|
gst_element_set_state (camerabin->audio_capsfilter, GST_STATE_READY);
|
||||||
|
gst_element_set_state (camerabin->audio_volume, GST_STATE_READY);
|
||||||
|
|
||||||
|
/* FIXME We need to set audiosrc to null to make it resync the ringbuffer
|
||||||
|
* while bug https://bugzilla.gnome.org/show_bug.cgi?id=648359 isn't
|
||||||
|
* fixed.
|
||||||
|
*
|
||||||
|
* Also, we don't reinit the audiosrc to keep audio devices from being open
|
||||||
|
* and running until we really need them */
|
||||||
|
gst_element_set_state (camerabin->audio_src, GST_STATE_NULL);
|
||||||
|
|
||||||
|
gst_element_sync_state_with_parent (camerabin->audio_capsfilter);
|
||||||
|
gst_element_sync_state_with_parent (camerabin->audio_volume);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
GST_DEBUG_OBJECT (camerabin, "Setting video state to idle");
|
||||||
|
camerabin->video_state = GST_CAMERA_BIN_VIDEO_IDLE;
|
||||||
|
g_cond_signal (camerabin->video_state_cond);
|
||||||
|
g_mutex_unlock (camerabin->video_capture_mutex);
|
||||||
|
|
||||||
|
gst_object_unref (camerabin);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
gst_camera_bin_handle_message (GstBin * bin, GstMessage * message)
|
gst_camera_bin_handle_message (GstBin * bin, GstMessage * message)
|
||||||
{
|
{
|
||||||
|
@ -1035,9 +1085,24 @@ gst_camera_bin_handle_message (GstBin * bin, GstMessage * message)
|
||||||
case GST_MESSAGE_EOS:{
|
case GST_MESSAGE_EOS:{
|
||||||
GstElement *src = GST_ELEMENT (GST_MESSAGE_SRC (message));
|
GstElement *src = GST_ELEMENT (GST_MESSAGE_SRC (message));
|
||||||
if (src == GST_CAMERA_BIN2_CAST (bin)->videosink) {
|
if (src == GST_CAMERA_BIN2_CAST (bin)->videosink) {
|
||||||
|
|
||||||
|
g_mutex_lock (camerabin->video_capture_mutex);
|
||||||
GST_DEBUG_OBJECT (bin, "EOS from video branch");
|
GST_DEBUG_OBJECT (bin, "EOS from video branch");
|
||||||
|
g_assert (camerabin->video_state == GST_CAMERA_BIN_VIDEO_FINISHING);
|
||||||
|
|
||||||
gst_video_capture_bin_post_video_done (GST_CAMERA_BIN2_CAST (bin));
|
gst_video_capture_bin_post_video_done (GST_CAMERA_BIN2_CAST (bin));
|
||||||
dec_counter = TRUE;
|
dec_counter = TRUE;
|
||||||
|
|
||||||
|
if (!g_thread_create (gst_camera_bin_video_reset_elements,
|
||||||
|
gst_object_ref (camerabin), FALSE, NULL)) {
|
||||||
|
GST_WARNING_OBJECT (camerabin, "Failed to create thread to "
|
||||||
|
"reset video elements' state, video recordings may not work "
|
||||||
|
"anymore");
|
||||||
|
gst_object_unref (camerabin);
|
||||||
|
camerabin->video_state = GST_CAMERA_BIN_VIDEO_IDLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
g_mutex_unlock (camerabin->video_capture_mutex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -1810,6 +1875,7 @@ gst_camera_bin_change_state (GstElement * element, GstStateChange trans)
|
||||||
|
|
||||||
gst_tag_setter_reset_tags (GST_TAG_SETTER (camera));
|
gst_tag_setter_reset_tags (GST_TAG_SETTER (camera));
|
||||||
GST_CAMERA_BIN2_RESET_PROCESSING_COUNTER (camera);
|
GST_CAMERA_BIN2_RESET_PROCESSING_COUNTER (camera);
|
||||||
|
camera->video_state = GST_CAMERA_BIN_VIDEO_IDLE;
|
||||||
|
|
||||||
g_mutex_lock (camera->image_capture_mutex);
|
g_mutex_lock (camera->image_capture_mutex);
|
||||||
g_slist_foreach (camera->image_location_list, (GFunc) g_free, NULL);
|
g_slist_foreach (camera->image_location_list, (GFunc) g_free, NULL);
|
||||||
|
|
|
@ -45,6 +45,14 @@ typedef enum
|
||||||
} GstCamFlags;
|
} GstCamFlags;
|
||||||
|
|
||||||
|
|
||||||
|
typedef enum _GstCameraBinVideoState
|
||||||
|
{
|
||||||
|
GST_CAMERA_BIN_VIDEO_IDLE=0,
|
||||||
|
GST_CAMERA_BIN_VIDEO_STARTING=1,
|
||||||
|
GST_CAMERA_BIN_VIDEO_RECORDING=2,
|
||||||
|
GST_CAMERA_BIN_VIDEO_FINISHING=3
|
||||||
|
} GstCameraBinVideoState;
|
||||||
|
|
||||||
typedef struct _GstCameraBin2 GstCameraBin2;
|
typedef struct _GstCameraBin2 GstCameraBin2;
|
||||||
typedef struct _GstCameraBin2Class GstCameraBin2Class;
|
typedef struct _GstCameraBin2Class GstCameraBin2Class;
|
||||||
|
|
||||||
|
@ -119,6 +127,10 @@ struct _GstCameraBin2
|
||||||
|
|
||||||
gboolean audio_drop_eos;
|
gboolean audio_drop_eos;
|
||||||
|
|
||||||
|
GMutex *video_capture_mutex;
|
||||||
|
GCond *video_state_cond;
|
||||||
|
GstCameraBinVideoState video_state;
|
||||||
|
|
||||||
/* properties */
|
/* properties */
|
||||||
gint mode;
|
gint mode;
|
||||||
gchar *location;
|
gchar *location;
|
||||||
|
|
|
@ -45,8 +45,7 @@ static GstStaticPadTemplate sink_template =
|
||||||
GST_STATIC_PAD_TEMPLATE ("sink", GST_PAD_SINK,
|
GST_STATIC_PAD_TEMPLATE ("sink", GST_PAD_SINK,
|
||||||
GST_PAD_ALWAYS,
|
GST_PAD_ALWAYS,
|
||||||
GST_STATIC_CAPS ("video/mpeg, "
|
GST_STATIC_CAPS ("video/mpeg, "
|
||||||
"mpegversion = (int) 4, "
|
"mpegversion = (int) 4, " "systemstream = (boolean) false")
|
||||||
"parsed = (boolean) false, " "systemstream = (boolean) false")
|
|
||||||
);
|
);
|
||||||
|
|
||||||
/* Properties */
|
/* Properties */
|
||||||
|
@ -634,7 +633,7 @@ plugin_init (GstPlugin * plugin)
|
||||||
GST_DEBUG_CATEGORY_INIT (mpeg4v_parse_debug, "mpeg4videoparse", 0,
|
GST_DEBUG_CATEGORY_INIT (mpeg4v_parse_debug, "mpeg4videoparse", 0,
|
||||||
"MPEG-4 video parser");
|
"MPEG-4 video parser");
|
||||||
|
|
||||||
if (!gst_element_register (plugin, "mpeg4videoparse", GST_RANK_SECONDARY,
|
if (!gst_element_register (plugin, "mpeg4videoparse", GST_RANK_PRIMARY + 1,
|
||||||
gst_mpeg4vparse_get_type ()))
|
gst_mpeg4vparse_get_type ()))
|
||||||
return FALSE;
|
return FALSE;
|
||||||
|
|
||||||
|
|
|
@ -1,117 +1,151 @@
|
||||||
mpegtsparse rebasing
|
tsdemux/tsparse TODO
|
||||||
--------------------
|
--------------------
|
||||||
|
|
||||||
Rationale :
|
* clock for live streams
|
||||||
-----------
|
In order for playback to happen at the same rate as on the producer,
|
||||||
|
we need to estimate the remote clock based on capture time and PCR
|
||||||
|
values.
|
||||||
|
For this estimation to be as accurate as possible, the capture time
|
||||||
|
needs to happen on the sources.
|
||||||
|
=> Ensure live sources actually timestamp their buffers
|
||||||
|
Once we have accurate timestamps, we can use an algorithm to
|
||||||
|
calculate the PCR/local-clock skew.
|
||||||
|
=> Use the EPTLA algorithm as used in -good/rtp/rtpmanager/
|
||||||
|
gstrtpjitterbuffer
|
||||||
|
|
||||||
mpegtsparse code is more sane to handle and work with.
|
* Seeking
|
||||||
|
=> Split out in a separate file/object. It is polluting tsdemux for
|
||||||
|
code readability/clarity.
|
||||||
|
|
||||||
We need a modular demuxer
|
|
||||||
|
|
||||||
We need to avoid duplicating code regarding mpeg-ts in a gazillion
|
|
||||||
elements and allow easy creatiof new elements.
|
|
||||||
|
|
||||||
|
|
||||||
Battleplan :
|
|
||||||
------------
|
|
||||||
* Figure out code from mpegtsparse which would be also needed for a
|
|
||||||
mpeg-ts demuxer (ex: packet/psi/pcr parsing).
|
|
||||||
* Extract common code into a base mpegtsbase class.
|
|
||||||
* Refactor mpegtsparse to subclass that base class.
|
|
||||||
* Create a minimalistic demuxer that creates pads (based on PSI info)
|
|
||||||
and outputs ES packets (let's say mpeg audio and video to start with)
|
|
||||||
|
|
||||||
Potential subclasses :
|
|
||||||
----------------------
|
|
||||||
* MpegTSParse : Program splitter. Given an incoming multi-program
|
|
||||||
mpeg-ts stream, it can provide request pads for each program. Each
|
|
||||||
of those pads will contain the ts packets specific to that program.
|
|
||||||
|
|
||||||
* TSDemux : Program demuxer. Given an incoming single or multi-program
|
|
||||||
mpeg-ts stream, it will reconstruct the original Program Streams of
|
|
||||||
the selected program and output them on dynamically created pads.
|
|
||||||
|
|
||||||
* HDVSplitter : Given an incoming HDV mpeg-ts stream, it will locate
|
|
||||||
the beginning of new scenes and output a mpeg-ts stream with the
|
|
||||||
PAT/PMT/AUX packets properly ordered and marked with DISCONT, so
|
|
||||||
that the following pipeline will automatically cut up a tape dump
|
|
||||||
into individual scenes:
|
|
||||||
filesrc ! hdvsplit ! multifilesink next-file=discont
|
|
||||||
|
|
||||||
Code/Design common to a program-spliter and a demuxer :
|
|
||||||
-------------------------------------------------------
|
|
||||||
* Parsing TS packets
|
|
||||||
* Establishing PAT/PMT mapping
|
|
||||||
* Handling the notions of Programs/Streams
|
|
||||||
* Seeking ?
|
|
||||||
|
|
||||||
One proposal... would be to have the base class automatically create
|
|
||||||
all the structures (and relationships) for the following objects:
|
|
||||||
|
|
||||||
* Programs (from PAT/PMT, dunno if it could come from something
|
|
||||||
else)
|
|
||||||
* Program id
|
|
||||||
* Streams contained in that program (with links to them)
|
|
||||||
* Which stream contains the PCR
|
|
||||||
* Metadata ?
|
|
||||||
* Streams (ideally... in a table for fast access)
|
|
||||||
* We want to be able to have stream-type specific information
|
|
||||||
easily accessible also (like mpeg video specific data)
|
|
||||||
* Maybe some other info ???
|
|
||||||
|
|
||||||
The subclasses would then be able to make their own decision based
|
|
||||||
on those objects.
|
|
||||||
Maybe we could have some virtual methods that will be called when a
|
|
||||||
new program is detected, a new stream is added, etc...
|
|
||||||
|
|
||||||
It is the subclass who decides what's to do with a given packet once
|
|
||||||
it's been parsed.
|
|
||||||
tsparse : forward it as-is to the pad corresponding to the program
|
|
||||||
tsdemux : forward it to the proper PS parser
|
|
||||||
hdvsplit : ?
|
|
||||||
|
|
||||||
|
|
||||||
Ideas to be taken into account for a proper demuxer :
|
|
||||||
-----------------------------------------------------
|
|
||||||
* Push-based (with inacurrate seeking)
|
|
||||||
* Pull-based (with fast *AND* accurate seeking)
|
|
||||||
* Modular system to add stream-type specific helper parsing
|
|
||||||
* Doesn't have to be fully fledged, just enough to help any kind of
|
|
||||||
seeking and scanning code.
|
|
||||||
* ...
|
|
||||||
|
|
||||||
Problems to figure out :
|
|
||||||
------------------------
|
|
||||||
* clock
|
|
||||||
Needed for proper dvb playback. mpegtsdemux currently does internal
|
|
||||||
clock estimation... to provide a clock with PCR estimations.
|
|
||||||
A proper way to solve that would be to timestamp the buffers at the
|
|
||||||
source element using the system clock, and then adjusting the PCR
|
|
||||||
against those values. (i.e. doing the opposite of what's done in
|
|
||||||
mpegtsdemux, but it will be more accurate since the timestamping is
|
|
||||||
done at the source).
|
|
||||||
|
|
||||||
|
|
||||||
Bugs that need fixing :
|
|
||||||
-----------------------
|
|
||||||
* Perfomance : Creation/Destruction of buffers is slow
|
* Perfomance : Creation/Destruction of buffers is slow
|
||||||
* => This is due to g_type_instance_create using a dogslow rwlock
|
* => This is due to g_type_instance_create using a dogslow rwlock
|
||||||
which take up to 50% of gst_adapter_take_buffer()
|
which take up to 50% of gst_adapter_take_buffer()
|
||||||
=> Bugzilla #585375 (performance and contention problems)
|
=> Bugzilla #585375 (performance and contention problems)
|
||||||
|
|
||||||
Code structure:
|
|
||||||
|
|
||||||
MpegTSBase
|
|
||||||
+--- MpegTSParse
|
|
||||||
+--- TSDemux
|
|
||||||
|
|
||||||
|
|
||||||
Known limitations and problems :
|
|
||||||
--------------------------------
|
|
||||||
* mpegtspacketizer
|
* mpegtspacketizer
|
||||||
* Assumes 188 bytes packets. It should support all modes.
|
|
||||||
* offset/timestamp of incoming buffers need to be carried on to the
|
* offset/timestamp of incoming buffers need to be carried on to the
|
||||||
sub-buffers in order for several demuxer features to work correctly.
|
sub-buffers in order for several demuxer features to work correctly.
|
||||||
|
|
||||||
* mpegtsparser
|
* mpegtsparser
|
||||||
* SERIOUS room for improvement performance-wise (see callgrind)
|
* SERIOUS room for improvement performance-wise (see callgrind)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Synchronization, Scheduling and Timestamping
|
||||||
|
--------------------------------------------
|
||||||
|
|
||||||
|
A mpeg-ts demuxer can be used in a variety of situations:
|
||||||
|
* lives streaming over DVB, UDP, RTP,..
|
||||||
|
* play-as-you-download like HTTP Live Streaming or UPNP/DLNA
|
||||||
|
* random-access local playback, file, Bluray, ...
|
||||||
|
|
||||||
|
Those use-cases can be categorized in 3 different categories:
|
||||||
|
* Push-based scheduling with live sources [0]
|
||||||
|
* Push-based scheduling with non-live sources
|
||||||
|
* Pull-based scheduling with fast random-access
|
||||||
|
|
||||||
|
Due to the nature of timing within the mpeg-ts format, we need to
|
||||||
|
pay extra attention to the outgoing NEWSEGMENT event and buffer
|
||||||
|
timestamps in order to guarantee proper playback and synchronization
|
||||||
|
of the stream.
|
||||||
|
|
||||||
|
|
||||||
|
1) Live push-based scheduling
|
||||||
|
|
||||||
|
The NEWSEGMENT event will be in time format and is forwarded as is,
|
||||||
|
and the values are cached locally.
|
||||||
|
|
||||||
|
Since the clock is running when the upstream buffers are captured,
|
||||||
|
the outgoing buffer timestamps need to correspond to the incoming
|
||||||
|
buffer timestamp values.
|
||||||
|
|
||||||
|
=> A delta, DTS_delta between incoming buffer timestamp and
|
||||||
|
DTS/PTS needs to be computed.
|
||||||
|
|
||||||
|
=> The outgoing buffers will be timestamped with their PTS values
|
||||||
|
(overflow corrected) offseted by that initial DTS_delta.
|
||||||
|
|
||||||
|
A latency is introduced between the time the buffer containing the
|
||||||
|
first bit of a Access Unit is received in the demuxer and the moment
|
||||||
|
the demuxer pushed out the buffer corresponding to that Access Unit.
|
||||||
|
|
||||||
|
=> That latency needs to be reported. It corresponds to the
|
||||||
|
biggest Access Unit spacing, in this case 1/video-framerate.
|
||||||
|
|
||||||
|
According to the ISO/IEC 13818-1:2007 specifications, D.0.1 Timing
|
||||||
|
mode, the "coded audio and video that represent sound and pictures
|
||||||
|
that are to be presented simultaneously may be separated in time
|
||||||
|
within the coded bit stream by ==>as much as one second<=="
|
||||||
|
|
||||||
|
=> The demuxer will therefore report an added latency of 1s to
|
||||||
|
handle this interleave.
|
||||||
|
|
||||||
|
|
||||||
|
2) Non-live push-based scheduling
|
||||||
|
|
||||||
|
If the upstream NEWSEGMENT is in time format, the NEWSEGMENT event
|
||||||
|
is forwarded as is, and the values are cached locally.
|
||||||
|
|
||||||
|
If upstream does provide a NEWSEGMENT in another format, we need to
|
||||||
|
compute one by taking the default values:
|
||||||
|
start : 0
|
||||||
|
stop : GST_CLOCK_TIME_NONE
|
||||||
|
time : 0
|
||||||
|
|
||||||
|
Since no prerolling is happening downstream and the incoming buffers
|
||||||
|
do not have capture timestamps, we need to ensure the first buffer
|
||||||
|
we push out corresponds to the base segment start runing time.
|
||||||
|
|
||||||
|
=> A delta between the first DTS to output and the segment start
|
||||||
|
position needs to be computed.
|
||||||
|
|
||||||
|
=> The outgoing buffers will be timestamped with their PTS values
|
||||||
|
(overflow corrected) offseted by that initial delta.
|
||||||
|
|
||||||
|
Latency is reported just as with the live use-case.
|
||||||
|
|
||||||
|
|
||||||
|
3) Random access pull-mode
|
||||||
|
|
||||||
|
We do not get a NEWSEGMENT event from upstream, we therefore need to
|
||||||
|
compute the outgoing values.
|
||||||
|
|
||||||
|
The base stream/running time corresponds to the DTS of the first
|
||||||
|
buffer we will output. The DTS_delta becomes that earliest DTS.
|
||||||
|
|
||||||
|
=> FILLME
|
||||||
|
|
||||||
|
X) General notes
|
||||||
|
|
||||||
|
It is assumed that PTS/DTS rollovers are detected and corrected such
|
||||||
|
as the outgoing timestamps never rollover. This can be easily
|
||||||
|
handled by correcting the DTS_delta when such rollovers are
|
||||||
|
detected. The maximum value of a GstClockTimeDiff is almost 3
|
||||||
|
centuries, we therefore have enough margin to handle a decent number
|
||||||
|
of rollovers.
|
||||||
|
|
||||||
|
The generic equation for calculating outgoing buffer timestamps
|
||||||
|
therefore becomes:
|
||||||
|
|
||||||
|
D = DTS_delta, with rollover corrections
|
||||||
|
PTS = PTS of the buffer we are going to push out
|
||||||
|
TS = Timestamp of the outgoing buffer
|
||||||
|
|
||||||
|
==> TS = PTS + D
|
||||||
|
|
||||||
|
If seeking is handled upstream for push-based cases, whether live or
|
||||||
|
not, no extra modification is required.
|
||||||
|
|
||||||
|
If seeking is handled by the demuxer in the non-live push-based
|
||||||
|
cases (converting from TIME to BYTES), the demuxer will need to
|
||||||
|
set the segment start/time values to the requested seek position.
|
||||||
|
The DTS_delta will also have to be recomputed to take into account
|
||||||
|
the seek position.
|
||||||
|
|
||||||
|
|
||||||
|
[0] When talking about live sources, we mean this in the GStreamer
|
||||||
|
definition of live sources, which is to say sources where if we miss
|
||||||
|
the capture, we will miss the data to be captured. Sources which do
|
||||||
|
internal buffering (like TCP connections or file descriptors) are
|
||||||
|
*NOT* live sources.
|
||||||
|
|
|
@ -44,6 +44,12 @@
|
||||||
#include "payload_parsers.h"
|
#include "payload_parsers.h"
|
||||||
#include "pesparse.h"
|
#include "pesparse.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* tsdemux
|
||||||
|
*
|
||||||
|
* See TODO for explanations on improvements needed
|
||||||
|
*/
|
||||||
|
|
||||||
/* latency in mseconds */
|
/* latency in mseconds */
|
||||||
#define TS_LATENCY 700
|
#define TS_LATENCY 700
|
||||||
|
|
||||||
|
@ -74,8 +80,6 @@ static GQuark QUARK_PTS;
|
||||||
static GQuark QUARK_DTS;
|
static GQuark QUARK_DTS;
|
||||||
static GQuark QUARK_OFFSET;
|
static GQuark QUARK_OFFSET;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
typedef enum
|
typedef enum
|
||||||
{
|
{
|
||||||
PENDING_PACKET_EMPTY = 0, /* No pending packet/buffer
|
PENDING_PACKET_EMPTY = 0, /* No pending packet/buffer
|
||||||
|
|
|
@ -91,6 +91,7 @@ static void gst_h264_parse_get_property (GObject * object, guint prop_id,
|
||||||
GValue * value, GParamSpec * pspec);
|
GValue * value, GParamSpec * pspec);
|
||||||
|
|
||||||
static gboolean gst_h264_parse_set_caps (GstBaseParse * parse, GstCaps * caps);
|
static gboolean gst_h264_parse_set_caps (GstBaseParse * parse, GstCaps * caps);
|
||||||
|
static GstCaps *gst_h264_parse_get_caps (GstBaseParse * parse);
|
||||||
static GstFlowReturn gst_h264_parse_chain (GstPad * pad, GstBuffer * buffer);
|
static GstFlowReturn gst_h264_parse_chain (GstPad * pad, GstBuffer * buffer);
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
@ -138,6 +139,7 @@ gst_h264_parse_class_init (GstH264ParseClass * klass)
|
||||||
parse_class->pre_push_frame =
|
parse_class->pre_push_frame =
|
||||||
GST_DEBUG_FUNCPTR (gst_h264_parse_pre_push_frame);
|
GST_DEBUG_FUNCPTR (gst_h264_parse_pre_push_frame);
|
||||||
parse_class->set_sink_caps = GST_DEBUG_FUNCPTR (gst_h264_parse_set_caps);
|
parse_class->set_sink_caps = GST_DEBUG_FUNCPTR (gst_h264_parse_set_caps);
|
||||||
|
parse_class->get_sink_caps = GST_DEBUG_FUNCPTR (gst_h264_parse_get_caps);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
@ -337,7 +339,7 @@ gst_h264_parse_wrap_nal (GstH264Parse * h264parse, guint format, guint8 * data,
|
||||||
guint size)
|
guint size)
|
||||||
{
|
{
|
||||||
GstBuffer *buf;
|
GstBuffer *buf;
|
||||||
const guint nl = h264parse->nal_length_size;
|
guint nl = h264parse->nal_length_size;
|
||||||
|
|
||||||
GST_DEBUG_OBJECT (h264parse, "nal length %d", size);
|
GST_DEBUG_OBJECT (h264parse, "nal length %d", size);
|
||||||
|
|
||||||
|
@ -345,7 +347,10 @@ gst_h264_parse_wrap_nal (GstH264Parse * h264parse, guint format, guint8 * data,
|
||||||
if (format == GST_H264_PARSE_FORMAT_AVC) {
|
if (format == GST_H264_PARSE_FORMAT_AVC) {
|
||||||
GST_WRITE_UINT32_BE (GST_BUFFER_DATA (buf), size << (32 - 8 * nl));
|
GST_WRITE_UINT32_BE (GST_BUFFER_DATA (buf), size << (32 - 8 * nl));
|
||||||
} else {
|
} else {
|
||||||
g_assert (nl == 4);
|
/* HACK: nl should always be 4 here, otherwise this won't work.
|
||||||
|
* There are legit cases where nl in avc stream is 2, but byte-stream
|
||||||
|
* SC is still always 4 bytes. */
|
||||||
|
nl = 4;
|
||||||
GST_WRITE_UINT32_BE (GST_BUFFER_DATA (buf), 1);
|
GST_WRITE_UINT32_BE (GST_BUFFER_DATA (buf), 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1333,6 +1338,38 @@ refuse_caps:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static GstCaps *
|
||||||
|
gst_h264_parse_get_caps (GstBaseParse * parse)
|
||||||
|
{
|
||||||
|
GstCaps *peercaps;
|
||||||
|
GstCaps *res;
|
||||||
|
|
||||||
|
peercaps = gst_pad_get_allowed_caps (GST_BASE_PARSE_SRC_PAD (parse));
|
||||||
|
if (peercaps) {
|
||||||
|
guint i, n;
|
||||||
|
|
||||||
|
peercaps = gst_caps_make_writable (peercaps);
|
||||||
|
n = gst_caps_get_size (peercaps);
|
||||||
|
for (i = 0; i < n; i++) {
|
||||||
|
GstStructure *s = gst_caps_get_structure (peercaps, i);
|
||||||
|
gst_structure_remove_field (s, "alignment");
|
||||||
|
gst_structure_remove_field (s, "stream-format");
|
||||||
|
}
|
||||||
|
|
||||||
|
res =
|
||||||
|
gst_caps_intersect_full (peercaps,
|
||||||
|
gst_pad_get_pad_template_caps (GST_BASE_PARSE_SRC_PAD (parse)),
|
||||||
|
GST_CAPS_INTERSECT_FIRST);
|
||||||
|
gst_caps_unref (peercaps);
|
||||||
|
} else {
|
||||||
|
res =
|
||||||
|
gst_caps_copy (gst_pad_get_pad_template_caps (GST_BASE_PARSE_SRC_PAD
|
||||||
|
(parse)));
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
static GstFlowReturn
|
static GstFlowReturn
|
||||||
gst_h264_parse_chain (GstPad * pad, GstBuffer * buffer)
|
gst_h264_parse_chain (GstPad * pad, GstBuffer * buffer)
|
||||||
{
|
{
|
||||||
|
|
|
@ -11,4 +11,4 @@ libgstacmmp3dec_la_LIBADD = \
|
||||||
-lgsttag-$(GST_MAJORMINOR) \
|
-lgsttag-$(GST_MAJORMINOR) \
|
||||||
-lmsacm32
|
-lmsacm32
|
||||||
libgstacmmp3dec_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) $(DIRECTSOUND_LDFLAGS)
|
libgstacmmp3dec_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) $(DIRECTSOUND_LDFLAGS)
|
||||||
|
libgstacmmp3dec_la_LIBTOOLFLAGS = --tag=disable-static
|
||||||
|
|
|
@ -23,4 +23,4 @@ libgstlinsys_la_CFLAGS = \
|
||||||
libgstlinsys_la_LDFLAGS = \
|
libgstlinsys_la_LDFLAGS = \
|
||||||
$(GST_PLUGIN_LDFLAGS)
|
$(GST_PLUGIN_LDFLAGS)
|
||||||
libgstlinsys_la_LIBADD = $(GST_BASE_LIBS) $(GST_LIBS)
|
libgstlinsys_la_LIBADD = $(GST_BASE_LIBS) $(GST_LIBS)
|
||||||
|
libgstlinsys_la_LIBTOOLFLAGS = --tag=disable-static
|
||||||
|
|
|
@ -622,6 +622,22 @@ wait_for_element_message (GstElement * camera, const gchar * name,
|
||||||
return msg;
|
return msg;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
wait_for_idle_state (void)
|
||||||
|
{
|
||||||
|
gboolean idle = FALSE;
|
||||||
|
|
||||||
|
/* not the ideal way, but should be enough for testing */
|
||||||
|
while (idle == FALSE) {
|
||||||
|
g_object_get (camera, "idle", &idle, NULL);
|
||||||
|
if (idle)
|
||||||
|
break;
|
||||||
|
|
||||||
|
g_usleep (GST_SECOND / 5);
|
||||||
|
}
|
||||||
|
fail_unless (idle);
|
||||||
|
}
|
||||||
|
|
||||||
GST_START_TEST (test_single_image_capture)
|
GST_START_TEST (test_single_image_capture)
|
||||||
{
|
{
|
||||||
gboolean idle;
|
gboolean idle;
|
||||||
|
@ -652,8 +668,7 @@ GST_START_TEST (test_single_image_capture)
|
||||||
/* check that we got a preview image */
|
/* check that we got a preview image */
|
||||||
check_preview_image (camera, image_filename, 0);
|
check_preview_image (camera, image_filename, 0);
|
||||||
|
|
||||||
g_object_get (camera, "idle", &idle, NULL);
|
wait_for_idle_state ();
|
||||||
fail_unless (idle);
|
|
||||||
gst_element_set_state (GST_ELEMENT (camera), GST_STATE_NULL);
|
gst_element_set_state (GST_ELEMENT (camera), GST_STATE_NULL);
|
||||||
check_file_validity (image_filename, 0, NULL, 0, 0, NO_AUDIO);
|
check_file_validity (image_filename, 0, NULL, 0, 0, NO_AUDIO);
|
||||||
}
|
}
|
||||||
|
@ -705,8 +720,7 @@ GST_START_TEST (test_multiple_image_captures)
|
||||||
check_preview_image (camera, image_filename, i);
|
check_preview_image (camera, image_filename, i);
|
||||||
}
|
}
|
||||||
|
|
||||||
g_object_get (camera, "idle", &idle, NULL);
|
wait_for_idle_state ();
|
||||||
fail_unless (idle);
|
|
||||||
gst_element_set_state (GST_ELEMENT (camera), GST_STATE_NULL);
|
gst_element_set_state (GST_ELEMENT (camera), GST_STATE_NULL);
|
||||||
for (i = 0; i < 3; i++) {
|
for (i = 0; i < 3; i++) {
|
||||||
check_file_validity (image_filename, i, NULL, widths[i], heights[i],
|
check_file_validity (image_filename, i, NULL, widths[i], heights[i],
|
||||||
|
@ -756,8 +770,7 @@ GST_START_TEST (test_single_video_recording)
|
||||||
fail_unless (msg != NULL);
|
fail_unless (msg != NULL);
|
||||||
gst_message_unref (msg);
|
gst_message_unref (msg);
|
||||||
|
|
||||||
g_object_get (camera, "idle", &idle, NULL);
|
wait_for_idle_state ();
|
||||||
fail_unless (idle);
|
|
||||||
gst_element_set_state (GST_ELEMENT (camera), GST_STATE_NULL);
|
gst_element_set_state (GST_ELEMENT (camera), GST_STATE_NULL);
|
||||||
|
|
||||||
check_file_validity (video_filename, 0, NULL, 0, 0, WITH_AUDIO);
|
check_file_validity (video_filename, 0, NULL, 0, 0, WITH_AUDIO);
|
||||||
|
@ -819,8 +832,7 @@ GST_START_TEST (test_multiple_video_recordings)
|
||||||
|
|
||||||
check_preview_image (camera, video_filename, i);
|
check_preview_image (camera, video_filename, i);
|
||||||
|
|
||||||
g_object_get (camera, "idle", &idle, NULL);
|
wait_for_idle_state ();
|
||||||
fail_unless (idle);
|
|
||||||
}
|
}
|
||||||
gst_element_set_state (GST_ELEMENT (camera), GST_STATE_NULL);
|
gst_element_set_state (GST_ELEMENT (camera), GST_STATE_NULL);
|
||||||
|
|
||||||
|
@ -834,7 +846,6 @@ GST_END_TEST;
|
||||||
|
|
||||||
GST_START_TEST (test_image_video_cycle)
|
GST_START_TEST (test_image_video_cycle)
|
||||||
{
|
{
|
||||||
gboolean idle;
|
|
||||||
gint i;
|
gint i;
|
||||||
|
|
||||||
if (!camera)
|
if (!camera)
|
||||||
|
@ -854,8 +865,7 @@ GST_START_TEST (test_image_video_cycle)
|
||||||
const gchar *img_filename;
|
const gchar *img_filename;
|
||||||
const gchar *vid_filename;
|
const gchar *vid_filename;
|
||||||
|
|
||||||
g_object_get (camera, "idle", &idle, NULL);
|
wait_for_idle_state ();
|
||||||
fail_unless (idle);
|
|
||||||
|
|
||||||
/* take a picture */
|
/* take a picture */
|
||||||
img_filename = make_const_file_name (image_filename, i);
|
img_filename = make_const_file_name (image_filename, i);
|
||||||
|
@ -885,10 +895,9 @@ GST_START_TEST (test_image_video_cycle)
|
||||||
gst_message_unref (msg);
|
gst_message_unref (msg);
|
||||||
|
|
||||||
check_preview_image (camera, vid_filename, i);
|
check_preview_image (camera, vid_filename, i);
|
||||||
|
|
||||||
/* wait for capture to finish */
|
|
||||||
g_usleep (G_USEC_PER_SEC);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
wait_for_idle_state ();
|
||||||
gst_element_set_state (GST_ELEMENT (camera), GST_STATE_NULL);
|
gst_element_set_state (GST_ELEMENT (camera), GST_STATE_NULL);
|
||||||
|
|
||||||
/* validate all the files */
|
/* validate all the files */
|
||||||
|
@ -1193,8 +1202,7 @@ GST_START_TEST (test_idle_property)
|
||||||
|
|
||||||
check_preview_image (camera, video_filename, 0);
|
check_preview_image (camera, video_filename, 0);
|
||||||
|
|
||||||
g_object_get (camera, "idle", &idle, NULL);
|
wait_for_idle_state ();
|
||||||
fail_unless (idle);
|
|
||||||
|
|
||||||
gst_element_set_state (GST_ELEMENT (camera), GST_STATE_NULL);
|
gst_element_set_state (GST_ELEMENT (camera), GST_STATE_NULL);
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue