diff --git a/ext/cairo/gsttextoverlay.c b/ext/cairo/gsttextoverlay.c index c210191ee3..3f4fe3a9bd 100644 --- a/ext/cairo/gsttextoverlay.c +++ b/ext/cairo/gsttextoverlay.c @@ -65,12 +65,14 @@ enum ARG_YPAD, ARG_DELTAX, ARG_DELTAY, + ARG_SILENT, ARG_FONT_DESC }; #define DEFAULT_YPAD 25 #define DEFAULT_XPAD 25 #define DEFAULT_FONT "sans" +#define DEFAULT_SILENT FALSE #define GST_CAIRO_TEXT_OVERLAY_DEFAULT_SCALE 20.0 @@ -201,6 +203,11 @@ gst_text_overlay_class_init (GstCairoTextOverlayClass * klass) "See documentation of " "pango_font_description_from_string" " for syntax.", "", G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS)); + /* FIXME 0.11: rename to "visible" or "text-visible" or "render-text" */ + g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_SILENT, + g_param_spec_boolean ("silent", "silent", + "Whether to render the text string", + DEFAULT_SILENT, G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS)); } static void @@ -267,6 +274,8 @@ gst_text_overlay_init (GstCairoTextOverlay * overlay, overlay->font = g_strdup (DEFAULT_FONT); gst_text_overlay_font_init (overlay); + overlay->silent = DEFAULT_SILENT; + overlay->fps_n = 0; overlay->fps_d = 1; @@ -410,6 +419,9 @@ gst_text_overlay_set_property (GObject * object, guint prop_id, gst_text_overlay_font_init (overlay); break; } + case ARG_SILENT: + overlay->silent = g_value_get_boolean (value); + break; default:{ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -431,6 +443,11 @@ gst_text_overlay_render_text (GstCairoTextOverlay * overlay, gchar *string; double x, y; + if (overlay->silent) { + GST_DEBUG_OBJECT (overlay, "Silent mode, not rendering"); + return; + } + if (textlen < 0) textlen = strlen (text); diff --git a/ext/cairo/gsttextoverlay.h b/ext/cairo/gsttextoverlay.h index 09cfd724ee..dbb21546ff 100644 --- a/ext/cairo/gsttextoverlay.h +++ b/ext/cairo/gsttextoverlay.h @@ -76,6 +76,7 @@ struct _GstCairoTextOverlay { gint slant; gint weight; gdouble scale; + gboolean silent; }; struct _GstCairoTextOverlayClass { diff --git a/gst/audioparsers/gstflacparse.c b/gst/audioparsers/gstflacparse.c index 953dbaf2c3..412d9fff41 100644 --- a/gst/audioparsers/gstflacparse.c +++ b/gst/audioparsers/gstflacparse.c @@ -527,6 +527,34 @@ gst_flac_parse_frame_header_is_valid (GstFlacParse * flacparse, if (actual_crc != expected_crc) goto error; + /* + The FLAC format documentation says: + The "blocking strategy" bit determines how to calculate the sample number + of the first sample in the frame. If the bit is 0 (fixed-blocksize), the + frame header encodes the frame number as above, and the frame's starting + sample number will be the frame number times the blocksize. If it is 1 + (variable-blocksize), the frame header encodes the frame's starting + sample number itself. (In the case of a fixed-blocksize stream, only the + last block may be shorter than the stream blocksize; its starting sample + number will be calculated as the frame number times the previous frame's + blocksize, or zero if it is the first frame). + + Therefore, when in fixed block size mode, we only update the block size + the first time, then reuse that block size for subsequent calls. + This will also fix a timestamp problem with the last block's timestamp + being miscalculated by scaling the block number by a "wrong" block size. + */ + if (blocking_strategy == 0) { + if (flacparse->block_size != 0) { + /* after first block */ + if (flacparse->block_size != block_size) { + /* TODO: can we know we're on the last frame, to avoid warning ? */ + GST_WARNING_OBJECT (flacparse, "Block size is not constant"); + block_size = flacparse->block_size; + } + } + } + if (set) { flacparse->block_size = block_size; if (!flacparse->samplerate) @@ -706,7 +734,6 @@ gst_flac_parse_check_valid_frame (GstBaseParse * parse, flacparse->offset = GST_BUFFER_OFFSET (buffer); flacparse->blocking_strategy = 0; - flacparse->block_size = 0; flacparse->sample_number = 0; GST_DEBUG_OBJECT (flacparse, "Found sync code"); @@ -1404,7 +1431,6 @@ gst_flac_parse_parse_frame (GstBaseParse * parse, GstBaseParseFrame * frame) flacparse->offset = -1; flacparse->blocking_strategy = 0; - flacparse->block_size = 0; flacparse->sample_number = 0; res = GST_FLOW_OK; } diff --git a/gst/flv/Makefile.am b/gst/flv/Makefile.am index 93e1de1cea..e8059cb4d7 100644 --- a/gst/flv/Makefile.am +++ b/gst/flv/Makefile.am @@ -7,7 +7,7 @@ libgstflv_la_LDFLAGS = ${GST_PLUGIN_LDFLAGS} libgstflv_la_SOURCES = gstflvdemux.c gstflvmux.c libgstflv_la_LIBTOOLFLAGS = --tag=disable-static -noinst_HEADERS = gstflvdemux.h gstflvmux.h +noinst_HEADERS = gstflvdemux.h gstflvmux.h amfdefs.h Android.mk: Makefile.am $(BUILT_SOURCES) androgenizer \ diff --git a/gst/flv/gstflvdemux.c b/gst/flv/gstflvdemux.c index 394a6aba55..9d49eb222a 100644 --- a/gst/flv/gstflvdemux.c +++ b/gst/flv/gstflvdemux.c @@ -85,6 +85,9 @@ G_DEFINE_TYPE (GstFlvDemux, gst_flv_demux, GST_TYPE_ELEMENT); /* 1 byte of tag type + 3 bytes of tag data size */ #define FLV_TAG_TYPE_SIZE 4 +/* two seconds - consider pts are resynced to another base if this different */ +#define RESYNC_THRESHOLD 2000 + static gboolean flv_demux_handle_seek_push (GstFlvDemux * demux, GstEvent * event); static gboolean gst_flv_demux_handle_seek_pull (GstFlvDemux * demux, @@ -775,6 +778,23 @@ gst_flv_demux_push_tags (GstFlvDemux * demux) } } +static void +gst_flv_demux_update_resync (GstFlvDemux * demux, guint32 pts, guint32 * last, + GstClockTime * offset) +{ + if (ABS (pts - *last) >= RESYNC_THRESHOLD) { + /* Theoretically, we should use substract the duration of the last buffer, + but this demuxer sends no durations on buffers, not sure if it cannot + know, or just does not care to calculate. */ + gint32 dpts = pts - *last; + *offset -= dpts * GST_MSECOND; + GST_WARNING_OBJECT (demux, + "Large pts gap (%" G_GINT32_FORMAT " ms), assuming resync, offset now %" + GST_TIME_FORMAT "", dpts, GST_TIME_ARGS (*offset)); + } + *last = pts; +} + static GstFlowReturn gst_flv_demux_parse_tag_audio (GstFlvDemux * demux, GstBuffer * buffer) { @@ -969,8 +989,12 @@ gst_flv_demux_parse_tag_audio (GstFlvDemux * demux, GstBuffer * buffer) } } + /* detect (and deem to be resyncs) large pts gaps */ + gst_flv_demux_update_resync (demux, pts, &demux->last_audio_pts, + &demux->audio_time_offset); + /* Fill buffer with data */ - GST_BUFFER_TIMESTAMP (outbuf) = pts * GST_MSECOND; + GST_BUFFER_TIMESTAMP (outbuf) = pts * GST_MSECOND + demux->audio_time_offset; GST_BUFFER_DURATION (outbuf) = GST_CLOCK_TIME_NONE; GST_BUFFER_OFFSET (outbuf) = demux->audio_offset++; GST_BUFFER_OFFSET_END (outbuf) = demux->audio_offset; @@ -1341,8 +1365,12 @@ gst_flv_demux_parse_tag_video (GstFlvDemux * demux, GstBuffer * buffer) } } + /* detect (and deem to be resyncs) large pts gaps */ + gst_flv_demux_update_resync (demux, pts, &demux->last_video_pts, + &demux->video_time_offset); + /* Fill buffer with data */ - GST_BUFFER_TIMESTAMP (outbuf) = pts * GST_MSECOND; + GST_BUFFER_TIMESTAMP (outbuf) = pts * GST_MSECOND + demux->video_time_offset; GST_BUFFER_DURATION (outbuf) = GST_CLOCK_TIME_NONE; GST_BUFFER_OFFSET (outbuf) = demux->video_offset++; GST_BUFFER_OFFSET_END (outbuf) = demux->video_offset; @@ -1659,6 +1687,8 @@ gst_flv_demux_cleanup (GstFlvDemux * demux) demux->index_max_time = 0; demux->audio_start = demux->video_start = GST_CLOCK_TIME_NONE; + demux->last_audio_pts = demux->last_video_pts = 0; + demux->audio_time_offset = demux->video_time_offset = 0; demux->no_more_pads = FALSE; diff --git a/gst/flv/gstflvdemux.h b/gst/flv/gstflvdemux.h index 3b50f96038..efbed316cf 100644 --- a/gst/flv/gstflvdemux.h +++ b/gst/flv/gstflvdemux.h @@ -94,6 +94,8 @@ struct _GstFlvDemux gboolean audio_linked; GstBuffer * audio_codec_data; GstClockTime audio_start; + guint32 last_audio_pts; + GstClockTime audio_time_offset; /* Video infos */ guint32 w; @@ -108,6 +110,8 @@ struct _GstFlvDemux gboolean got_par; GstBuffer * video_codec_data; GstClockTime video_start; + guint32 last_video_pts; + GstClockTime video_time_offset; gdouble framerate; gboolean random_access; diff --git a/gst/isomp4/qtdemux.c b/gst/isomp4/qtdemux.c index ff48a5e454..784648e00c 100644 --- a/gst/isomp4/qtdemux.c +++ b/gst/isomp4/qtdemux.c @@ -8831,8 +8831,16 @@ qtdemux_parse_tree (GstQTDemux * qtdemux) /* Moving qt creation time (secs since 1904) to unix time */ if (creation_time != 0) { if (creation_time > QTDEMUX_SECONDS_FROM_1904_TO_1970) { + GTimeVal now; + creation_time -= QTDEMUX_SECONDS_FROM_1904_TO_1970; - datetime = gst_date_time_new_from_unix_epoch_local_time (creation_time); + /* some data cleansing sanity */ + g_get_current_time (&now); + if (now.tv_sec + 24 * 3600 < creation_time) { + GST_DEBUG_OBJECT (qtdemux, "discarding bogus future creation time"); + } else { + datetime = gst_date_time_new_from_unix_epoch_local_time (creation_time); + } } else { GST_WARNING_OBJECT (qtdemux, "Can't handle datetimes before 1970 yet, " "please file a bug at http://bugzilla.gnome.org"); @@ -8868,8 +8876,13 @@ qtdemux_parse_tree (GstQTDemux * qtdemux) /* set duration in the segment info */ gst_qtdemux_get_duration (qtdemux, &duration); - if (duration) + if (duration) { qtdemux->segment.duration = duration; + /* also do not exceed duration; stop is set that way post seek anyway, + * and segment activation falls back to duration, + * whereas loop only checks stop, so let's align this here as well */ + qtdemux->segment.stop = duration; + } /* parse all traks */ trak = qtdemux_tree_get_child_by_type (qtdemux->moov_node, FOURCC_trak); diff --git a/gst/matroska/ebml-write.c b/gst/matroska/ebml-write.c index 9bc88eafd3..06f3568779 100644 --- a/gst/matroska/ebml-write.c +++ b/gst/matroska/ebml-write.c @@ -411,10 +411,12 @@ gst_ebml_write_element_push (GstEbmlWrite * ebml, GstBuffer * buf, if (!buf_data) buf_data = GST_BUFFER_DATA (buf); - if (buf_data_end) + if (buf_data_end) { data_size = buf_data_end - buf_data; - else + GST_BUFFER_SIZE (buf) = data_size; + } else { data_size = GST_BUFFER_SIZE (buf); + } ebml->pos += data_size; diff --git a/gst/rtp/gstrtph263ppay.c b/gst/rtp/gstrtph263ppay.c index df77fae70f..21e88ae4fa 100644 --- a/gst/rtp/gstrtph263ppay.c +++ b/gst/rtp/gstrtph263ppay.c @@ -22,6 +22,8 @@ #endif #include +#include +#include #include @@ -61,9 +63,23 @@ static GstStaticPadTemplate gst_rtp_h263p_pay_sink_template = GST_STATIC_PAD_TEMPLATE ("sink", GST_PAD_SINK, GST_PAD_ALWAYS, - GST_STATIC_CAPS ("video/x-h263, " "variant = (string) \"itu\" ") + GST_STATIC_CAPS ("video/x-h263, " "variant = (string) \"itu\"") ); +/* + * We also return these in getcaps() as required by the SDP caps + * + * width = (int) [16, 4096] + * height = (int) [16, 4096] + * "annex-f = (boolean) {true, false}," + * "annex-i = (boolean) {true, false}," + * "annex-j = (boolean) {true, false}," + * "annex-l = (boolean) {true, false}," + * "annex-t = (boolean) {true, false}," + * "annex-v = (boolean) {true, false}") + */ + + static GstStaticPadTemplate gst_rtp_h263p_pay_src_template = GST_STATIC_PAD_TEMPLATE ("src", GST_PAD_SRC, @@ -87,6 +103,8 @@ static void gst_rtp_h263p_pay_get_property (GObject * object, guint prop_id, static gboolean gst_rtp_h263p_pay_setcaps (GstBaseRTPPayload * payload, GstCaps * caps); +static GstCaps *gst_rtp_h263p_pay_sink_getcaps (GstBaseRTPPayload * payload, + GstPad * pad); static GstFlowReturn gst_rtp_h263p_pay_handle_buffer (GstBaseRTPPayload * payload, GstBuffer * buffer); @@ -109,6 +127,7 @@ gst_rtp_h263p_pay_class_init (GstRtpH263PPayClass * klass) gobject_class->get_property = gst_rtp_h263p_pay_get_property; gstbasertppayload_class->set_caps = gst_rtp_h263p_pay_setcaps; + gstbasertppayload_class->get_caps = gst_rtp_h263p_pay_sink_getcaps; gstbasertppayload_class->handle_buffer = gst_rtp_h263p_pay_handle_buffer; g_object_class_install_property (G_OBJECT_CLASS (klass), @@ -187,6 +206,380 @@ gst_rtp_h263p_pay_setcaps (GstBaseRTPPayload * payload, GstCaps * caps) return res; } +static void +caps_append (GstCaps * caps, GstStructure * in_s, guint x, guint y, guint mpi) +{ + GstStructure *s; + + if (!in_s) + return; + + if (mpi < 1 || mpi > 32) + return; + + s = gst_structure_copy (in_s); + + gst_structure_set (s, + "width", GST_TYPE_INT_RANGE, 1, x, + "height", GST_TYPE_INT_RANGE, 1, y, + "framerate", GST_TYPE_FRACTION_RANGE, 0, 1, 30000, 1001 * mpi, NULL); + + gst_caps_merge_structure (caps, s); +} + + +static GstCaps * +gst_rtp_h263p_pay_sink_getcaps (GstBaseRTPPayload * payload, GstPad * pad) +{ + GstRtpH263PPay *rtph263ppay; + GstCaps *caps = gst_caps_new_empty (); + GstCaps *peercaps = NULL; + GstCaps *intersect = NULL; + guint i; + + rtph263ppay = GST_RTP_H263P_PAY (payload); + + peercaps = gst_pad_peer_get_caps (GST_BASE_RTP_PAYLOAD_SRCPAD (payload)); + if (!peercaps) + return + gst_caps_copy (gst_pad_get_pad_template_caps + (GST_BASE_RTP_PAYLOAD_SINKPAD (payload))); + + intersect = gst_caps_intersect (peercaps, + gst_pad_get_pad_template_caps (GST_BASE_RTP_PAYLOAD_SRCPAD (payload))); + gst_caps_unref (peercaps); + + if (gst_caps_is_empty (intersect)) + return intersect; + + for (i = 0; i < gst_caps_get_size (intersect); i++) { + GstStructure *s = gst_caps_get_structure (intersect, i); + const gchar *encoding_name = gst_structure_get_string (s, "encoding-name"); + + if (!strcmp (encoding_name, "H263-2000")) { + const gchar *profile_str = gst_structure_get_string (s, "profile"); + const gchar *level_str = gst_structure_get_string (s, "level"); + int profile = 0; + int level = 0; + + if (profile_str && level_str) { + gboolean i = FALSE, j = FALSE, l = FALSE, t = FALSE, f = FALSE, + v = FALSE; + GstStructure *new_s = gst_structure_new ("video/x-h263", + "variant", G_TYPE_STRING, "itu", + NULL); + + profile = atoi (profile_str); + level = atoi (level_str); + + /* These profiles are defined in the H.263 Annex X */ + switch (profile) { + case 0: + /* The Baseline Profile (Profile 0) */ + break; + case 1: + /* H.320 Coding Efficiency Version 2 Backward-Compatibility Profile + * (Profile 1) + * Baseline + Annexes I, J, L.4 and T + */ + i = j = l = t = TRUE; + break; + case 2: + /* Version 1 Backward-Compatibility Profile (Profile 2) + * Baseline + Annex F + */ + i = j = l = t = f = TRUE; + break; + case 3: + /* Version 2 Interactive and Streaming Wireless Profile + * Baseline + Annexes I, J, T + */ + i = j = t = TRUE; + break; + case 4: + /* Version 3 Interactive and Streaming Wireless Profile (Profile 4) + * Baseline + Annexes I, J, T, V, W.6.3.8, + */ + /* Missing W.6.3.8 */ + i = j = t = v = TRUE; + break; + case 5: + /* Conversational High Compression Profile (Profile 5) + * Baseline + Annexes F, I, J, L.4, T, D, U + */ + /* Missing D, U */ + f = i = j = l = t = TRUE; + break; + case 6: + /* Conversational Internet Profile (Profile 6) + * Baseline + Annexes F, I, J, L.4, T, D, U and + * K with arbitratry slice ordering + */ + /* Missing D, U, K with arbitratry slice ordering */ + f = i = j = l = t = TRUE; + break; + case 7: + /* Conversational Interlace Profile (Profile 7) + * Baseline + Annexes F, I, J, L.4, T, D, U, W.6.3.11 + */ + /* Missing D, U, W.6.3.11 */ + f = i = j = l = t = TRUE; + break; + case 8: + /* High Latency Profile (Profile 8) + * Baseline + Annexes F, I, J, L.4, T, D, U, P.5, O.1.1 and + * K with arbitratry slice ordering + */ + /* Missing D, U, P.5, O.1.1 */ + f = i = j = l = t = TRUE; + break; + } + + + if (f || i || j || t || l || v) { + GValue list = { 0 }; + GValue vstr = { 0 }; + + g_value_init (&list, GST_TYPE_LIST); + g_value_init (&vstr, G_TYPE_STRING); + + g_value_set_static_string (&vstr, "h263"); + gst_value_list_append_value (&list, &vstr); + g_value_set_static_string (&vstr, "h263p"); + gst_value_list_append_value (&list, &vstr); + + if (l || v) { + g_value_set_static_string (&vstr, "h263pp"); + gst_value_list_append_value (&list, &vstr); + } + g_value_unset (&vstr); + + gst_structure_set_value (new_s, "h263version", &list); + g_value_unset (&list); + } else { + gst_structure_set (new_s, "h263version", G_TYPE_STRING, "h263", NULL); + } + + + if (!f) + gst_structure_set (new_s, "annex-f", G_TYPE_BOOLEAN, FALSE, NULL); + if (!i) + gst_structure_set (new_s, "annex-i", G_TYPE_BOOLEAN, FALSE, NULL); + if (!j) + gst_structure_set (new_s, "annex-j", G_TYPE_BOOLEAN, FALSE, NULL); + if (!t) + gst_structure_set (new_s, "annex-t", G_TYPE_BOOLEAN, FALSE, NULL); + if (!l) + gst_structure_set (new_s, "annex-l", G_TYPE_BOOLEAN, FALSE, NULL); + if (!v) + gst_structure_set (new_s, "annex-v", G_TYPE_BOOLEAN, FALSE, NULL); + + + if (level <= 10 || level == 45) { + gst_structure_set (new_s, + "width", GST_TYPE_INT_RANGE, 1, 176, + "height", GST_TYPE_INT_RANGE, 1, 144, + "framerate", GST_TYPE_FRACTION_RANGE, 0, 1, 30000, 2002, NULL); + gst_caps_merge_structure (caps, new_s); + } else if (level <= 20) { + GstStructure *s_copy = gst_structure_copy (new_s); + + gst_structure_set (new_s, + "width", GST_TYPE_INT_RANGE, 1, 352, + "height", GST_TYPE_INT_RANGE, 1, 288, + "framerate", GST_TYPE_FRACTION_RANGE, 0, 1, 30000, 2002, NULL); + gst_caps_merge_structure (caps, new_s); + + gst_structure_set (s_copy, + "width", GST_TYPE_INT_RANGE, 1, 176, + "height", GST_TYPE_INT_RANGE, 1, 144, + "framerate", GST_TYPE_FRACTION_RANGE, 0, 1, 30000, 1001, NULL); + gst_caps_merge_structure (caps, s_copy); + } else if (level <= 40) { + + gst_structure_set (new_s, + "width", GST_TYPE_INT_RANGE, 1, 352, + "height", GST_TYPE_INT_RANGE, 1, 288, + "framerate", GST_TYPE_FRACTION_RANGE, 0, 1, 30000, 1001, NULL); + gst_caps_merge_structure (caps, new_s); + } else if (level <= 50) { + GstStructure *s_copy = gst_structure_copy (new_s); + + gst_structure_set (new_s, + "width", GST_TYPE_INT_RANGE, 1, 352, + "height", GST_TYPE_INT_RANGE, 1, 288, + "framerate", GST_TYPE_FRACTION_RANGE, 0, 1, 50, 1, NULL); + gst_caps_merge_structure (caps, new_s); + + gst_structure_set (s_copy, + "width", GST_TYPE_INT_RANGE, 1, 352, + "height", GST_TYPE_INT_RANGE, 1, 240, + "framerate", GST_TYPE_FRACTION_RANGE, 0, 1, 60000, 1001, NULL); + gst_caps_merge_structure (caps, s_copy); + } else if (level <= 60) { + GstStructure *s_copy = gst_structure_copy (new_s); + + gst_structure_set (new_s, + "width", GST_TYPE_INT_RANGE, 1, 720, + "height", GST_TYPE_INT_RANGE, 1, 288, + "framerate", GST_TYPE_FRACTION_RANGE, 0, 1, 50, 1, NULL); + gst_caps_merge_structure (caps, new_s); + + gst_structure_set (s_copy, + "width", GST_TYPE_INT_RANGE, 1, 720, + "height", GST_TYPE_INT_RANGE, 1, 240, + "framerate", GST_TYPE_FRACTION_RANGE, 0, 1, 60000, 1001, NULL); + gst_caps_merge_structure (caps, s_copy); + } else if (level <= 70) { + GstStructure *s_copy = gst_structure_copy (new_s); + + gst_structure_set (new_s, + "width", GST_TYPE_INT_RANGE, 1, 720, + "height", GST_TYPE_INT_RANGE, 1, 576, + "framerate", GST_TYPE_FRACTION_RANGE, 0, 1, 50, 1, NULL); + gst_caps_merge_structure (caps, new_s); + + gst_structure_set (s_copy, + "width", GST_TYPE_INT_RANGE, 1, 720, + "height", GST_TYPE_INT_RANGE, 1, 480, + "framerate", GST_TYPE_FRACTION_RANGE, 0, 1, 60000, 1001, NULL); + gst_caps_merge_structure (caps, s_copy); + } else { + gst_caps_merge_structure (caps, new_s); + } + + } else { + GstStructure *new_s = gst_structure_new ("video/x-h263", + "variant", G_TYPE_STRING, "itu", + "h263version", G_TYPE_STRING, "h263", + NULL); + + GST_DEBUG_OBJECT (rtph263ppay, "No profile or level specified" + " for H263-2000, defaulting to baseline H263"); + + gst_caps_merge_structure (caps, new_s); + } + } else { + gboolean f = FALSE, i = FALSE, j = FALSE, t = FALSE; + /* FIXME: ffmpeg support the Appendix K too, how do we express it ? + * guint k; + */ + const gchar *str; + GstStructure *new_s = gst_structure_new ("video/x-h263", + "variant", G_TYPE_STRING, "itu", + NULL); + gboolean added = FALSE; + + str = gst_structure_get_string (s, "f"); + if (str && !strcmp (str, "1")) + f = TRUE; + + str = gst_structure_get_string (s, "i"); + if (str && !strcmp (str, "1")) + i = TRUE; + + str = gst_structure_get_string (s, "j"); + if (str && !strcmp (str, "1")) + j = TRUE; + + str = gst_structure_get_string (s, "t"); + if (str && !strcmp (str, "1")) + t = TRUE; + + if (f || i || j || t) { + GValue list = { 0 }; + GValue vstr = { 0 }; + + g_value_init (&list, GST_TYPE_LIST); + g_value_init (&vstr, G_TYPE_STRING); + + g_value_set_static_string (&vstr, "h263"); + gst_value_list_append_value (&list, &vstr); + g_value_set_static_string (&vstr, "h263p"); + gst_value_list_append_value (&list, &vstr); + g_value_unset (&vstr); + + gst_structure_set_value (new_s, "h263version", &list); + g_value_unset (&list); + } else { + gst_structure_set (new_s, "h263version", G_TYPE_STRING, "h263", NULL); + } + + if (!f) + gst_structure_set (new_s, "annex-f", G_TYPE_BOOLEAN, FALSE, NULL); + if (!i) + gst_structure_set (new_s, "annex-i", G_TYPE_BOOLEAN, FALSE, NULL); + if (!j) + gst_structure_set (new_s, "annex-j", G_TYPE_BOOLEAN, FALSE, NULL); + if (!t) + gst_structure_set (new_s, "annex-t", G_TYPE_BOOLEAN, FALSE, NULL); + + + str = gst_structure_get_string (s, "custom"); + if (str) { + unsigned int xmax, ymax, mpi; + if (sscanf (str, "%u,%u,%u", &xmax, &ymax, &mpi) == 3) { + if (xmax % 4 && ymax % 4 && mpi >= 1 && mpi <= 32) { + caps_append (caps, new_s, xmax, ymax, mpi); + added = TRUE; + } else { + GST_WARNING_OBJECT (rtph263ppay, "Invalid custom framesize/MPI" + " %u x %u at %u, ignoring", xmax, ymax, mpi); + } + } else { + GST_WARNING_OBJECT (rtph263ppay, "Invalid custom framesize/MPI: %s," + " ignoring", str); + } + } + + str = gst_structure_get_string (s, "16cif"); + if (str) { + int mpi = atoi (str); + caps_append (caps, new_s, 1408, 1152, mpi); + added = TRUE; + } + + str = gst_structure_get_string (s, "4cif"); + if (str) { + int mpi = atoi (str); + caps_append (caps, new_s, 704, 576, mpi); + added = TRUE; + } + + str = gst_structure_get_string (s, "cif"); + if (str) { + int mpi = atoi (str); + caps_append (caps, new_s, 352, 288, mpi); + added = TRUE; + } + + str = gst_structure_get_string (s, "qcif"); + if (str) { + int mpi = atoi (str); + caps_append (caps, new_s, 176, 144, mpi); + added = TRUE; + } + + str = gst_structure_get_string (s, "sqcif"); + if (str) { + int mpi = atoi (str); + caps_append (caps, new_s, 128, 96, mpi); + added = TRUE; + } + + if (added) + gst_structure_free (new_s); + else + gst_caps_merge_structure (caps, new_s); + } + } + + gst_caps_unref (intersect); + + return caps; +} + + static void gst_rtp_h263p_pay_set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec)