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:
Wim Taymans 2011-11-17 17:32:42 +01:00
commit bc6ed0bf97
26 changed files with 1157 additions and 1590 deletions

View file

@ -1,12 +1,13 @@
plugin_LTLIBRARIES = libgstcelt.la
libgstcelt_la_SOURCES = gstcelt.c gstceltdec.c gstceltenc.c
libgstcelt_la_CFLAGS = \
libgstcelt_la_CFLAGS = -DGST_USE_UNSTABLE_API \
$(GST_PLUGINS_BASE_CFLAGS) \
$(GST_CFLAGS) \
$(CELT_CFLAGS)
libgstcelt_la_LIBADD = \
$(GST_PLUGINS_BASE_LIBS) -lgsttag-$(GST_MAJORMINOR) \
$(GST_PLUGINS_BASE_LIBS) \
-lgstaudio-$(GST_MAJORMINOR) -lgsttag-$(GST_MAJORMINOR) \
$(GST_BASE_LIBS) \
$(GST_LIBS) \
$(CELT_LIBS)

View file

@ -68,38 +68,26 @@ GST_STATIC_PAD_TEMPLATE ("sink",
);
#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 GstFlowReturn celt_dec_chain (GstPad * pad, GstBuffer * buf);
static gboolean celt_dec_sink_setcaps (GstPad * pad, GstCaps * caps);
static GstStateChangeReturn celt_dec_change_state (GstElement * element,
GstStateChange transition);
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 gboolean gst_celt_dec_start (GstAudioDecoder * dec);
static gboolean gst_celt_dec_stop (GstAudioDecoder * dec);
static gboolean gst_celt_dec_set_format (GstAudioDecoder * bdec,
GstCaps * caps);
static GstFlowReturn gst_celt_dec_handle_frame (GstAudioDecoder * dec,
GstBuffer * buffer);
static void
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_static_pad_template_get (&celt_dec_src_factory));
@ -118,11 +106,8 @@ gst_celt_dec_class_init (GstCeltDecClass * klass)
static void
gst_celt_dec_reset (GstCeltDec * dec)
{
gst_segment_init (&dec->segment, GST_FORMAT_UNDEFINED);
dec->granulepos = -1;
dec->packetno = 0;
dec->frame_size = 0;
dec->frame_duration = 0;
if (dec->state) {
celt_decoder_destroy (dec->state);
dec->state = NULL;
@ -145,411 +130,36 @@ gst_celt_dec_reset (GstCeltDec * dec)
static void
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);
}
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));
gboolean ret = TRUE;
GstStructure *s;
const GValue *streamheader;
GstCeltDec *cd = GST_CELT_DEC (dec);
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;
GST_DEBUG_OBJECT (dec, "start");
gst_celt_dec_reset (cd);
header = gst_value_array_get_value (streamheader, 0);
if (header && G_VALUE_HOLDS (header, GST_TYPE_BUFFER)) {
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);
}
/* we know about concealment */
gst_audio_decoder_set_plc_aware (dec, TRUE);
vorbiscomment = gst_value_array_get_value (streamheader, 1);
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;
return TRUE;
}
static gboolean
celt_dec_convert (GstPad * pad,
GstFormat src_format, gint64 src_value,
GstFormat * dest_format, gint64 * dest_value)
gst_celt_dec_stop (GstAudioDecoder * dec)
{
gboolean res = TRUE;
GstCeltDec *dec;
guint64 scale = 1;
GstCeltDec *cd = GST_CELT_DEC (dec);
dec = GST_CELT_DEC (gst_pad_get_parent (pad));
GST_DEBUG_OBJECT (dec, "stop");
gst_celt_dec_reset (cd);
if (dec->packetno < 1) {
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;
}
return TRUE;
}
static GstFlowReturn
celt_dec_chain_parse_header (GstCeltDec * dec, GstBuffer * buf)
gst_celt_dec_parse_header (GstCeltDec * dec, GstBuffer * buf)
{
GstCaps *caps;
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);
#endif
dec->frame_duration = gst_util_uint64_scale_int (dec->frame_size,
GST_SECOND, dec->header.sample_rate);
/* set caps */
caps = gst_caps_new_simple ("audio/x-raw-int",
"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",
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;
gst_caps_unref (caps);
@ -650,7 +257,7 @@ nego_failed:
}
static GstFlowReturn
celt_dec_chain_parse_comments (GstCeltDec * dec, GstBuffer * buf)
gst_celt_dec_parse_comments (GstCeltDec * dec, GstBuffer * buf)
{
GstTagList *list;
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_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 (ver);
@ -694,8 +302,7 @@ celt_dec_chain_parse_comments (GstCeltDec * dec, GstBuffer * buf)
}
static GstFlowReturn
celt_dec_chain_parse_data (GstCeltDec * dec, GstBuffer * buf,
GstClockTime timestamp, GstClockTime duration)
gst_celt_dec_parse_data (GstCeltDec * dec, GstBuffer * buf)
{
GstFlowReturn res = GST_FLOW_OK;
gint size;
@ -705,33 +312,23 @@ celt_dec_chain_parse_data (GstCeltDec * dec, GstBuffer * buf,
gint error = CELT_OK;
int skip = 0;
if (timestamp != -1) {
dec->segment.last_stop = timestamp;
dec->granulepos = -1;
}
if (!dec->frame_size)
goto not_negotiated;
if (buf) {
if (G_LIKELY (GST_BUFFER_SIZE (buf))) {
data = GST_BUFFER_DATA (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 {
/* FIXME ? actually consider how much concealment is needed */
/* concealment data, pass NULL as the bits parameters */
GST_DEBUG_OBJECT (dec, "creating concealment data");
data = NULL;
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
/* what will be 0.11.5, I guess, but no versioning yet in git */
celt_decoder_ctl (dec->state, CELT_GET_LOOKAHEAD_REQUEST, &skip);
@ -740,9 +337,9 @@ celt_dec_chain_parse_data (GstCeltDec * dec, GstBuffer * buf,
#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_PAD_CAPS (dec->srcpad), &outbuf);
GST_PAD_CAPS (GST_AUDIO_DECODER_SRC_PAD (dec)), &outbuf);
if (res != GST_FLOW_OK) {
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;
}
if (dec->granulepos == -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);
res = gst_audio_decoder_finish_frame (GST_AUDIO_DECODER (dec), outbuf, 1);
if (res != GST_FLOW_OK)
GST_DEBUG_OBJECT (dec, "flow: %s", gst_flow_get_name (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
celt_dec_chain (GstPad * pad, GstBuffer * buf)
gst_celt_dec_handle_frame (GstAudioDecoder * bdec, GstBuffer * buf)
{
GstFlowReturn res;
GstCeltDec *dec;
dec = GST_CELT_DEC (gst_pad_get_parent (pad));
dec = GST_CELT_DEC (bdec);
if (GST_BUFFER_IS_DISCONT (buf)) {
dec->discont = TRUE;
}
/* no fancy draining */
if (G_UNLIKELY (!buf))
return GST_FLOW_OK;
/* If we have the streamheader and vorbiscomment from the caps already
* ignore them here */
@ -835,10 +461,14 @@ celt_dec_chain (GstPad * pad, GstBuffer * buf)
if (GST_BUFFER_SIZE (dec->streamheader) == GST_BUFFER_SIZE (buf)
&& memcmp (GST_BUFFER_DATA (dec->streamheader), GST_BUFFER_DATA (buf),
GST_BUFFER_SIZE (buf)) == 0) {
GST_DEBUG_OBJECT (dec, "found streamheader");
gst_audio_decoder_finish_frame (bdec, NULL, 1);
res = GST_FLOW_OK;
} else if (GST_BUFFER_SIZE (dec->vorbiscomment) == GST_BUFFER_SIZE (buf)
&& memcmp (GST_BUFFER_DATA (dec->vorbiscomment), GST_BUFFER_DATA (buf),
GST_BUFFER_SIZE (buf)) == 0) {
GST_DEBUG_OBJECT (dec, "found vorbiscomments");
gst_audio_decoder_finish_frame (bdec, NULL, 1);
res = GST_FLOW_OK;
} else {
GList *l;
@ -848,66 +478,36 @@ celt_dec_chain (GstPad * pad, GstBuffer * buf)
if (GST_BUFFER_SIZE (header) == GST_BUFFER_SIZE (buf) &&
memcmp (GST_BUFFER_DATA (header), GST_BUFFER_DATA (buf),
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;
goto done;
}
}
res =
celt_dec_chain_parse_data (dec, buf, GST_BUFFER_TIMESTAMP (buf),
GST_BUFFER_DURATION (buf));
res = gst_celt_dec_parse_data (dec, buf);
}
} else {
/* Otherwise fall back to packet counting and assume that the
* first two packets are the headers. */
if (dec->packetno == 0)
res = celt_dec_chain_parse_header (dec, buf);
else if (dec->packetno == 1)
res = celt_dec_chain_parse_comments (dec, buf);
else if (dec->packetno <= 1 + dec->header.extra_headers)
if (dec->packetno == 0) {
GST_DEBUG_OBJECT (dec, "counted streamheader");
res = gst_celt_dec_parse_header (dec, buf);
gst_audio_decoder_finish_frame (bdec, NULL, 1);
} 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;
else
res = celt_dec_chain_parse_data (dec, buf, GST_BUFFER_TIMESTAMP (buf),
GST_BUFFER_DURATION (buf));
} else {
res = gst_celt_dec_parse_data (dec, buf);
}
}
done:
dec->packetno++;
gst_buffer_unref (buf);
gst_object_unref (dec);
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;
}

View file

@ -22,6 +22,7 @@
#define __GST_CELT_DEC_H__
#include <gst/gst.h>
#include <gst/audio/gstaudiodecoder.h>
#include <celt/celt.h>
#include <celt/celt_header.h>
@ -42,22 +43,15 @@ typedef struct _GstCeltDec GstCeltDec;
typedef struct _GstCeltDecClass GstCeltDecClass;
struct _GstCeltDec {
GstElement element;
/* pads */
GstPad *sinkpad;
GstPad *srcpad;
GstAudioDecoder element;
CELTDecoder *state;
CELTMode *mode;
CELTHeader header;
gint frame_size;
GstClockTime frame_duration;
guint64 packetno;
GstSegment segment; /* STREAM LOCK */
gint64 granulepos; /* -1 = needs to be set from current time */
gboolean discont;
GstBuffer *streamheader;
@ -66,7 +60,7 @@ struct _GstCeltDec {
};
struct _GstCeltDecClass {
GstElementClass parent_class;
GstAudioDecoderClass parent_class;
};
GType gst_celt_dec_get_type (void);

File diff suppressed because it is too large Load diff

View file

@ -24,7 +24,7 @@
#include <gst/gst.h>
#include <gst/base/gstadapter.h>
#include <gst/audio/gstaudioencoder.h>
#include <celt/celt.h>
#include <celt/celt_header.h>
@ -49,16 +49,11 @@ typedef struct _GstCeltEnc GstCeltEnc;
typedef struct _GstCeltEncClass GstCeltEncClass;
struct _GstCeltEnc {
GstElement element;
/* pads */
GstPad *sinkpad;
GstPad *srcpad;
GstAudioEncoder element;
CELTHeader header;
CELTMode *mode;
CELTEncoder *state;
GstAdapter *adapter;
gint bitrate;
gint frame_size;
@ -72,26 +67,12 @@ struct _GstCeltEnc {
gint channels;
gint rate;
gboolean setup;
gboolean header_sent;
gboolean eos;
guint64 samples_in;
guint64 bytes_out;
guint64 frameno;
guint64 frameno_out;
GstClockTime start_ts;
GstClockTime next_ts;
guint64 granulepos_offset;
GSList *headers;
};
struct _GstCeltEncClass {
GstElementClass parent_class;
/* signals */
void (*frame_encoded) (GstElement *element);
GstAudioEncoderClass parent_class;
};
GType gst_celt_enc_get_type (void);

View file

@ -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_LIBADD = $(GST_BASE_LIBS) $(GST_LIBS) $(MIMIC_LIBS)
libgstmimic_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS)
libgstmimic_la_LIBTOOLFLAGS = --tag=disable-static
noinst_HEADERS = gstmimdec.h gstmimenc.h

View file

@ -36,6 +36,7 @@ libgstopencv_la_LIBADD = $(GST_LIBS) $(GST_BASE_LIBS) $(OPENCV_LIBS) \
$(GSTPB_BASE_LIBS) -lgstvideo-$(GST_MAJORMINOR)
libgstopencv_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS)
libgstopencv_la_LIBTOOLFLAGS = --tag=disable-static
# headers we need but don't want installed
noinst_HEADERS = gstopencvvideofilter.h gstopencvutils.h \

View file

@ -3,6 +3,7 @@
* Copyright (C) 2005 Thomas Vander Stichele <thomas@apestaart.org>
* Copyright (C) 2005 Ronald S. Bultje <rbultje@ronald.bitfreak.net>
* 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
* copy of this software and associated documentation files (the "Software"),
@ -48,14 +49,27 @@
*
* Performs face detection on videos and images.
*
* The image is scaled down multiple times using the GstFacedetect::scale-factor
* until the size is &lt;= GstFacedetect::min-size-width or
* GstFacedetect::min-size-height.
*
* <refsect2>
* <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>
*/
/* 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
# include <config.h>
#endif
@ -67,7 +81,10 @@
GST_DEBUG_CATEGORY_STATIC (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_FLAGS 0
#define DEFAULT_MIN_NEIGHBORS 3
@ -85,7 +102,10 @@ enum
{
PROP_0,
PROP_DISPLAY,
PROP_PROFILE,
PROP_FACE_PROFILE,
PROP_NOSE_PROFILE,
PROP_MOUTH_PROFILE,
PROP_EYES_PROFILE,
PROP_SCALE_FACTOR,
PROP_MIN_NEIGHBORS,
PROP_FLAGS,
@ -141,7 +161,7 @@ static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src",
GST_STATIC_CAPS ("video/x-raw-rgb")
);
GST_BOILERPLATE (Gstfacedetect, gst_facedetect, GstOpencvVideoFilter,
GST_BOILERPLATE (GstFacedetect, gst_facedetect, GstOpencvVideoFilter,
GST_TYPE_OPENCV_VIDEO_FILTER);
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,
GstBuffer * buf, IplImage * img);
static void gst_facedetect_load_profile (Gstfacedetect * filter);
static CvHaarClassifierCascade *gst_facedetect_load_profile (GstFacedetect *
filter, gchar * profile);
/* Clean up */
static void
gst_facedetect_finalize (GObject * obj)
{
Gstfacedetect *filter = GST_FACEDETECT (obj);
GstFacedetect *filter = GST_FACEDETECT (obj);
if (filter->cvGray) {
if (filter->cvGray)
cvReleaseImage (&filter->cvGray);
}
if (filter->cvStorage) {
if (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);
}
@ -196,7 +227,7 @@ gst_facedetect_base_init (gpointer gclass)
/* initialize the facedetect's class */
static void
gst_facedetect_class_init (GstfacedetectClass * klass)
gst_facedetect_class_init (GstFacedetectClass * klass)
{
GObjectClass *gobject_class;
GstOpencvVideoFilterClass *gstopencvbasefilter_class;
@ -215,17 +246,31 @@ gst_facedetect_class_init (GstfacedetectClass * klass)
g_param_spec_boolean ("display", "Display",
"Sets whether the detected faces should be highlighted in the output",
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",
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_param_spec_flags ("flags", "Flags", "Flags to cvHaarDetectObjects",
GST_TYPE_OPENCV_FACE_DETECT_FLAGS, DEFAULT_FLAGS,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
g_object_class_install_property (gobject_class, PROP_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,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
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,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
g_object_class_install_property (gobject_class, PROP_MIN_SIZE_WIDTH,
g_param_spec_int ("min-size-width", "Minimum size width",
"Minimum window width size", 0, G_MAXINT, DEFAULT_MIN_SIZE_WIDTH,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
g_param_spec_int ("min-size-width", "Minimum face width",
"Minimum area width to be recognized as a face", 0, G_MAXINT,
DEFAULT_MIN_SIZE_WIDTH, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
g_object_class_install_property (gobject_class, PROP_MIN_SIZE_HEIGHT,
g_param_spec_int ("min-size-height", "Minimum size height",
"Minimum window height size", 0, G_MAXINT, DEFAULT_MIN_SIZE_HEIGHT,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
g_param_spec_int ("min-size-height", "Minimum face height",
"Minimum area height to be recognized as a face", 0, G_MAXINT,
DEFAULT_MIN_SIZE_HEIGHT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
}
/* initialize the new element
* instantiate pads and add them to element
* set pad calback functions
* initialize instance structure
*/
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->scale_factor = DEFAULT_SCALE_FACTOR;
filter->min_neighbors = DEFAULT_MIN_NEIGHBORS;
filter->flags = DEFAULT_FLAGS;
filter->min_size_width = DEFAULT_MIN_SIZE_WIDTH;
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),
TRUE);
@ -268,13 +321,40 @@ static void
gst_facedetect_set_property (GObject * object, guint prop_id,
const GValue * value, GParamSpec * pspec)
{
Gstfacedetect *filter = GST_FACEDETECT (object);
GstFacedetect *filter = GST_FACEDETECT (object);
switch (prop_id) {
case PROP_PROFILE:
g_free (filter->profile);
filter->profile = g_value_dup_string (value);
gst_facedetect_load_profile (filter);
case PROP_FACE_PROFILE:
g_free (filter->face_profile);
if (filter->cvFaceDetect)
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;
case PROP_DISPLAY:
filter->display = g_value_get_boolean (value);
@ -304,11 +384,20 @@ static void
gst_facedetect_get_property (GObject * object, guint prop_id,
GValue * value, GParamSpec * pspec)
{
Gstfacedetect *filter = GST_FACEDETECT (object);
GstFacedetect *filter = GST_FACEDETECT (object);
switch (prop_id) {
case PROP_PROFILE:
g_value_set_string (value, filter->profile);
case PROP_FACE_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;
case PROP_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 out_width, gint out_height, gint out_depth, gint out_channels)
{
Gstfacedetect *filter;
GstFacedetect *filter;
filter = GST_FACEDETECT (transform);
@ -361,7 +450,7 @@ gst_facedetect_set_caps (GstOpencvVideoFilter * transform, gint in_width,
}
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);
GstStructure *s;
@ -389,21 +478,29 @@ static GstFlowReturn
gst_facedetect_transform_ip (GstOpencvVideoFilter * base, GstBuffer * buf,
IplImage * img)
{
Gstfacedetect *filter;
CvSeq *faces;
int i;
GstFacedetect *filter = GST_FACEDETECT (base);
filter = GST_FACEDETECT (base);
cvCvtColor (img, filter->cvGray, CV_RGB2GRAY);
cvClearMemStorage (filter->cvStorage);
if (filter->cvCascade) {
if (filter->cvFaceDetect) {
GstMessage *msg = NULL;
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 =
cvHaarDetectObjects (filter->cvGray, filter->cvCascade,
cvHaarDetectObjects (filter->cvGray, filter->cvFaceDetect,
filter->cvStorage, filter->scale_factor, filter->min_neighbors,
filter->flags, cvSize (filter->min_size_width, filter->min_size_height)
#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++) {
CvRect *r = (CvRect *) cvGetSeqElem (faces, i);
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,
"y", G_TYPE_UINT, r->y,
"width", G_TYPE_UINT, r->width,
"height", G_TYPE_UINT, r->height, NULL);
GstMessage *m = gst_message_new_element (GST_OBJECT (filter), s);
if (nose && nose->total) {
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);
gst_value_set_structure (&value, s);
gst_value_list_append_value (&facelist, &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) {
if (gst_buffer_is_writable (buf)) {
CvPoint center;
int radius;
center.x = cvRound ((r->x + r->width * 0.5));
center.y = cvRound ((r->y + r->height * 0.5));
radius = cvRound ((r->width + r->height) * 0.25);
cvCircle (img, center, radius, CV_RGB (255, 32, 32), 3, 8, 0);
} else {
GST_DEBUG_OBJECT (filter, "Buffer is not writable, not drawing "
"circles for faces");
w = r->width / 2;
h = r->height / 2;
center.x = cvRound ((r->x + w));
center.y = cvRound ((r->y + h));
axes.width = w;
axes.height = h * 1.25; /* tweak for face form */
cvEllipse (img, center, axes, 0.0, 0.0, 360.0, CV_RGB (cr, cg, cb),
3, 8, 0);
if (nose && nose->total) {
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) {
@ -462,14 +691,16 @@ gst_facedetect_transform_ip (GstOpencvVideoFilter * base, GstBuffer * buf,
}
static void
gst_facedetect_load_profile (Gstfacedetect * filter)
static CvHaarClassifierCascade *
gst_facedetect_load_profile (GstFacedetect * filter, gchar * profile)
{
filter->cvCascade =
(CvHaarClassifierCascade *) cvLoad (filter->profile, 0, 0, 0);
if (!filter->cvCascade) {
GST_WARNING ("Couldn't load Haar classifier cascade: %s.", filter->profile);
CvHaarClassifierCascade *cascade;
if (!(cascade = (CvHaarClassifierCascade *) cvLoad (profile, 0, 0, 0))) {
GST_WARNING_OBJECT (filter, "Couldn't load Haar classifier cascade: %s.",
profile);
}
return cascade;
}

View file

@ -3,6 +3,7 @@
* Copyright (C) 2005 Thomas Vander Stichele <thomas@apestaart.org>
* Copyright (C) 2005 Ronald S. Bultje <rbultje@ronald.bitfreak.net>
* 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
* copy of this software and associated documentation files (the "Software"),
@ -59,23 +60,26 @@ G_BEGIN_DECLS
#define GST_TYPE_FACEDETECT \
(gst_facedetect_get_type())
#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) \
(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) \
(G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_FACEDETECT))
#define GST_IS_FACEDETECT_CLASS(klass) \
(G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_FACEDETECT))
typedef struct _Gstfacedetect Gstfacedetect;
typedef struct _GstfacedetectClass GstfacedetectClass;
typedef struct _GstFacedetect GstFacedetect;
typedef struct _GstFacedetectClass GstFacedetectClass;
struct _Gstfacedetect
struct _GstFacedetect
{
GstOpencvVideoFilter element;
gboolean display;
gchar *profile;
gchar *face_profile;
gchar *nose_profile;
gchar *mouth_profile;
gchar *eyes_profile;
gdouble scale_factor;
gint min_neighbors;
gint flags;
@ -83,11 +87,14 @@ struct _Gstfacedetect
gint min_size_height;
IplImage *cvGray;
CvHaarClassifierCascade *cvCascade;
CvHaarClassifierCascade *cvFaceDetect;
CvHaarClassifierCascade *cvNoseDetect;
CvHaarClassifierCascade *cvMouthDetect;
CvHaarClassifierCascade *cvEyesDetect;
CvMemStorage *cvStorage;
};
struct _GstfacedetectClass
struct _GstFacedetectClass
{
GstOpencvVideoFilterClass parent_class;
};

View file

@ -67,14 +67,14 @@ GST_STATIC_PAD_TEMPLATE ("sink",
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_stop (GstAudioDecoder * dec);
static GstFlowReturn gst_opus_dec_handle_frame (GstAudioDecoder * dec,
GstBuffer * buffer);
static gboolean gst_opus_dec_set_format (GstAudioDecoder * bdec,
GstCaps * caps);
static GstFlowReturn opus_dec_chain_parse_data (GstOpusDec * dec,
GstBuffer * buf, GstClockTime timestamp, GstClockTime duration);
static void
gst_opus_dec_class_init (GstOpusDecClass * klass)
@ -112,8 +112,6 @@ gst_opus_dec_reset (GstOpusDec * dec)
dec->state = NULL;
}
dec->next_ts = 0;
gst_buffer_replace (&dec->streamheader, NULL);
gst_buffer_replace (&dec->vorbiscomment, NULL);
}
@ -167,17 +165,19 @@ gst_opus_dec_setup_from_peer_caps (GstOpusDec * dec)
{
GstPad *srcpad, *peer;
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);
peer = gst_pad_get_peer (srcpad);
if (peer) {
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);
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);
s = gst_caps_get_structure (caps, 0);
@ -203,8 +203,7 @@ gst_opus_dec_setup_from_peer_caps (GstOpusDec * dec)
}
static GstFlowReturn
opus_dec_chain_parse_data (GstOpusDec * dec, GstBuffer * buf,
GstClockTime timestamp, GstClockTime duration)
opus_dec_chain_parse_data (GstOpusDec * dec, GstBuffer * buf)
{
GstFlowReturn res = GST_FLOW_OK;
gsize size, out_size;
@ -218,6 +217,8 @@ opus_dec_chain_parse_data (GstOpusDec * dec, GstBuffer * buf,
if (dec->state == NULL) {
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);
if (!dec->state || err != OPUS_OK)
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);
GST_DEBUG_OBJECT (dec, "received buffer of size %u", size);
/* copy timestamp */
} else {
/* concealment data, pass NULL as the bits parameters */
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);
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);
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);
res = GST_FLOW_OK;
} else {
res = opus_dec_chain_parse_data (dec, buf, GST_BUFFER_TIMESTAMP (buf),
GST_BUFFER_DURATION (buf));
res = opus_dec_chain_parse_data (dec, buf);
}
} else {
/* 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);
gst_audio_decoder_finish_frame (adec, NULL, 1);
} else {
res = opus_dec_chain_parse_data (dec, buf, GST_BUFFER_TIMESTAMP (buf),
GST_BUFFER_DURATION (buf));
res = opus_dec_chain_parse_data (dec, buf);
}
break;
case 1:
@ -418,14 +401,12 @@ gst_opus_dec_handle_frame (GstAudioDecoder * adec, GstBuffer * buf)
res = gst_opus_dec_parse_comments (dec, buf);
gst_audio_decoder_finish_frame (adec, NULL, 1);
} else {
res = opus_dec_chain_parse_data (dec, buf, GST_BUFFER_TIMESTAMP (buf),
GST_BUFFER_DURATION (buf));
res = opus_dec_chain_parse_data (dec, buf);
}
break;
default:
{
res = opus_dec_chain_parse_data (dec, buf, GST_BUFFER_TIMESTAMP (buf),
GST_BUFFER_DURATION (buf));
res = opus_dec_chain_parse_data (dec, buf);
break;
}
}

View file

@ -47,7 +47,6 @@ struct _GstOpusDec {
OpusDecoder *state;
guint64 packetno;
GstClockTime next_ts;
GstBuffer *streamheader;
GstBuffer *vorbiscomment;

View file

@ -94,9 +94,7 @@ static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE ("sink",
static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src",
GST_PAD_SRC,
GST_PAD_ALWAYS,
GST_STATIC_CAPS ("audio/x-opus, "
"rate = (int) { 8000, 12000, 16000, 24000, 48000 }, "
"channels = (int) [ 1, 2 ], " "frame-size = (int) [ 2, 60 ]")
GST_STATIC_CAPS ("audio/x-opus")
);
#define DEFAULT_AUDIO TRUE
@ -144,6 +142,9 @@ static GstFlowReturn gst_opus_enc_handle_frame (GstAudioEncoder * benc,
GstBuffer * buf);
static GstFlowReturn gst_opus_enc_pre_push (GstAudioEncoder * benc,
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 gint64 gst_opus_enc_get_latency (GstOpusEnc * enc);
@ -157,13 +158,16 @@ static void
gst_opus_enc_class_init (GstOpusEncClass * klass)
{
GObjectClass *gobject_class;
GstElementClass *element_class;
GstElementClass *gstelement_class;
GstAudioEncoderClass *base_class;
gobject_class = (GObjectClass *) klass;
element_class = (GstElementClass *) klass;
gstelement_class = (GstElementClass *) 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_static_pad_template_get (&src_factory));
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->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_param_spec_boolean ("audio", "Audio or voice",
"Audio or voice", DEFAULT_AUDIO,
@ -205,7 +206,7 @@ gst_opus_enc_class_init (GstOpusEncClass * klass)
"Constant bit rate", DEFAULT_CBR,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
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,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
g_object_class_install_property (gobject_class, PROP_COMPLEXITY,
@ -237,12 +238,11 @@ gst_opus_enc_finalize (GObject * object)
enc = GST_OPUS_ENC (object);
GST_DEBUG_OBJECT (enc, "finalize");
G_OBJECT_CLASS (parent_class)->finalize (object);
}
static void
gst_opus_enc_init (GstOpusEnc * enc)
gst_opus_enc_init (GstOpusEnc * enc, GstOpusEncClass * klass)
{
GstAudioEncoder *benc = GST_AUDIO_ENCODER (enc);
@ -273,7 +273,7 @@ gst_opus_enc_start (GstAudioEncoder * benc)
GstOpusEnc *enc = GST_OPUS_ENC (benc);
GST_DEBUG_OBJECT (enc, "start");
enc->tags = gst_tag_list_new_empty ();
enc->tags = gst_tag_list_new ();
enc->header_sent = FALSE;
return TRUE;
}
@ -297,6 +297,15 @@ gst_opus_enc_stop (GstAudioEncoder * benc)
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
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);
enc->state = NULL;
}
if (!gst_opus_enc_setup (enc))
return FALSE;
@ -354,7 +362,6 @@ gst_opus_enc_set_format (GstAudioEncoder * benc, GstAudioInfo * info)
/* feedback to base class */
gst_audio_encoder_set_latency (benc,
gst_opus_enc_get_latency (enc), gst_opus_enc_get_latency (enc));
gst_audio_encoder_set_frame_samples_min (benc,
enc->frame_samples * enc->n_channels * 2);
gst_audio_encoder_set_frame_samples_max (benc,
@ -364,15 +371,6 @@ gst_opus_enc_set_format (GstAudioEncoder * benc, GstAudioInfo * info)
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 *
gst_opus_enc_create_id_buffer (GstOpusEnc * enc)
{
@ -495,8 +493,8 @@ gst_opus_enc_sink_event (GstAudioEncoder * benc, GstEvent * event)
static GstFlowReturn
gst_opus_enc_pre_push (GstAudioEncoder * benc, GstBuffer ** buffer)
{
GstOpusEnc *enc;
GstFlowReturn ret = GST_FLOW_OK;
GstOpusEnc *enc;
enc = GST_OPUS_ENC (benc);
@ -522,6 +520,39 @@ gst_opus_enc_pre_push (GstAudioEncoder * benc, GstBuffer ** buffer)
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
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;
enc = GST_OPUS_ENC (benc);
GST_DEBUG_OBJECT (enc, "handle_frame");
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);
/* mark and put on caps */
caps =
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_from_string ("audio/x-opus");
caps = _gst_caps_set_buffer_array (caps, "streamheader", buf1, buf2, NULL);
/* negotiate with these 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_caps_unref (caps);
/* push out buffers */
/* 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");
enc->headers = g_slist_prepend (enc->headers, buf2);
enc->headers = g_slist_prepend (enc->headers, buf1);
enc->header_sent = TRUE;
}
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);

View file

@ -48,11 +48,7 @@ typedef struct _GstOpusEnc GstOpusEnc;
typedef struct _GstOpusEncClass GstOpusEncClass;
struct _GstOpusEnc {
GstAudioEncoder element;
/* pads */
GstPad *sinkpad;
GstPad *srcpad;
GstAudioEncoder element;
OpusEncoder *state;
@ -74,7 +70,8 @@ struct _GstOpusEnc {
gboolean setup;
gboolean header_sent;
GSList *headers;
GSList *headers;
GstTagList *tags;
};

View file

@ -18,6 +18,7 @@ libgstvp8_la_LIBADD = \
$(GST_PLUGINS_BASE_LIBS) -lgsttag-@GST_MAJORMINOR@ -lgstvideo-@GST_MAJORMINOR@ \
$(GST_BASE_LIBS) $(GST_LIBS) $(VPX_LIBS)
libgstvp8_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS)
libgstvp8_la_LIBTOOLFLAGS = --tag=disable-static
noinst_HEADERS = \
gstvp8dec.h \

View file

@ -681,8 +681,8 @@ gst_mpeg_video_parse_picture_header (GstMpegVideoPictureHdr * hdr,
if (hdr->pic_type == 0 || hdr->pic_type > 4)
goto failed; /* Corrupted picture packet */
/* skype VBV delay */
if (!gst_bit_reader_skip (&br, 8))
/* skip VBV delay */
if (!gst_bit_reader_skip (&br, 16))
goto failed;
if (hdr->pic_type == GST_MPEG_VIDEO_PICTURE_TYPE_P

View file

@ -38,7 +38,7 @@ G_BEGIN_DECLS
/**
* GstMpegVideoPacketTypeCode:
* @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_USER_DATA: User data 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
* @height: Height of each frame
* @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_d: Calculated Framerate denominator
* @bitrate_value: Value of the bitrate as is in the stream (400bps unit)

View file

@ -84,6 +84,8 @@
* 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
* 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>
* </refsect2>
* <refsect2>

View file

@ -183,8 +183,10 @@
#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"); \
GST_DEBUG_OBJECT ((c), "Camerabin now idle"); \
} \
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");
/* check that we have a valid location */
if (camerabin->mode == MODE_VIDEO && camerabin->location == NULL) {
GST_ELEMENT_ERROR (camerabin, RESOURCE, OPEN_WRITE,
(_("File location is set to NULL, please set it to a valid filename")),
(NULL));
return;
if (camerabin->mode == MODE_VIDEO) {
if (camerabin->location == NULL) {
GST_ELEMENT_ERROR (camerabin, RESOURCE, OPEN_WRITE,
(_("File location is set to NULL, please set it to a valid filename")), (NULL));
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);
@ -384,12 +399,6 @@ gst_camera_bin_start_capture (GstCameraBin2 * camerabin)
if (camerabin->audio_src) {
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_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);
if (camerabin->mode == MODE_VIDEO && camerabin->audio_src)
gst_element_set_state (camerabin->audio_src, GST_STATE_PLAYING);
if (camerabin->mode == MODE_VIDEO) {
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
@ -458,12 +472,19 @@ static void
gst_camera_bin_stop_capture (GstCameraBin2 * camerabin)
{
GST_DEBUG_OBJECT (camerabin, "Received stop-capture");
if (camerabin->src)
g_signal_emit_by_name (camerabin->src, "stop-capture", NULL);
if (camerabin->mode == MODE_VIDEO) {
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->audio_drop_eos = FALSE;
gst_element_send_event (camerabin->audio_src, gst_event_new_eos ());
camerabin->video_state = GST_CAMERA_BIN_VIDEO_FINISHING;
if (camerabin->audio_src) {
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;
if (camera->mode == MODE_VIDEO) {
/* a video recording is about to start, we reset the videobin to clear eos/flushing state
* also need to clean the queue ! capsfilter before it */
/* a video recording is about to start, change the filesink location */
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);
GST_DEBUG_OBJECT (camera, "Switching videobin location to %s", location);
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 */
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++;
} 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_mutex_free (camerabin->preview_list_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)
g_signal_handler_disconnect (camerabin->src,
@ -892,6 +900,8 @@ gst_camera_bin_init (GstCameraBin2 * camera)
camera->flags = DEFAULT_FLAGS;
camera->preview_list_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
* 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);
}
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
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:{
GstElement *src = GST_ELEMENT (GST_MESSAGE_SRC (message));
if (src == GST_CAMERA_BIN2_CAST (bin)->videosink) {
g_mutex_lock (camerabin->video_capture_mutex);
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));
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;
@ -1810,6 +1875,7 @@ gst_camera_bin_change_state (GstElement * element, GstStateChange trans)
gst_tag_setter_reset_tags (GST_TAG_SETTER (camera));
GST_CAMERA_BIN2_RESET_PROCESSING_COUNTER (camera);
camera->video_state = GST_CAMERA_BIN_VIDEO_IDLE;
g_mutex_lock (camera->image_capture_mutex);
g_slist_foreach (camera->image_location_list, (GFunc) g_free, NULL);

View file

@ -45,6 +45,14 @@ typedef enum
} 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 _GstCameraBin2Class GstCameraBin2Class;
@ -119,6 +127,10 @@ struct _GstCameraBin2
gboolean audio_drop_eos;
GMutex *video_capture_mutex;
GCond *video_state_cond;
GstCameraBinVideoState video_state;
/* properties */
gint mode;
gchar *location;

View file

@ -45,8 +45,7 @@ static GstStaticPadTemplate sink_template =
GST_STATIC_PAD_TEMPLATE ("sink", GST_PAD_SINK,
GST_PAD_ALWAYS,
GST_STATIC_CAPS ("video/mpeg, "
"mpegversion = (int) 4, "
"parsed = (boolean) false, " "systemstream = (boolean) false")
"mpegversion = (int) 4, " "systemstream = (boolean) false")
);
/* Properties */
@ -634,7 +633,7 @@ plugin_init (GstPlugin * plugin)
GST_DEBUG_CATEGORY_INIT (mpeg4v_parse_debug, "mpeg4videoparse", 0,
"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 ()))
return FALSE;

View file

@ -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
* => This is due to g_type_instance_create using a dogslow rwlock
which take up to 50% of gst_adapter_take_buffer()
=> Bugzilla #585375 (performance and contention problems)
Code structure:
MpegTSBase
+--- MpegTSParse
+--- TSDemux
Known limitations and problems :
--------------------------------
* mpegtspacketizer
* Assumes 188 bytes packets. It should support all modes.
* offset/timestamp of incoming buffers need to be carried on to the
sub-buffers in order for several demuxer features to work correctly.
* mpegtsparser
* 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.

View file

@ -44,6 +44,12 @@
#include "payload_parsers.h"
#include "pesparse.h"
/*
* tsdemux
*
* See TODO for explanations on improvements needed
*/
/* latency in mseconds */
#define TS_LATENCY 700
@ -74,8 +80,6 @@ static GQuark QUARK_PTS;
static GQuark QUARK_DTS;
static GQuark QUARK_OFFSET;
typedef enum
{
PENDING_PACKET_EMPTY = 0, /* No pending packet/buffer

View file

@ -91,6 +91,7 @@ static void gst_h264_parse_get_property (GObject * object, guint prop_id,
GValue * value, GParamSpec * pspec);
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 void
@ -138,6 +139,7 @@ gst_h264_parse_class_init (GstH264ParseClass * klass)
parse_class->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->get_sink_caps = GST_DEBUG_FUNCPTR (gst_h264_parse_get_caps);
}
static void
@ -337,7 +339,7 @@ gst_h264_parse_wrap_nal (GstH264Parse * h264parse, guint format, guint8 * data,
guint size)
{
GstBuffer *buf;
const guint nl = h264parse->nal_length_size;
guint nl = h264parse->nal_length_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) {
GST_WRITE_UINT32_BE (GST_BUFFER_DATA (buf), size << (32 - 8 * nl));
} 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);
}
@ -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
gst_h264_parse_chain (GstPad * pad, GstBuffer * buffer)
{

View file

@ -11,4 +11,4 @@ libgstacmmp3dec_la_LIBADD = \
-lgsttag-$(GST_MAJORMINOR) \
-lmsacm32
libgstacmmp3dec_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) $(DIRECTSOUND_LDFLAGS)
libgstacmmp3dec_la_LIBTOOLFLAGS = --tag=disable-static

View file

@ -23,4 +23,4 @@ libgstlinsys_la_CFLAGS = \
libgstlinsys_la_LDFLAGS = \
$(GST_PLUGIN_LDFLAGS)
libgstlinsys_la_LIBADD = $(GST_BASE_LIBS) $(GST_LIBS)
libgstlinsys_la_LIBTOOLFLAGS = --tag=disable-static

View file

@ -622,6 +622,22 @@ wait_for_element_message (GstElement * camera, const gchar * name,
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)
{
gboolean idle;
@ -652,8 +668,7 @@ GST_START_TEST (test_single_image_capture)
/* check that we got a preview image */
check_preview_image (camera, image_filename, 0);
g_object_get (camera, "idle", &idle, NULL);
fail_unless (idle);
wait_for_idle_state ();
gst_element_set_state (GST_ELEMENT (camera), GST_STATE_NULL);
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);
}
g_object_get (camera, "idle", &idle, NULL);
fail_unless (idle);
wait_for_idle_state ();
gst_element_set_state (GST_ELEMENT (camera), GST_STATE_NULL);
for (i = 0; i < 3; 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);
gst_message_unref (msg);
g_object_get (camera, "idle", &idle, NULL);
fail_unless (idle);
wait_for_idle_state ();
gst_element_set_state (GST_ELEMENT (camera), GST_STATE_NULL);
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);
g_object_get (camera, "idle", &idle, NULL);
fail_unless (idle);
wait_for_idle_state ();
}
gst_element_set_state (GST_ELEMENT (camera), GST_STATE_NULL);
@ -834,7 +846,6 @@ GST_END_TEST;
GST_START_TEST (test_image_video_cycle)
{
gboolean idle;
gint i;
if (!camera)
@ -854,8 +865,7 @@ GST_START_TEST (test_image_video_cycle)
const gchar *img_filename;
const gchar *vid_filename;
g_object_get (camera, "idle", &idle, NULL);
fail_unless (idle);
wait_for_idle_state ();
/* take a picture */
img_filename = make_const_file_name (image_filename, i);
@ -885,10 +895,9 @@ GST_START_TEST (test_image_video_cycle)
gst_message_unref (msg);
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);
/* validate all the files */
@ -1193,8 +1202,7 @@ GST_START_TEST (test_idle_property)
check_preview_image (camera, video_filename, 0);
g_object_get (camera, "idle", &idle, NULL);
fail_unless (idle);
wait_for_idle_state ();
gst_element_set_state (GST_ELEMENT (camera), GST_STATE_NULL);