h264parse: avc input must either pass-through or be split into bytestream chunks
[gstreamer-omap:gst-plugins-bad.git] / gst / videoparsers / gsth264parse.c
1 /* GStreamer H.264 Parser
2  * Copyright (C) <2010> Mark Nauwelaerts <mark.nauwelaerts@collabora.co.uk>
3  * Copyright (C) <2010> Collabora Multimedia
4  * Copyright (C) <2010> Nokia Corporation
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Library General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Library General Public License for more details.
15  *
16  * You should have received a copy of the GNU Library General Public
17  * License along with this library; if not, write to the
18  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19  * Boston, MA 02111-1307, USA.
20  */
21
22 #ifdef HAVE_CONFIG_H
23 #  include "config.h"
24 #endif
25
26 #include <gst/base/gstbytereader.h>
27 #include <gst/base/gstbytewriter.h>
28 #include <gst/base/gstadapter.h>
29 #include "gsth264parse.h"
30
31 #include <string.h>
32
33 GST_DEBUG_CATEGORY (h264_parse_debug);
34 #define GST_CAT_DEFAULT h264_parse_debug
35
36 #define DEFAULT_CONFIG_INTERVAL      (0)
37
38 enum
39 {
40   PROP_0,
41   PROP_CONFIG_INTERVAL,
42   PROP_LAST
43 };
44
45 enum
46 {
47   GST_H264_PARSE_FORMAT_NONE,
48   GST_H264_PARSE_FORMAT_AVC,
49   GST_H264_PARSE_FORMAT_BYTE
50 };
51
52 enum
53 {
54   GST_H264_PARSE_ALIGN_NONE = 0,
55   GST_H264_PARSE_ALIGN_NAL,
56   GST_H264_PARSE_ALIGN_AU
57 };
58
59 static GstStaticPadTemplate sinktemplate = GST_STATIC_PAD_TEMPLATE ("sink",
60     GST_PAD_SINK,
61     GST_PAD_ALWAYS,
62     GST_STATIC_CAPS ("video/x-h264, parsed = (boolean) false"));
63
64 static GstStaticPadTemplate srctemplate = GST_STATIC_PAD_TEMPLATE ("src",
65     GST_PAD_SRC,
66     GST_PAD_ALWAYS,
67     GST_STATIC_CAPS ("video/x-h264, parsed = (boolean) true"));
68
69 GST_BOILERPLATE (GstH264Parse, gst_h264_parse, GstBaseParse,
70     GST_TYPE_BASE_PARSE);
71
72 static void gst_h264_parse_finalize (GObject * object);
73
74 static gboolean gst_h264_parse_start (GstBaseParse * parse);
75 static gboolean gst_h264_parse_stop (GstBaseParse * parse);
76 static gboolean gst_h264_parse_check_valid_frame (GstBaseParse * parse,
77     GstBaseParseFrame * frame, guint * framesize, gint * skipsize);
78 static GstFlowReturn gst_h264_parse_parse_frame (GstBaseParse * parse,
79     GstBaseParseFrame * frame);
80 static GstFlowReturn gst_h264_parse_pre_push_frame (GstBaseParse * parse,
81     GstBaseParseFrame * frame);
82
83 static void gst_h264_parse_set_property (GObject * object, guint prop_id,
84     const GValue * value, GParamSpec * pspec);
85 static void gst_h264_parse_get_property (GObject * object, guint prop_id,
86     GValue * value, GParamSpec * pspec);
87
88 static gboolean gst_h264_parse_set_caps (GstBaseParse * parse, GstCaps * caps);
89 static GstFlowReturn gst_h264_parse_chain (GstPad * pad, GstBuffer * buffer);
90
91 static void
92 gst_h264_parse_base_init (gpointer g_class)
93 {
94   GstElementClass *gstelement_class = GST_ELEMENT_CLASS (g_class);
95
96   gst_element_class_add_pad_template (gstelement_class,
97       gst_static_pad_template_get (&srctemplate));
98   gst_element_class_add_pad_template (gstelement_class,
99       gst_static_pad_template_get (&sinktemplate));
100
101   gst_element_class_set_details_simple (gstelement_class, "H.264 parser",
102       "Codec/Parser/Video",
103       "Parses H.264 streams",
104       "Mark Nauwelaerts <mark.nauwelaerts@collabora.co.uk>");
105
106   GST_DEBUG_CATEGORY_INIT (h264_parse_debug, "h264parse", 0, "h264 parser");
107 }
108
109 static void
110 gst_h264_parse_class_init (GstH264ParseClass * klass)
111 {
112   GObjectClass *gobject_class = (GObjectClass *) klass;
113   GstBaseParseClass *parse_class = GST_BASE_PARSE_CLASS (klass);
114
115   gobject_class->finalize = gst_h264_parse_finalize;
116   gobject_class->set_property = gst_h264_parse_set_property;
117   gobject_class->get_property = gst_h264_parse_get_property;
118
119   g_object_class_install_property (gobject_class, PROP_CONFIG_INTERVAL,
120       g_param_spec_uint ("config-interval",
121           "SPS PPS Send Interval",
122           "Send SPS and PPS Insertion Interval in seconds (sprop parameter sets "
123           "will be multiplexed in the data stream when detected.) (0 = disabled)",
124           0, 3600, DEFAULT_CONFIG_INTERVAL,
125           G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS));
126
127   /* Override BaseParse vfuncs */
128   parse_class->start = GST_DEBUG_FUNCPTR (gst_h264_parse_start);
129   parse_class->stop = GST_DEBUG_FUNCPTR (gst_h264_parse_stop);
130   parse_class->check_valid_frame =
131       GST_DEBUG_FUNCPTR (gst_h264_parse_check_valid_frame);
132   parse_class->parse_frame = GST_DEBUG_FUNCPTR (gst_h264_parse_parse_frame);
133   parse_class->pre_push_frame =
134       GST_DEBUG_FUNCPTR (gst_h264_parse_pre_push_frame);
135   parse_class->set_sink_caps = GST_DEBUG_FUNCPTR (gst_h264_parse_set_caps);
136 }
137
138 static void
139 gst_h264_parse_init (GstH264Parse * h264parse, GstH264ParseClass * g_class)
140 {
141   h264parse->frame_out = gst_adapter_new ();
142
143   /* retrieve and intercept baseparse.
144    * Quite HACKish, but fairly OK since it is needed to perform avc packet
145    * splitting, which is the penultimate de-parsing */
146   h264parse->parse_chain =
147       GST_PAD_CHAINFUNC (GST_BASE_PARSE_SINK_PAD (h264parse));
148   gst_pad_set_chain_function (GST_BASE_PARSE_SINK_PAD (h264parse),
149       gst_h264_parse_chain);
150 }
151
152
153 static void
154 gst_h264_parse_finalize (GObject * object)
155 {
156   GstH264Parse *h264parse = GST_H264_PARSE (object);
157
158   g_object_unref (h264parse->frame_out);
159
160   G_OBJECT_CLASS (parent_class)->finalize (object);
161 }
162
163 static void
164 gst_h264_parse_reset_frame (GstH264Parse * h264parse)
165 {
166   /* done parsing; reset state */
167   h264parse->last_nal_pos = 0;
168   h264parse->next_sc_pos = 0;
169   h264parse->picture_start = FALSE;
170   h264parse->update_caps = FALSE;
171   h264parse->idr_pos = -1;
172   h264parse->keyframe = FALSE;
173   h264parse->frame_start = FALSE;
174   gst_adapter_clear (h264parse->frame_out);
175 }
176
177 static void
178 gst_h264_parse_reset (GstH264Parse * h264parse)
179 {
180   h264parse->width = 0;
181   h264parse->height = 0;
182   h264parse->fps_num = 0;
183   h264parse->fps_den = 0;
184   gst_buffer_replace (&h264parse->codec_data, NULL);
185   h264parse->nal_length_size = 4;
186   h264parse->packetized = FALSE;
187
188   h264parse->align = GST_H264_PARSE_ALIGN_NONE;
189   h264parse->format = GST_H264_PARSE_FORMAT_NONE;
190
191   h264parse->last_report = GST_CLOCK_TIME_NONE;
192   h264parse->push_codec = FALSE;
193
194   gst_h264_parse_reset_frame (h264parse);
195 }
196
197 static gboolean
198 gst_h264_parse_start (GstBaseParse * parse)
199 {
200   GstH264Parse *h264parse = GST_H264_PARSE (parse);
201
202   GST_DEBUG_OBJECT (parse, "start");
203   gst_h264_parse_reset (h264parse);
204
205   gst_h264_params_create (&h264parse->params, GST_ELEMENT (h264parse));
206
207   gst_base_parse_set_min_frame_size (parse, 6);
208
209   return TRUE;
210 }
211
212 static gboolean
213 gst_h264_parse_stop (GstBaseParse * parse)
214 {
215   GstH264Parse *h264parse = GST_H264_PARSE (parse);
216
217   GST_DEBUG_OBJECT (parse, "stop");
218   gst_h264_parse_reset (h264parse);
219
220   gst_h264_params_free (h264parse->params);
221   h264parse->params = NULL;
222
223   return TRUE;
224 }
225
226 static const gchar *
227 gst_h264_parse_get_string (GstH264Parse * parse, gboolean format, gint code)
228 {
229   if (format) {
230     switch (code) {
231       case GST_H264_PARSE_FORMAT_AVC:
232         return "avc";
233       case GST_H264_PARSE_FORMAT_BYTE:
234         return "byte-stream";
235       default:
236         return "none";
237     }
238   } else {
239     switch (code) {
240       case GST_H264_PARSE_ALIGN_NAL:
241         return "nal";
242       case GST_H264_PARSE_ALIGN_AU:
243         return "au";
244       default:
245         return "none";
246     }
247   }
248 }
249
250 static void
251 gst_h264_parse_format_from_caps (GstCaps * caps, guint * format, guint * align)
252 {
253
254   if (format)
255     *format = GST_H264_PARSE_FORMAT_NONE;
256
257   if (align)
258     *align = GST_H264_PARSE_ALIGN_NONE;
259
260   if (caps && gst_caps_get_size (caps) > 0) {
261     GstStructure *s = gst_caps_get_structure (caps, 0);
262     const gchar *str = NULL;
263
264     if (format) {
265       if ((str = gst_structure_get_string (s, "stream-format"))) {
266         if (strcmp (str, "avc") == 0)
267           *format = GST_H264_PARSE_FORMAT_AVC;
268         else if (strcmp (str, "byte-stream") == 0)
269           *format = GST_H264_PARSE_FORMAT_BYTE;
270       }
271     }
272
273     if (align) {
274       if ((str = gst_structure_get_string (s, "alignment"))) {
275         if (strcmp (str, "au") == 0)
276           *align = GST_H264_PARSE_ALIGN_AU;
277         else if (strcmp (str, "nal") == 0)
278           *align = GST_H264_PARSE_ALIGN_NAL;
279       }
280     }
281   }
282 }
283
284 /* check downstream caps to configure format and alignment */
285 static void
286 gst_h264_parse_negotiate (GstH264Parse * h264parse)
287 {
288   GstCaps *caps;
289   guint format = GST_H264_PARSE_FORMAT_NONE;
290   guint align = GST_H264_PARSE_ALIGN_NONE;
291
292   caps = gst_pad_get_allowed_caps (GST_BASE_PARSE_SRC_PAD (h264parse));
293   GST_DEBUG_OBJECT (h264parse, "allowed caps: %" GST_PTR_FORMAT, caps);
294
295   gst_h264_parse_format_from_caps (caps, &format, &align);
296
297   if (caps)
298     gst_caps_unref (caps);
299
300   /* default */
301   if (!format)
302     format = GST_H264_PARSE_FORMAT_BYTE;
303   if (!align)
304     align = GST_H264_PARSE_ALIGN_AU;
305
306   GST_DEBUG_OBJECT (h264parse, "selected format %s, alignment %s",
307       gst_h264_parse_get_string (h264parse, TRUE, format),
308       gst_h264_parse_get_string (h264parse, FALSE, align));
309
310   h264parse->format = format;
311   h264parse->align = align;
312 }
313
314 static GstBuffer *
315 gst_h264_parse_wrap_nal (GstH264Parse * h264parse, guint format, guint8 * data,
316     guint size)
317 {
318   GstBuffer *buf;
319   const guint nl = h264parse->nal_length_size;
320
321   buf = gst_buffer_new_and_alloc (size + nl + 4);
322   if (format == GST_H264_PARSE_FORMAT_AVC) {
323     GST_WRITE_UINT32_BE (GST_BUFFER_DATA (buf), size << (32 - 8 * nl));
324   } else {
325     g_assert (nl == 4);
326     GST_WRITE_UINT32_BE (GST_BUFFER_DATA (buf), 1);
327   }
328
329   GST_BUFFER_SIZE (buf) = size + nl;
330   memcpy (GST_BUFFER_DATA (buf) + nl, data, size);
331
332   return buf;
333 }
334
335 /* SPS/PPS/IDR considered key, all others DELTA;
336  * so downstream waiting for keyframe can pick up at SPS/PPS/IDR */
337 #define NAL_TYPE_IS_KEY(nt) (((nt) == 5) || ((nt) == 7) || ((nt) == 8))
338
339 /* caller guarantees 2 bytes of nal payload */
340 static void
341 gst_h264_parse_process_nal (GstH264Parse * h264parse, guint8 * data,
342     gint sc_pos, gint nal_pos, guint nal_size)
343 {
344   guint nal_type;
345
346   g_return_if_fail (nal_pos - sc_pos > 0 && nal_pos - sc_pos <= 4);
347
348   /* nothing to do for broken input */
349   if (G_UNLIKELY (nal_size < 2)) {
350     GST_DEBUG_OBJECT (h264parse, "not processing nal size %u", nal_size);
351     return;
352   }
353
354   /* lower layer collects params */
355   gst_h264_params_parse_nal (h264parse->params, data + nal_pos, nal_size);
356
357   /* we have a peek as well */
358   nal_type = data[nal_pos] & 0x1f;
359   h264parse->keyframe |= NAL_TYPE_IS_KEY (nal_type);
360
361   switch (nal_type) {
362     case NAL_SPS:
363     case NAL_PPS:
364       /* parameters might have changed, force caps check */
365       GST_DEBUG_OBJECT (h264parse, "triggering src caps check");
366       h264parse->update_caps = TRUE;
367       /* found in stream, no need to forcibly push at start */
368       h264parse->push_codec = FALSE;
369       break;
370     case NAL_SLICE:
371     case NAL_SLICE_DPA:
372     case NAL_SLICE_DPB:
373     case NAL_SLICE_DPC:
374       /* real frame data */
375       h264parse->frame_start |= (h264parse->params->first_mb_in_slice == 0);
376       /* if we need to sneak codec NALs into the stream,
377        * this is a good place, so fake it as IDR
378        * (which should be at start anyway) */
379       if (G_LIKELY (!h264parse->push_codec))
380         break;
381       /* fall-through */
382     case NAL_SLICE_IDR:
383       /* real frame data */
384       h264parse->frame_start |= (h264parse->params->first_mb_in_slice == 0);
385       /* mark where config needs to go if interval expired */
386       /* mind replacement buffer if applicable */
387       if (h264parse->format == GST_H264_PARSE_FORMAT_AVC)
388         h264parse->idr_pos = gst_adapter_available (h264parse->frame_out);
389       else
390         h264parse->idr_pos = sc_pos;
391       GST_DEBUG_OBJECT (h264parse, "marking IDR in frame at offset %d",
392           h264parse->idr_pos);
393       break;
394   }
395
396   /* if AVC output needed, collect properly prefixed nal in adapter,
397    * and use that to replace outgoing buffer data later on */
398   if (h264parse->format == GST_H264_PARSE_FORMAT_AVC) {
399     GstBuffer *buf;
400
401     GST_LOG_OBJECT (h264parse, "collecting NAL in AVC frame");
402     buf = gst_h264_parse_wrap_nal (h264parse, h264parse->format,
403         data + nal_pos, nal_size);
404     gst_adapter_push (h264parse->frame_out, buf);
405   }
406 }
407
408 /* caller guarantees at least 2 bytes of nal payload for each nal
409  * returns TRUE if next_nal indicates that nal terminates an AU */
410 static inline gboolean
411 gst_h264_parse_collect_nal (GstH264Parse * h264parse, guint8 * nal,
412     guint8 * next_nal)
413 {
414   gint nal_type;
415   gboolean complete;
416
417   if (h264parse->align == GST_H264_PARSE_ALIGN_NAL)
418     return TRUE;
419
420   /* determine if AU complete */
421   nal_type = nal[0] & 0x1f;
422   GST_LOG_OBJECT (h264parse, "nal type: %d", nal_type);
423   /* coded slice NAL starts a picture,
424    * i.e. other types become aggregated in front of it */
425   h264parse->picture_start |= (nal_type == 1 || nal_type == 2 || nal_type == 5);
426
427   /* consider a coded slices (IDR or not) to start a picture,
428    * (so ending the previous one) if first_mb_in_slice == 0
429    * (non-0 is part of previous one) */
430   /* NOTE this is not entirely according to Access Unit specs in 7.4.1.2.4,
431    * but in practice it works in sane cases, needs not much parsing,
432    * and also works with broken frame_num in NAL
433    * (where spec-wise would fail) */
434   nal_type = next_nal[0] & 0x1f;
435   GST_LOG_OBJECT (h264parse, "next nal type: %d", nal_type);
436   complete = h264parse->picture_start && (nal_type >= 6 && nal_type <= 9);
437   complete |= h264parse->picture_start &&
438       (nal_type == 1 || nal_type == 2 || nal_type == 5) &&
439       /* first_mb_in_slice == 0 considered start of frame */
440       (next_nal[1] & 0x80);
441
442   GST_LOG_OBJECT (h264parse, "au complete: %d", complete);
443
444   return complete;
445 }
446
447 /* finds next startcode == 00 00 01, along with a subsequent byte */
448 static guint
449 gst_h264_parse_find_sc (GstBuffer * buffer, guint skip)
450 {
451   GstByteReader br;
452   guint sc_pos = -1;
453
454   gst_byte_reader_init_from_buffer (&br, buffer);
455
456   /* NALU not empty, so we can at least expect 1 (even 2) bytes following sc */
457   sc_pos = gst_byte_reader_masked_scan_uint32 (&br, 0xffffff00, 0x00000100,
458       skip, gst_byte_reader_get_remaining (&br) - skip);
459
460   return sc_pos;
461 }
462
463 /* FIXME move into baseparse, or anything equivalent;
464  * see https://bugzilla.gnome.org/show_bug.cgi?id=650093 */
465 #define GST_BASE_PARSE_FRAME_FLAG_PARSING   0x10000
466
467 static gboolean
468 gst_h264_parse_check_valid_frame (GstBaseParse * parse,
469     GstBaseParseFrame * frame, guint * framesize, gint * skipsize)
470 {
471   GstH264Parse *h264parse = GST_H264_PARSE (parse);
472   GstBuffer *buffer = frame->buffer;
473   gint sc_pos, nal_pos, next_sc_pos, next_nal_pos;
474   guint8 *data;
475   guint size;
476   gboolean drain;
477
478   /* expect at least 3 bytes startcode == sc, and 2 bytes NALU payload */
479   if (G_UNLIKELY (GST_BUFFER_SIZE (buffer) < 5))
480     return FALSE;
481
482   /* need to configure aggregation */
483   if (G_UNLIKELY (h264parse->format == GST_H264_PARSE_FORMAT_NONE))
484     gst_h264_parse_negotiate (h264parse);
485
486   /* avoid stale cached parsing state */
487   if (!(frame->flags & GST_BASE_PARSE_FRAME_FLAG_PARSING)) {
488     GST_LOG_OBJECT (h264parse, "parsing new frame");
489     gst_h264_parse_reset_frame (h264parse);
490     frame->flags |= GST_BASE_PARSE_FRAME_FLAG_PARSING;
491   } else {
492     GST_LOG_OBJECT (h264parse, "resuming frame parsing");
493   }
494
495   data = GST_BUFFER_DATA (buffer);
496   size = GST_BUFFER_SIZE (buffer);
497
498   GST_LOG_OBJECT (h264parse, "last_nal_pos: %d, last_scan_pos %d",
499       h264parse->last_nal_pos, h264parse->next_sc_pos);
500
501   nal_pos = h264parse->last_nal_pos;
502   next_sc_pos = h264parse->next_sc_pos;
503
504   if (!next_sc_pos) {
505     sc_pos = gst_h264_parse_find_sc (buffer, 0);
506
507     if (sc_pos == -1) {
508       /* SC not found, need more data */
509       sc_pos = GST_BUFFER_SIZE (buffer) - 3;
510       /* avoid going < 0 later on */
511       nal_pos = next_sc_pos = sc_pos;
512       goto more;
513     }
514
515     nal_pos = sc_pos + 3;
516     next_sc_pos = nal_pos;
517     /* sc might have 2 or 3 0-bytes */
518     if (sc_pos > 0 && data[sc_pos - 1] == 00)
519       sc_pos--;
520     GST_LOG_OBJECT (h264parse, "found sc at offset %d", sc_pos);
521   } else {
522     /* previous checks already arrange sc at start */
523     sc_pos = 0;
524   }
525
526   drain = GST_BASE_PARSE_DRAINING (parse);
527   while (TRUE) {
528     gint prev_sc_pos;
529
530     next_sc_pos = gst_h264_parse_find_sc (buffer, next_sc_pos);
531     if (next_sc_pos == -1) {
532       GST_LOG_OBJECT (h264parse, "no next sc");
533       if (drain) {
534         /* FLUSH/EOS, it's okay if we can't find the next frame */
535         next_sc_pos = size;
536         next_nal_pos = size;
537       } else {
538         next_sc_pos = size - 3;
539         goto more;
540       }
541     } else {
542       next_nal_pos = next_sc_pos + 3;
543       if (data[next_sc_pos - 1] == 00)
544         next_sc_pos--;
545       GST_LOG_OBJECT (h264parse, "found next sc at offset %d", next_sc_pos);
546       /* need at least 1 more byte of next NAL */
547       if (!drain && (next_nal_pos == size - 1))
548         goto more;
549     }
550
551     /* determine nal's sc position */
552     prev_sc_pos = nal_pos - 3;
553     g_assert (prev_sc_pos >= 0);
554     if (prev_sc_pos > 0 && data[prev_sc_pos - 1] == 0)
555       prev_sc_pos--;
556
557     /* already consume and gather info from NAL */
558     if (G_UNLIKELY (next_sc_pos - nal_pos < 2)) {
559       GST_WARNING_OBJECT (h264parse, "input stream is corrupt; "
560           "it contains a NAL unit of length %d", next_sc_pos - nal_pos);
561       /* broken nal at start -> arrange to skip it,
562        * otherwise have it terminate current au
563        * (and so it will be skippd on next frame round) */
564       if (prev_sc_pos == sc_pos) {
565         *skipsize = sc_pos + 2;
566         return FALSE;
567       } else {
568         next_sc_pos = prev_sc_pos;
569         break;
570       }
571     }
572     gst_h264_parse_process_nal (h264parse, data, prev_sc_pos, nal_pos,
573         next_sc_pos - nal_pos);
574     if (next_nal_pos >= size - 1 ||
575         gst_h264_parse_collect_nal (h264parse, data + nal_pos,
576             data + next_nal_pos))
577       break;
578
579     /* move along */
580     next_sc_pos = nal_pos = next_nal_pos;
581   }
582
583   *skipsize = sc_pos;
584   *framesize = next_sc_pos - sc_pos;
585
586   return TRUE;
587
588 more:
589   /* ask for best next available */
590   *framesize = G_MAXUINT;
591
592   /* skip up to initial startcode */
593   *skipsize = sc_pos;
594   /* resume scanning here next time */
595   h264parse->last_nal_pos = nal_pos - sc_pos;
596   h264parse->next_sc_pos = next_sc_pos - sc_pos;
597
598   return FALSE;
599 }
600
601 /* byte together avc codec data based on collected pps and sps so far */
602 static GstBuffer *
603 gst_h264_parse_make_codec_data (GstH264Parse * h264parse)
604 {
605   GstBuffer *buf, *nal;
606   gint i, sps_size = 0, pps_size = 0, num_sps = 0, num_pps = 0;
607   guint8 profile_idc = 0, profile_comp = 0, level_idc = 0;
608   gboolean found = FALSE;
609   guint8 *data;
610
611   /* only nal payload in stored nals */
612
613   for (i = 0; i < MAX_SPS_COUNT; i++) {
614     if ((nal = h264parse->params->sps_nals[i])) {
615       num_sps++;
616       /* size bytes also count */
617       sps_size += GST_BUFFER_SIZE (nal) + 2;
618       if (GST_BUFFER_SIZE (nal) >= 4) {
619         found = TRUE;
620         profile_idc = (GST_BUFFER_DATA (nal))[1];
621         profile_comp = (GST_BUFFER_DATA (nal))[2];
622         level_idc = (GST_BUFFER_DATA (nal))[3];
623       }
624     }
625   }
626   for (i = 0; i < MAX_PPS_COUNT; i++) {
627     if ((nal = h264parse->params->pps_nals[i])) {
628       num_pps++;
629       /* size bytes also count */
630       pps_size += GST_BUFFER_SIZE (nal) + 2;
631     }
632   }
633
634   GST_DEBUG_OBJECT (h264parse,
635       "constructing codec_data: num_sps=%d, num_pps=%d", num_sps, num_pps);
636
637   if (!found || !num_pps)
638     return NULL;
639
640   buf = gst_buffer_new_and_alloc (5 + 1 + sps_size + 1 + pps_size);
641   data = GST_BUFFER_DATA (buf);
642
643   data[0] = 1;                  /* AVC Decoder Configuration Record ver. 1 */
644   data[1] = profile_idc;        /* profile_idc                             */
645   data[2] = profile_comp;       /* profile_compability                     */
646   data[3] = level_idc;          /* level_idc                               */
647   data[4] = 0xfc | (4 - 1);     /* nal_length_size_minus1                  */
648   data[5] = 0xe0 | num_sps;     /* number of SPSs */
649
650   data += 6;
651   for (i = 0; i < MAX_SPS_COUNT; i++) {
652     if ((nal = h264parse->params->sps_nals[i])) {
653       GST_WRITE_UINT16_BE (data, GST_BUFFER_SIZE (nal));
654       memcpy (data + 2, GST_BUFFER_DATA (nal), GST_BUFFER_SIZE (nal));
655       data += 2 + GST_BUFFER_SIZE (nal);
656     }
657   }
658
659   data[0] = num_pps;
660   data++;
661   for (i = 0; i < MAX_PPS_COUNT; i++) {
662     if ((nal = h264parse->params->pps_nals[i])) {
663       GST_WRITE_UINT16_BE (data, GST_BUFFER_SIZE (nal));
664       memcpy (data + 2, GST_BUFFER_DATA (nal), GST_BUFFER_SIZE (nal));
665       data += 2 + GST_BUFFER_SIZE (nal);
666     }
667   }
668
669   return buf;
670 }
671
672 static void
673 gst_h264_parse_update_src_caps (GstH264Parse * h264parse, GstCaps * caps)
674 {
675   GstH264ParamsSPS *sps;
676   GstCaps *sink_caps;
677   gboolean modified = FALSE;
678   GstBuffer *buf = NULL;
679
680   if (G_UNLIKELY (!GST_PAD_CAPS (GST_BASE_PARSE_SRC_PAD (h264parse))))
681     modified = TRUE;
682   else if (G_UNLIKELY (!h264parse->update_caps))
683     return;
684
685   /* if this is being called from the first _setcaps call, caps on the sinkpad
686    * aren't set yet and so they need to be passed as an argument */
687   if (caps)
688     sink_caps = caps;
689   else
690     sink_caps = GST_PAD_CAPS (GST_BASE_PARSE_SINK_PAD (h264parse));
691
692   /* carry over input caps as much as possible; override with our own stuff */
693   if (sink_caps)
694     gst_caps_ref (sink_caps);
695   else
696     sink_caps = gst_caps_new_simple ("video/x-h264", NULL);
697
698   sps = h264parse->params->sps;
699   GST_DEBUG_OBJECT (h264parse, "sps: %p", sps);
700
701   /* only codec-data for nice-and-clean au aligned packetized avc format */
702   if (h264parse->format == GST_H264_PARSE_FORMAT_AVC &&
703       h264parse->align == GST_H264_PARSE_ALIGN_AU) {
704     buf = gst_h264_parse_make_codec_data (h264parse);
705     if (buf && h264parse->codec_data) {
706       if (GST_BUFFER_SIZE (buf) != GST_BUFFER_SIZE (h264parse->codec_data) ||
707           memcmp (GST_BUFFER_DATA (buf),
708               GST_BUFFER_DATA (h264parse->codec_data), GST_BUFFER_SIZE (buf)))
709         modified = TRUE;
710     } else {
711       if (h264parse->codec_data)
712         buf = gst_buffer_ref (h264parse->codec_data);
713       modified = TRUE;
714     }
715   }
716
717   caps = NULL;
718   if (G_UNLIKELY (!sps)) {
719     caps = gst_caps_copy (sink_caps);
720   } else if (G_UNLIKELY (h264parse->width != sps->width ||
721           h264parse->height != sps->height || h264parse->fps_num != sps->fps_num
722           || h264parse->fps_den != sps->fps_den || modified)) {
723     caps = gst_caps_copy (sink_caps);
724     /* sps should give this */
725     gst_caps_set_simple (caps, "width", G_TYPE_INT, sps->width,
726         "height", G_TYPE_INT, sps->height, NULL);
727     h264parse->height = sps->height;
728     h264parse->width = sps->width;
729     /* but not necessarily or reliably this */
730     if ((!h264parse->fps_num || !h264parse->fps_den) &&
731         sps->fps_num > 0 && sps->fps_den > 0) {
732       gst_caps_set_simple (caps, "framerate",
733           GST_TYPE_FRACTION, sps->fps_num, sps->fps_den, NULL);
734       h264parse->fps_num = sps->fps_num;
735       h264parse->fps_den = sps->fps_den;
736       gst_base_parse_set_frame_rate (GST_BASE_PARSE (h264parse),
737           h264parse->fps_num, h264parse->fps_den, 0, 0);
738     }
739   }
740
741   if (caps) {
742     gst_caps_set_simple (caps, "parsed", G_TYPE_BOOLEAN, TRUE,
743         "stream-format", G_TYPE_STRING,
744         gst_h264_parse_get_string (h264parse, TRUE, h264parse->format),
745         "alignment", G_TYPE_STRING,
746         gst_h264_parse_get_string (h264parse, FALSE, h264parse->align), NULL);
747     if (buf) {
748       gst_caps_set_simple (caps, "codec_data", GST_TYPE_BUFFER, buf, NULL);
749       gst_buffer_replace (&h264parse->codec_data, buf);
750       gst_buffer_unref (buf);
751       buf = NULL;
752     } else {
753       GstStructure *s;
754       /* remove any left-over codec-data hanging around */
755       s = gst_caps_get_structure (caps, 0);
756       gst_structure_remove_field (s, "codec_data");
757     }
758     gst_pad_set_caps (GST_BASE_PARSE_SRC_PAD (h264parse), caps);
759     gst_caps_unref (caps);
760   }
761
762   gst_caps_unref (sink_caps);
763   if (buf)
764     gst_buffer_unref (buf);
765 }
766
767 static GstFlowReturn
768 gst_h264_parse_parse_frame (GstBaseParse * parse, GstBaseParseFrame * frame)
769 {
770   GstH264Parse *h264parse;
771   GstBuffer *buffer;
772   guint av;
773
774   h264parse = GST_H264_PARSE (parse);
775   buffer = frame->buffer;
776
777   gst_h264_parse_update_src_caps (h264parse, NULL);
778
779   gst_h264_params_get_timestamp (h264parse->params,
780       &GST_BUFFER_TIMESTAMP (buffer), &GST_BUFFER_DURATION (buffer),
781       h264parse->frame_start);
782
783   if (h264parse->keyframe)
784     GST_BUFFER_FLAG_UNSET (buffer, GST_BUFFER_FLAG_DELTA_UNIT);
785   else
786     GST_BUFFER_FLAG_SET (buffer, GST_BUFFER_FLAG_DELTA_UNIT);
787
788   /* replace with transformed AVC output if applicable */
789   av = gst_adapter_available (h264parse->frame_out);
790   if (av) {
791     GstBuffer *buf;
792
793     buf = gst_adapter_take_buffer (h264parse->frame_out, av);
794     gst_buffer_copy_metadata (buf, buffer, GST_BUFFER_COPY_ALL);
795     gst_buffer_replace (&frame->buffer, buf);
796     gst_buffer_unref (buf);
797   }
798
799   return GST_FLOW_OK;
800 }
801
802 /* sends a codec NAL downstream, decorating and transforming as needed.
803  * No ownership is taken of @nal */
804 static GstFlowReturn
805 gst_h264_parse_push_codec_buffer (GstH264Parse * h264parse, GstBuffer * nal,
806     GstClockTime ts)
807 {
808   nal = gst_h264_parse_wrap_nal (h264parse, h264parse->format,
809       GST_BUFFER_DATA (nal), GST_BUFFER_SIZE (nal));
810
811   GST_BUFFER_TIMESTAMP (nal) = ts;
812   GST_BUFFER_DURATION (nal) = 0;
813
814   gst_buffer_set_caps (nal, GST_PAD_CAPS (GST_BASE_PARSE_SRC_PAD (h264parse)));
815
816   return gst_pad_push (GST_BASE_PARSE_SRC_PAD (h264parse), nal);
817 }
818
819 static GstFlowReturn
820 gst_h264_parse_pre_push_frame (GstBaseParse * parse, GstBaseParseFrame * frame)
821 {
822   GstH264Parse *h264parse;
823   GstBuffer *buffer;
824
825   h264parse = GST_H264_PARSE (parse);
826   buffer = frame->buffer;
827
828   /* periodic SPS/PPS sending */
829   if (h264parse->interval > 0 || h264parse->push_codec) {
830     GstClockTime timestamp = GST_BUFFER_TIMESTAMP (buffer);
831     guint64 diff;
832
833     /* init */
834     if (!GST_CLOCK_TIME_IS_VALID (h264parse->last_report)) {
835       h264parse->last_report = timestamp;
836     }
837
838     if (h264parse->idr_pos >= 0) {
839       GST_LOG_OBJECT (h264parse, "IDR nal at offset %d", h264parse->idr_pos);
840
841       if (timestamp > h264parse->last_report)
842         diff = timestamp - h264parse->last_report;
843       else
844         diff = 0;
845
846       GST_LOG_OBJECT (h264parse,
847           "now %" GST_TIME_FORMAT ", last SPS/PPS %" GST_TIME_FORMAT,
848           GST_TIME_ARGS (timestamp), GST_TIME_ARGS (h264parse->last_report));
849
850       GST_DEBUG_OBJECT (h264parse,
851           "interval since last SPS/PPS %" GST_TIME_FORMAT,
852           GST_TIME_ARGS (diff));
853
854       if (GST_TIME_AS_SECONDS (diff) >= h264parse->interval ||
855           h264parse->push_codec) {
856         GstBuffer *codec_nal;
857         gint i;
858         GstClockTime new_ts;
859
860         /* avoid overwriting a perfectly fine timestamp */
861         new_ts = GST_CLOCK_TIME_IS_VALID (timestamp) ? timestamp :
862             h264parse->last_report;
863
864         if (h264parse->align == GST_H264_PARSE_ALIGN_NAL) {
865           /* send separate config NAL buffers */
866           GST_DEBUG_OBJECT (h264parse, "- sending SPS/PPS");
867           for (i = 0; i < MAX_SPS_COUNT; i++) {
868             if ((codec_nal = h264parse->params->sps_nals[i])) {
869               GST_DEBUG_OBJECT (h264parse, "sending SPS nal");
870               gst_h264_parse_push_codec_buffer (h264parse, codec_nal,
871                   timestamp);
872               h264parse->last_report = new_ts;
873             }
874           }
875           for (i = 0; i < MAX_PPS_COUNT; i++) {
876             if ((codec_nal = h264parse->params->pps_nals[i])) {
877               GST_DEBUG_OBJECT (h264parse, "sending PPS nal");
878               gst_h264_parse_push_codec_buffer (h264parse, codec_nal,
879                   timestamp);
880               h264parse->last_report = new_ts;
881             }
882           }
883         } else {
884           /* insert config NALs into AU */
885           GstByteWriter bw;
886           GstBuffer *new_buf;
887           const gboolean bs = h264parse->format == GST_H264_PARSE_FORMAT_BYTE;
888
889           gst_byte_writer_init_with_size (&bw, GST_BUFFER_SIZE (buffer), FALSE);
890           gst_byte_writer_put_data (&bw, GST_BUFFER_DATA (buffer),
891               h264parse->idr_pos);
892           GST_DEBUG_OBJECT (h264parse, "- inserting SPS/PPS");
893           for (i = 0; i < MAX_SPS_COUNT; i++) {
894             if ((codec_nal = h264parse->params->sps_nals[i])) {
895               GST_DEBUG_OBJECT (h264parse, "inserting SPS nal");
896               gst_byte_writer_put_uint32_be (&bw,
897                   bs ? 1 : GST_BUFFER_SIZE (codec_nal));
898               gst_byte_writer_put_data (&bw, GST_BUFFER_DATA (codec_nal),
899                   GST_BUFFER_SIZE (codec_nal));
900               h264parse->last_report = new_ts;
901             }
902           }
903           for (i = 0; i < MAX_PPS_COUNT; i++) {
904             if ((codec_nal = h264parse->params->pps_nals[i])) {
905               GST_DEBUG_OBJECT (h264parse, "inserting PPS nal");
906               gst_byte_writer_put_uint32_be (&bw,
907                   bs ? 1 : GST_BUFFER_SIZE (codec_nal));
908               gst_byte_writer_put_data (&bw, GST_BUFFER_DATA (codec_nal),
909                   GST_BUFFER_SIZE (codec_nal));
910               h264parse->last_report = new_ts;
911             }
912           }
913           gst_byte_writer_put_data (&bw,
914               GST_BUFFER_DATA (buffer) + h264parse->idr_pos,
915               GST_BUFFER_SIZE (buffer) - h264parse->idr_pos);
916           /* collect result and push */
917           new_buf = gst_byte_writer_reset_and_get_buffer (&bw);
918           gst_buffer_copy_metadata (new_buf, buffer, GST_BUFFER_COPY_ALL);
919           gst_buffer_replace (&frame->buffer, new_buf);
920           gst_buffer_unref (new_buf);
921         }
922       }
923       /* we pushed whatever we had */
924       h264parse->push_codec = FALSE;
925     }
926   }
927
928   gst_h264_parse_reset_frame (h264parse);
929
930   return GST_FLOW_OK;
931 }
932
933 static gboolean
934 gst_h264_parse_set_caps (GstBaseParse * parse, GstCaps * caps)
935 {
936   GstH264Parse *h264parse;
937   GstStructure *str;
938   const GValue *value;
939   GstBuffer *codec_data = NULL;
940   guint size, format, align;
941
942   h264parse = GST_H264_PARSE (parse);
943
944   /* reset */
945   h264parse->push_codec = FALSE;
946
947   str = gst_caps_get_structure (caps, 0);
948
949   /* accept upstream info if provided */
950   gst_structure_get_int (str, "width", &h264parse->width);
951   gst_structure_get_int (str, "height", &h264parse->height);
952   gst_structure_get_fraction (str, "framerate", &h264parse->fps_num,
953       &h264parse->fps_den);
954
955   /* packetized video has a codec_data */
956   if ((value = gst_structure_get_value (str, "codec_data"))) {
957     guint8 *data;
958     guint num_sps, num_pps, profile, len;
959     gint i;
960
961     GST_DEBUG_OBJECT (h264parse, "have packetized h264");
962     /* make note for optional split processing */
963     h264parse->packetized = TRUE;
964
965     codec_data = gst_value_get_buffer (value);
966     if (!codec_data)
967       goto wrong_type;
968     data = GST_BUFFER_DATA (codec_data);
969     size = GST_BUFFER_SIZE (codec_data);
970
971     /* parse the avcC data */
972     if (size < 7)
973       goto avcc_too_small;
974     /* parse the version, this must be 1 */
975     if (data[0] != 1)
976       goto wrong_version;
977
978     /* AVCProfileIndication */
979     /* profile_compat */
980     /* AVCLevelIndication */
981     profile = (data[1] << 16) | (data[2] << 8) | data[3];
982     GST_DEBUG_OBJECT (h264parse, "profile %06x", profile);
983
984     /* 6 bits reserved | 2 bits lengthSizeMinusOne */
985     /* this is the number of bytes in front of the NAL units to mark their
986      * length */
987     h264parse->nal_length_size = (data[4] & 0x03) + 1;
988     GST_DEBUG_OBJECT (h264parse, "nal length %u", h264parse->nal_length_size);
989
990     num_sps = data[5] & 0x1f;
991     data += 6;
992     size -= 6;
993     for (i = 0; i < num_sps; i++) {
994       len = GST_READ_UINT16_BE (data);
995       if (size < len + 2 || len < 2)
996         goto avcc_too_small;
997       /* digest for later reference */
998       gst_h264_parse_process_nal (h264parse, data, 0, 2, len);
999       data += len + 2;
1000       size -= len + 2;
1001     }
1002     num_pps = data[0];
1003     data++;
1004     size++;
1005     for (i = 0; i < num_pps; i++) {
1006       len = GST_READ_UINT16_BE (data);
1007       if (size < len + 2 || len < 2)
1008         goto avcc_too_small;
1009       /* digest for later reference */
1010       gst_h264_parse_process_nal (h264parse, data, 0, 2, len);
1011       data += len + 2;
1012       size -= len + 2;
1013     }
1014
1015     h264parse->codec_data = gst_buffer_ref (codec_data);
1016   } else {
1017     GST_DEBUG_OBJECT (h264parse, "have bytestream h264");
1018     /* nothing to pre-process */
1019     h264parse->packetized = FALSE;
1020     /* we have 4 sync bytes */
1021     h264parse->nal_length_size = 4;
1022   }
1023
1024   /* negotiate with downstream, sets ->format and ->align */
1025   gst_h264_parse_negotiate (h264parse);
1026
1027   /* get upstream format and align from caps */
1028   gst_h264_parse_format_from_caps (caps, &format, &align);
1029
1030   /* if upstream sets codec_data without setting stream-format and alignment, we
1031    * assume stream-format=avc,alignment=au */
1032   if (format == GST_H264_PARSE_FORMAT_NONE) {
1033     if (codec_data == NULL)
1034       goto unknown_input_format;
1035
1036     format = GST_H264_PARSE_FORMAT_AVC;
1037     align = GST_H264_PARSE_ALIGN_AU;
1038   }
1039
1040   if (format == h264parse->format && align == h264parse->align) {
1041     gst_base_parse_set_passthrough (parse, TRUE);
1042
1043     /* we did parse codec-data and might supplement src caps */
1044     gst_h264_parse_update_src_caps (h264parse, caps);
1045   } else if (format == GST_H264_PARSE_FORMAT_AVC) {
1046     /* if input != output, and input is avc, must split before anything else */
1047     /* arrange to insert codec-data in-stream if needed.
1048      * src caps are only arranged for later on */
1049     h264parse->push_codec = TRUE;
1050     h264parse->split_packetized = TRUE;
1051     h264parse->packetized = TRUE;
1052   }
1053
1054   return TRUE;
1055
1056   /* ERRORS */
1057 avcc_too_small:
1058   {
1059     GST_DEBUG_OBJECT (h264parse, "avcC size %u < 7", size);
1060     goto refuse_caps;
1061   }
1062 wrong_version:
1063   {
1064     GST_DEBUG_OBJECT (h264parse, "wrong avcC version");
1065     goto refuse_caps;
1066   }
1067 wrong_type:
1068   {
1069     GST_DEBUG_OBJECT (h264parse, "wrong codec-data type");
1070     goto refuse_caps;
1071   }
1072 unknown_input_format:
1073   {
1074     GST_DEBUG_OBJECT (h264parse, "unknown stream-format and no codec_data");
1075     goto refuse_caps;
1076   }
1077 refuse_caps:
1078   {
1079     GST_WARNING_OBJECT (h264parse, "refused caps %" GST_PTR_FORMAT, caps);
1080     return FALSE;
1081   }
1082 }
1083
1084 static GstFlowReturn
1085 gst_h264_parse_chain (GstPad * pad, GstBuffer * buffer)
1086 {
1087   GstH264Parse *h264parse = GST_H264_PARSE (GST_PAD_PARENT (pad));
1088
1089   if (h264parse->packetized && buffer) {
1090     GstByteReader br;
1091     GstBuffer *sub;
1092     GstFlowReturn ret = GST_FLOW_OK;
1093     guint32 len;
1094     const guint nl = h264parse->nal_length_size;
1095
1096     GST_LOG_OBJECT (h264parse, "processing packet buffer of size %d",
1097         GST_BUFFER_SIZE (buffer));
1098     gst_byte_reader_init_from_buffer (&br, buffer);
1099     while (ret == GST_FLOW_OK && gst_byte_reader_get_remaining (&br)) {
1100       GST_DEBUG_OBJECT (h264parse, "AVC nal offset %d",
1101           gst_byte_reader_get_pos (&br));
1102       if (gst_byte_reader_get_remaining (&br) < nl)
1103         goto parse_failed;
1104       switch (nl) {
1105         case 4:
1106           len = gst_byte_reader_get_uint32_be_unchecked (&br);
1107           break;
1108         case 3:
1109           len = gst_byte_reader_get_uint24_be_unchecked (&br);
1110           break;
1111         case 2:
1112           len = gst_byte_reader_get_uint16_be_unchecked (&br);
1113           break;
1114         case 1:
1115           len = gst_byte_reader_get_uint8_unchecked (&br);
1116           break;
1117         default:
1118           goto not_negotiated;
1119           break;
1120       }
1121       GST_DEBUG_OBJECT (h264parse, "AVC nal size %d", len);
1122       if (gst_byte_reader_get_remaining (&br) < len)
1123         goto parse_failed;
1124       if (h264parse->split_packetized) {
1125         /* convert to NAL aligned byte stream input */
1126         sub = gst_h264_parse_wrap_nal (h264parse, GST_H264_PARSE_FORMAT_BYTE,
1127             (guint8 *) gst_byte_reader_get_data_unchecked (&br, len), len);
1128         /* at least this should make sense */
1129         GST_BUFFER_TIMESTAMP (sub) = GST_BUFFER_TIMESTAMP (buffer);
1130         GST_LOG_OBJECT (h264parse, "pushing NAL of size %d", len);
1131         ret = h264parse->parse_chain (pad, sub);
1132       } else {
1133         /* pass-through: no looking for frames (and nal processing),
1134          * so need to parse to collect data here */
1135         /* NOTE: so if it is really configured to do so,
1136          * pre_push can/will still insert codec-data at intervals,
1137          * which is not really pure pass-through, but anyway ... */
1138         gst_h264_parse_process_nal (h264parse,
1139             GST_BUFFER_DATA (buffer), gst_byte_reader_get_pos (&br) - nl,
1140             gst_byte_reader_get_pos (&br), len);
1141         gst_byte_reader_skip_unchecked (&br, len);
1142       }
1143     }
1144     if (h264parse->split_packetized) {
1145       gst_buffer_unref (buffer);
1146       return ret;
1147     } else {
1148       /* nal processing in pass-through might have collected stuff;
1149        * ensure nothing happens with this later on */
1150       gst_adapter_clear (h264parse->frame_out);
1151     }
1152   }
1153
1154 exit:
1155   return h264parse->parse_chain (pad, buffer);
1156
1157   /* ERRORS */
1158 not_negotiated:
1159   {
1160     GST_DEBUG_OBJECT (h264parse, "insufficient data to split input");
1161     gst_buffer_unref (buffer);
1162     return GST_FLOW_NOT_NEGOTIATED;
1163   }
1164 parse_failed:
1165   {
1166     if (h264parse->split_packetized) {
1167       GST_ELEMENT_ERROR (h264parse, STREAM, FAILED, (NULL),
1168           ("invalid AVC input data"));
1169       gst_buffer_unref (buffer);
1170       return GST_FLOW_ERROR;
1171     } else {
1172       /* do not meddle to much in this case */
1173       GST_DEBUG_OBJECT (h264parse, "parsing packet failed");
1174       goto exit;
1175     }
1176   }
1177 }
1178
1179 static void
1180 gst_h264_parse_set_property (GObject * object, guint prop_id,
1181     const GValue * value, GParamSpec * pspec)
1182 {
1183   GstH264Parse *parse;
1184
1185   parse = GST_H264_PARSE (object);
1186
1187   switch (prop_id) {
1188     case PROP_CONFIG_INTERVAL:
1189       parse->interval = g_value_get_uint (value);
1190       break;
1191     default:
1192       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1193       break;
1194   }
1195 }
1196
1197 static void
1198 gst_h264_parse_get_property (GObject * object, guint prop_id, GValue * value,
1199     GParamSpec * pspec)
1200 {
1201   GstH264Parse *parse;
1202
1203   parse = GST_H264_PARSE (object);
1204
1205   switch (prop_id) {
1206     case PROP_CONFIG_INTERVAL:
1207       g_value_set_uint (value, parse->interval);
1208       break;
1209     default:
1210       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1211       break;
1212   }
1213 }