1 /* GStreamer H.263 Parser
2 * Copyright (C) <2010> Arun Raghavan <arun.raghavan@collabora.co.uk>
3 * Copyright (C) <2010> Edward Hervey <edward.hervey@collabora.co.uk>
4 * Copyright (C) <2010> Collabora Multimedia
5 * Copyright (C) <2010> Nokia Corporation
6 *
7 * Some bits C-c,C-v'ed and s/4/3 from h264parse:
8 * (C) 2005 Michal Benes <michal.benes@itonis.tv>
9 * (C) 2008 Wim Taymans <wim.taymans@gmail.com>
10 * (C) 2009 Mark Nauwelaerts <mnauw users sf net>
11 *
12 * This library is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU Library General Public
14 * License as published by the Free Software Foundation; either
15 * version 2 of the License, or (at your option) any later version.
16 *
17 * This library is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * Library General Public License for more details.
21 *
22 * You should have received a copy of the GNU Library General Public
23 * License along with this library; if not, write to the
24 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
25 * Boston, MA 02110-1301, USA.
26 */
27
28 #ifdef HAVE_CONFIG_H
29 # include "config.h"
30 #endif
31
32 #include <gst/base/base.h>
33 #include <gst/pbutils/pbutils.h>
34 #include "gsth263parse.h"
35
36 #include <string.h>
37
38 GST_DEBUG_CATEGORY (h263_parse_debug);
39 #define GST_CAT_DEFAULT h263_parse_debug
40
41 static GstStaticPadTemplate srctemplate =
42 GST_STATIC_PAD_TEMPLATE ("src", GST_PAD_SRC,
43 GST_PAD_ALWAYS,
44 GST_STATIC_CAPS ("video/x-h263, variant = (string) itu, "
45 "parsed = (boolean) true, framerate=(fraction)[0/1,MAX]")
46 );
47
48 static GstStaticPadTemplate sinktemplate =
49 GST_STATIC_PAD_TEMPLATE ("sink", GST_PAD_SINK,
50 GST_PAD_ALWAYS,
51 GST_STATIC_CAPS ("video/x-h263, variant = (string) itu")
52 );
53
54 #define parent_class gst_h263_parse_parent_class
55 G_DEFINE_TYPE (GstH263Parse, gst_h263_parse, GST_TYPE_BASE_PARSE);
56
57 static gboolean gst_h263_parse_start (GstBaseParse * parse);
58 static gboolean gst_h263_parse_stop (GstBaseParse * parse);
59 static gboolean gst_h263_parse_sink_event (GstBaseParse * parse,
60 GstEvent * event);
61 static GstFlowReturn gst_h263_parse_handle_frame (GstBaseParse * parse,
62 GstBaseParseFrame * frame, gint * skipsize);
63 static GstFlowReturn gst_h263_parse_pre_push_frame (GstBaseParse * parse,
64 GstBaseParseFrame * frame);
65 static GstCaps *gst_h263_parse_get_sink_caps (GstBaseParse * parse,
66 GstCaps * filter);
67
68 static void
gst_h263_parse_class_init(GstH263ParseClass * klass)69 gst_h263_parse_class_init (GstH263ParseClass * klass)
70 {
71 GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass);
72 GstBaseParseClass *parse_class = GST_BASE_PARSE_CLASS (klass);
73
74 GST_DEBUG_CATEGORY_INIT (h263_parse_debug, "h263parse", 0, "h263 parser");
75
76 gst_element_class_add_static_pad_template (gstelement_class, &srctemplate);
77 gst_element_class_add_static_pad_template (gstelement_class, &sinktemplate);
78 gst_element_class_set_static_metadata (gstelement_class, "H.263 parser",
79 "Codec/Parser/Video",
80 "Parses H.263 streams",
81 "Arun Raghavan <arun.raghavan@collabora.co.uk>,"
82 "Edward Hervey <edward.hervey@collabora.co.uk>");
83
84 /* Override BaseParse vfuncs */
85 parse_class->start = GST_DEBUG_FUNCPTR (gst_h263_parse_start);
86 parse_class->stop = GST_DEBUG_FUNCPTR (gst_h263_parse_stop);
87 parse_class->sink_event = GST_DEBUG_FUNCPTR (gst_h263_parse_sink_event);
88 parse_class->handle_frame = GST_DEBUG_FUNCPTR (gst_h263_parse_handle_frame);
89 parse_class->pre_push_frame =
90 GST_DEBUG_FUNCPTR (gst_h263_parse_pre_push_frame);
91 parse_class->get_sink_caps = GST_DEBUG_FUNCPTR (gst_h263_parse_get_sink_caps);
92 }
93
94 static void
gst_h263_parse_init(GstH263Parse * h263parse)95 gst_h263_parse_init (GstH263Parse * h263parse)
96 {
97 GST_PAD_SET_ACCEPT_INTERSECT (GST_BASE_PARSE_SINK_PAD (h263parse));
98 GST_PAD_SET_ACCEPT_TEMPLATE (GST_BASE_PARSE_SINK_PAD (h263parse));
99 }
100
101 static gboolean
gst_h263_parse_start(GstBaseParse * parse)102 gst_h263_parse_start (GstBaseParse * parse)
103 {
104 GstH263Parse *h263parse = GST_H263_PARSE (parse);
105
106 GST_DEBUG_OBJECT (h263parse, "start");
107
108 h263parse->bitrate = 0;
109 h263parse->profile = -1;
110 h263parse->level = -1;
111
112 h263parse->state = PARSING;
113
114 h263parse->sent_codec_tag = FALSE;
115
116 gst_base_parse_set_min_frame_size (parse, 4);
117
118 return TRUE;
119 }
120
121 static gboolean
gst_h263_parse_stop(GstBaseParse * parse)122 gst_h263_parse_stop (GstBaseParse * parse)
123 {
124 GST_DEBUG_OBJECT (parse, "stop");
125
126 return TRUE;
127 }
128
129 static gboolean
gst_h263_parse_sink_event(GstBaseParse * parse,GstEvent * event)130 gst_h263_parse_sink_event (GstBaseParse * parse, GstEvent * event)
131 {
132 GstH263Parse *h263parse;
133
134 h263parse = GST_H263_PARSE (parse);
135
136 switch (GST_EVENT_TYPE (event)) {
137 case GST_EVENT_TAG:
138 {
139 GstTagList *taglist;
140
141 gst_event_parse_tag (event, &taglist);
142
143 if (gst_tag_list_get_uint (taglist, GST_TAG_BITRATE, &h263parse->bitrate))
144 GST_DEBUG_OBJECT (h263parse, "got bitrate tag: %u", h263parse->bitrate);
145
146 break;
147 }
148 default:
149 break;
150 }
151
152 return GST_BASE_PARSE_CLASS (parent_class)->sink_event (parse, event);
153 }
154
155 static guint
find_psc(GstBuffer * buffer,guint skip)156 find_psc (GstBuffer * buffer, guint skip)
157 {
158 GstMapInfo map;
159 GstByteReader br;
160 guint psc_pos = -1, psc;
161
162 gst_buffer_map (buffer, &map, GST_MAP_READ);
163 gst_byte_reader_init (&br, map.data, map.size);
164
165 if (!gst_byte_reader_set_pos (&br, skip))
166 goto out;
167
168 if (gst_byte_reader_peek_uint24_be (&br, &psc) == FALSE)
169 goto out;
170
171 /* Scan for the picture start code (22 bits - 0x0020)
172 * startcode : 0000 0000 0000 0000 1000 00xx
173 * mask (bin) : 1111 1111 1111 1111 1111 1100
174 * mask (hex) : f f f f f c
175 * match : 0 0 0 0 8 0
176 */
177 while ((gst_byte_reader_get_remaining (&br) >= 3)) {
178 if (gst_byte_reader_peek_uint24_be (&br, &psc) &&
179 ((psc & 0xfffffc) == 0x000080)) {
180 psc_pos = gst_byte_reader_get_pos (&br);
181 break;
182 } else if (gst_byte_reader_skip (&br, 1) == FALSE)
183 break;
184 }
185
186 out:
187 gst_buffer_unmap (buffer, &map);
188 return psc_pos;
189 }
190
191 static void
gst_h263_parse_set_src_caps(GstH263Parse * h263parse,const H263Params * params)192 gst_h263_parse_set_src_caps (GstH263Parse * h263parse,
193 const H263Params * params)
194 {
195 GstStructure *st = NULL;
196 GstCaps *caps, *sink_caps;
197 gint fr_num, fr_denom, par_num, par_denom;
198
199 g_assert (h263parse->state == PASSTHROUGH || h263parse->state == GOT_HEADER);
200
201 caps = gst_pad_get_current_caps (GST_BASE_PARSE_SINK_PAD (h263parse));
202 if (caps) {
203 caps = gst_caps_make_writable (caps);
204 } else {
205 caps = gst_caps_new_simple ("video/x-h263",
206 "variant", G_TYPE_STRING, "itu", NULL);
207 }
208 gst_caps_set_simple (caps, "parsed", G_TYPE_BOOLEAN, TRUE, NULL);
209
210 sink_caps = gst_pad_get_current_caps (GST_BASE_PARSE_SINK_PAD (h263parse));
211 if (sink_caps && (st = gst_caps_get_structure (sink_caps, 0)) &&
212 gst_structure_get_fraction (st, "framerate", &fr_num, &fr_denom)) {
213 /* Got it in caps - nothing more to do */
214 GST_DEBUG_OBJECT (h263parse, "sink caps override framerate from headers");
215 } else {
216 /* Caps didn't have the framerate - get it from params */
217 gst_h263_parse_get_framerate (params, &fr_num, &fr_denom);
218 }
219 gst_caps_set_simple (caps, "framerate", GST_TYPE_FRACTION, fr_num, fr_denom,
220 NULL);
221
222 if (params->width && params->height)
223 gst_caps_set_simple (caps, "width", G_TYPE_INT, params->width,
224 "height", G_TYPE_INT, params->height, NULL);
225
226 if (st != NULL
227 && gst_structure_get_fraction (st, "pixel-aspect-ratio", &par_num,
228 &par_denom)) {
229 /* Got it in caps - nothing more to do */
230 GST_DEBUG_OBJECT (h263parse, "sink caps override PAR");
231 } else {
232 /* Caps didn't have the framerate - get it from params */
233 gst_h263_parse_get_par (params, &par_num, &par_denom);
234 }
235 gst_caps_set_simple (caps, "pixel-aspect-ratio", GST_TYPE_FRACTION,
236 par_num, par_denom, NULL);
237
238 if (h263parse->state == GOT_HEADER) {
239 gst_caps_set_simple (caps,
240 "annex-d", G_TYPE_BOOLEAN, (params->features & H263_OPTION_UMV_MODE),
241 "annex-e", G_TYPE_BOOLEAN, (params->features & H263_OPTION_SAC_MODE),
242 "annex-f", G_TYPE_BOOLEAN, (params->features & H263_OPTION_AP_MODE),
243 "annex-g", G_TYPE_BOOLEAN, (params->features & H263_OPTION_PB_MODE),
244 "annex-i", G_TYPE_BOOLEAN, (params->features & H263_OPTION_AIC_MODE),
245 "annex-j", G_TYPE_BOOLEAN, (params->features & H263_OPTION_DF_MODE),
246 "annex-k", G_TYPE_BOOLEAN, (params->features & H263_OPTION_SS_MODE),
247 "annex-m", G_TYPE_BOOLEAN, (params->type == PICTURE_IMPROVED_PB),
248 "annex-n", G_TYPE_BOOLEAN, (params->features & H263_OPTION_RPS_MODE),
249 "annex-q", G_TYPE_BOOLEAN, (params->features & H263_OPTION_RRU_MODE),
250 "annex-r", G_TYPE_BOOLEAN, (params->features & H263_OPTION_ISD_MODE),
251 "annex-s", G_TYPE_BOOLEAN, (params->features & H263_OPTION_AIV_MODE),
252 "annex-t", G_TYPE_BOOLEAN, (params->features & H263_OPTION_MQ_MODE),
253 "annex-u", G_TYPE_BOOLEAN, (params->features & H263_OPTION_ERPS_MODE),
254 "annex-v", G_TYPE_BOOLEAN, (params->features & H263_OPTION_DPS_MODE),
255 NULL);
256
257 h263parse->profile = gst_h263_parse_get_profile (params);
258 if (h263parse->profile != -1) {
259 gchar *profile_str;
260
261 profile_str = g_strdup_printf ("%u", h263parse->profile);
262 gst_caps_set_simple (caps, "profile", G_TYPE_STRING, profile_str, NULL);
263 g_free (profile_str);
264 }
265
266 h263parse->level = gst_h263_parse_get_level (params, h263parse->profile,
267 h263parse->bitrate, fr_num, fr_denom);
268 if (h263parse->level != -1) {
269 gchar *level_str;
270
271 level_str = g_strdup_printf ("%u", h263parse->level);
272 gst_caps_set_simple (caps, "level", G_TYPE_STRING, level_str, NULL);
273 g_free (level_str);
274 }
275 }
276
277 gst_pad_set_caps (GST_BASE_PARSE_SRC_PAD (GST_BASE_PARSE (h263parse)), caps);
278 gst_caps_unref (caps);
279 if (sink_caps)
280 gst_caps_unref (sink_caps);
281 }
282
283 static GstFlowReturn
gst_h263_parse_handle_frame(GstBaseParse * parse,GstBaseParseFrame * frame,gint * skipsize)284 gst_h263_parse_handle_frame (GstBaseParse * parse,
285 GstBaseParseFrame * frame, gint * skipsize)
286 {
287 GstH263Parse *h263parse;
288 GstBuffer *buffer;
289 guint psc_pos, next_psc_pos;
290 gsize size;
291 H263Params params = { 0, };
292 GstFlowReturn res = GST_FLOW_OK;
293
294 h263parse = GST_H263_PARSE (parse);
295 buffer = frame->buffer;
296 size = gst_buffer_get_size (buffer);
297
298 if (size < 3) {
299 *skipsize = 1;
300 return GST_FLOW_OK;
301 }
302
303 psc_pos = find_psc (buffer, 0);
304
305 if (psc_pos == -1) {
306 /* PSC not found, need more data */
307 if (size > 3)
308 psc_pos = size - 3;
309 else
310 psc_pos = 0;
311 goto more;
312 }
313
314 /* need to skip */
315 if (psc_pos > 0)
316 goto more;
317
318 /* Found the start of the frame, now try to find the end */
319 next_psc_pos = psc_pos + 3;
320 next_psc_pos = find_psc (buffer, next_psc_pos);
321
322 if (next_psc_pos == -1) {
323 if (GST_BASE_PARSE_DRAINING (parse))
324 /* FLUSH/EOS, it's okay if we can't find the next frame */
325 next_psc_pos = size;
326 else
327 goto more;
328 }
329
330 /* We should now have a complete frame */
331
332 /* If this is the first frame, parse and set srcpad caps */
333 if (h263parse->state == PARSING) {
334 res = gst_h263_parse_get_params (¶ms, buffer, FALSE, &h263parse->state);
335 if (res != GST_FLOW_OK || h263parse->state != GOT_HEADER) {
336 GST_WARNING ("Couldn't parse header - setting passthrough mode");
337 gst_base_parse_set_passthrough (parse, TRUE);
338 } else {
339 /* Set srcpad caps since we now have sufficient information to do so */
340 gst_h263_parse_set_src_caps (h263parse, ¶ms);
341 gst_base_parse_set_passthrough (parse, FALSE);
342 }
343 memset (¶ms, 0, sizeof (params));
344 }
345
346 /* XXX: After getting a keyframe, should we adjust min_frame_size to
347 * something smaller so we don't end up collecting too many non-keyframes? */
348
349 GST_DEBUG_OBJECT (h263parse, "found a frame of size %u at pos %u",
350 next_psc_pos, psc_pos);
351
352 res = gst_h263_parse_get_params (¶ms, buffer, TRUE, &h263parse->state);
353 if (res != GST_FLOW_OK)
354 goto more;
355
356 if (h263parse->state == PASSTHROUGH || h263parse->state == PARSING) {
357 /* There's a feature we don't support, or we didn't have enough data to
358 * parse the header, which should not be possible. Either way, go into
359 * passthrough mode and let downstream handle it if it can. */
360 GST_WARNING ("Couldn't parse header - setting passthrough mode");
361 gst_base_parse_set_passthrough (parse, TRUE);
362 goto more;
363 }
364
365 if (gst_h263_parse_is_delta_unit (¶ms))
366 GST_BUFFER_FLAG_UNSET (buffer, GST_BUFFER_FLAG_DELTA_UNIT);
367 else
368 GST_BUFFER_FLAG_SET (buffer, GST_BUFFER_FLAG_DELTA_UNIT);
369
370 return gst_base_parse_finish_frame (parse, frame, next_psc_pos);
371
372 more:
373 *skipsize = psc_pos;
374
375 return res;
376 }
377
378 static void
remove_fields(GstCaps * caps)379 remove_fields (GstCaps * caps)
380 {
381 guint i, n;
382
383 n = gst_caps_get_size (caps);
384 for (i = 0; i < n; i++) {
385 GstStructure *s = gst_caps_get_structure (caps, i);
386
387 gst_structure_remove_field (s, "parsed");
388 }
389 }
390
391 static GstCaps *
gst_h263_parse_get_sink_caps(GstBaseParse * parse,GstCaps * filter)392 gst_h263_parse_get_sink_caps (GstBaseParse * parse, GstCaps * filter)
393 {
394 GstCaps *peercaps, *templ;
395 GstCaps *res;
396
397 templ = gst_pad_get_pad_template_caps (GST_BASE_PARSE_SINK_PAD (parse));
398 if (filter) {
399 GstCaps *fcopy = gst_caps_copy (filter);
400 /* Remove the fields we convert */
401 remove_fields (fcopy);
402 peercaps = gst_pad_peer_query_caps (GST_BASE_PARSE_SRC_PAD (parse), fcopy);
403 gst_caps_unref (fcopy);
404 } else
405 peercaps = gst_pad_peer_query_caps (GST_BASE_PARSE_SRC_PAD (parse), NULL);
406
407 if (peercaps) {
408 /* Remove parsed field */
409 peercaps = gst_caps_make_writable (peercaps);
410 remove_fields (peercaps);
411
412 res = gst_caps_intersect_full (peercaps, templ, GST_CAPS_INTERSECT_FIRST);
413 gst_caps_unref (peercaps);
414 gst_caps_unref (templ);
415 } else {
416 res = templ;
417 }
418
419 if (filter) {
420 GstCaps *intersection;
421
422 intersection =
423 gst_caps_intersect_full (filter, res, GST_CAPS_INTERSECT_FIRST);
424 gst_caps_unref (res);
425 res = intersection;
426 }
427
428 return res;
429 }
430
431 static GstFlowReturn
gst_h263_parse_pre_push_frame(GstBaseParse * parse,GstBaseParseFrame * frame)432 gst_h263_parse_pre_push_frame (GstBaseParse * parse, GstBaseParseFrame * frame)
433 {
434 GstH263Parse *h263parse = GST_H263_PARSE (parse);
435
436 if (!h263parse->sent_codec_tag) {
437 GstTagList *taglist;
438 GstCaps *caps;
439
440 /* codec tag */
441 caps = gst_pad_get_current_caps (GST_BASE_PARSE_SRC_PAD (parse));
442 if (G_UNLIKELY (caps == NULL)) {
443 if (GST_PAD_IS_FLUSHING (GST_BASE_PARSE_SRC_PAD (parse))) {
444 GST_INFO_OBJECT (parse, "Src pad is flushing");
445 return GST_FLOW_FLUSHING;
446 } else {
447 GST_INFO_OBJECT (parse, "Src pad is not negotiated!");
448 return GST_FLOW_NOT_NEGOTIATED;
449 }
450 }
451
452 taglist = gst_tag_list_new_empty ();
453 gst_pb_utils_add_codec_description_to_tag_list (taglist,
454 GST_TAG_VIDEO_CODEC, caps);
455 gst_caps_unref (caps);
456
457 gst_base_parse_merge_tags (parse, taglist, GST_TAG_MERGE_REPLACE);
458 gst_tag_list_unref (taglist);
459
460 /* also signals the end of first-frame processing */
461 h263parse->sent_codec_tag = TRUE;
462 }
463
464 return GST_FLOW_OK;
465 }
466