1 /* GStreamer
2 * Copyright (C) 2010 David Schleef <ds@schleef.org>
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for more details.
13 *
14 * You should have received a copy of the GNU Library General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
17 * Boston, MA 02110-1301, USA.
18 */
19 /**
20 * SECTION:element-diracparse
21 * @title: gstdiracparse
22 *
23 * The gstdiracparse element does FIXME stuff.
24 *
25 * ## Example launch line
26 * |[
27 * gst-launch-1.0 -v fakesrc ! gstdiracparse ! FIXME ! fakesink
28 * ]|
29 * FIXME Describe what the pipeline does.
30 *
31 */
32
33 #ifdef HAVE_CONFIG_H
34 #include "config.h"
35 #endif
36
37 #include <gst/gst.h>
38 #include <gst/base/base.h>
39 #include <gst/pbutils/pbutils.h>
40 #include <string.h>
41 #include "gstvideoparserselements.h"
42 #include "gstdiracparse.h"
43 #include "dirac_parse.h"
44
45 /* prototypes */
46
47
48 static void gst_dirac_parse_set_property (GObject * object,
49 guint property_id, const GValue * value, GParamSpec * pspec);
50 static void gst_dirac_parse_get_property (GObject * object,
51 guint property_id, GValue * value, GParamSpec * pspec);
52 static void gst_dirac_parse_dispose (GObject * object);
53 static void gst_dirac_parse_finalize (GObject * object);
54
55 static gboolean gst_dirac_parse_start (GstBaseParse * parse);
56 static gboolean gst_dirac_parse_stop (GstBaseParse * parse);
57 static gboolean gst_dirac_parse_set_sink_caps (GstBaseParse * parse,
58 GstCaps * caps);
59 static GstCaps *gst_dirac_parse_get_sink_caps (GstBaseParse * parse,
60 GstCaps * filter);
61 static GstFlowReturn gst_dirac_parse_handle_frame (GstBaseParse * parse,
62 GstBaseParseFrame * frame, gint * skipsize);
63 static gboolean gst_dirac_parse_convert (GstBaseParse * parse,
64 GstFormat src_format, gint64 src_value, GstFormat dest_format,
65 gint64 * dest_value);
66 static GstFlowReturn gst_dirac_parse_pre_push_frame (GstBaseParse * parse,
67 GstBaseParseFrame * frame);
68
69 enum
70 {
71 PROP_0
72 };
73
74 /* pad templates */
75
76 static GstStaticPadTemplate gst_dirac_parse_sink_template =
77 GST_STATIC_PAD_TEMPLATE ("sink",
78 GST_PAD_SINK,
79 GST_PAD_ALWAYS,
80 GST_STATIC_CAPS ("video/x-dirac")
81 );
82
83 static GstStaticPadTemplate gst_dirac_parse_src_template =
84 GST_STATIC_PAD_TEMPLATE ("src",
85 GST_PAD_SRC,
86 GST_PAD_ALWAYS,
87 GST_STATIC_CAPS ("video/x-dirac, parsed=(boolean)TRUE, "
88 "width=(int)[1,MAX], height=(int)[1,MAX], "
89 "framerate=(fraction)[0/1,MAX], "
90 "pixel-aspect-ratio=(fraction)[0/1,MAX], "
91 "interlace-mode=(string) { progressive, interleaved }, "
92 "profile=(string){ vc2-low-delay, vc2-simple, vc2-main, main }, "
93 "level=(string) { 0, 1, 128}")
94 );
95
96 /* class initialization */
97
98 #define parent_class gst_dirac_parse_parent_class
99 G_DEFINE_TYPE (GstDiracParse, gst_dirac_parse, GST_TYPE_BASE_PARSE);
100 GST_ELEMENT_REGISTER_DEFINE_WITH_CODE (diracparse, "diracparse", GST_RANK_NONE,
101 GST_TYPE_DIRAC_PARSE, videoparsers_element_init (plugin));
102
103 static void
gst_dirac_parse_class_init(GstDiracParseClass * klass)104 gst_dirac_parse_class_init (GstDiracParseClass * klass)
105 {
106 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
107 GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
108 GstBaseParseClass *base_parse_class = GST_BASE_PARSE_CLASS (klass);
109
110 gobject_class->set_property = gst_dirac_parse_set_property;
111 gobject_class->get_property = gst_dirac_parse_get_property;
112 gobject_class->dispose = gst_dirac_parse_dispose;
113 gobject_class->finalize = gst_dirac_parse_finalize;
114
115 gst_element_class_add_static_pad_template (element_class,
116 &gst_dirac_parse_src_template);
117 gst_element_class_add_static_pad_template (element_class,
118 &gst_dirac_parse_sink_template);
119
120 gst_element_class_set_static_metadata (element_class, "Dirac parser",
121 "Codec/Parser/Video", "Parses Dirac streams",
122 "David Schleef <ds@schleef.org>");
123
124 base_parse_class->start = GST_DEBUG_FUNCPTR (gst_dirac_parse_start);
125 base_parse_class->stop = GST_DEBUG_FUNCPTR (gst_dirac_parse_stop);
126 base_parse_class->set_sink_caps =
127 GST_DEBUG_FUNCPTR (gst_dirac_parse_set_sink_caps);
128 base_parse_class->get_sink_caps =
129 GST_DEBUG_FUNCPTR (gst_dirac_parse_get_sink_caps);
130 base_parse_class->handle_frame =
131 GST_DEBUG_FUNCPTR (gst_dirac_parse_handle_frame);
132 base_parse_class->convert = GST_DEBUG_FUNCPTR (gst_dirac_parse_convert);
133 base_parse_class->pre_push_frame =
134 GST_DEBUG_FUNCPTR (gst_dirac_parse_pre_push_frame);
135
136 }
137
138 static void
gst_dirac_parse_init(GstDiracParse * diracparse)139 gst_dirac_parse_init (GstDiracParse * diracparse)
140 {
141 gst_base_parse_set_min_frame_size (GST_BASE_PARSE (diracparse), 13);
142 gst_base_parse_set_pts_interpolation (GST_BASE_PARSE (diracparse), FALSE);
143 GST_PAD_SET_ACCEPT_INTERSECT (GST_BASE_PARSE_SINK_PAD (diracparse));
144 GST_PAD_SET_ACCEPT_TEMPLATE (GST_BASE_PARSE_SINK_PAD (diracparse));
145 }
146
147 void
gst_dirac_parse_set_property(GObject * object,guint property_id,const GValue * value,GParamSpec * pspec)148 gst_dirac_parse_set_property (GObject * object, guint property_id,
149 const GValue * value, GParamSpec * pspec)
150 {
151 g_return_if_fail (GST_IS_DIRAC_PARSE (object));
152
153 switch (property_id) {
154 default:
155 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
156 break;
157 }
158 }
159
160 void
gst_dirac_parse_get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)161 gst_dirac_parse_get_property (GObject * object, guint property_id,
162 GValue * value, GParamSpec * pspec)
163 {
164 g_return_if_fail (GST_IS_DIRAC_PARSE (object));
165
166 switch (property_id) {
167 default:
168 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
169 break;
170 }
171 }
172
173 void
gst_dirac_parse_dispose(GObject * object)174 gst_dirac_parse_dispose (GObject * object)
175 {
176 g_return_if_fail (GST_IS_DIRAC_PARSE (object));
177
178 /* clean up as possible. may be called multiple times */
179
180 G_OBJECT_CLASS (parent_class)->dispose (object);
181 }
182
183 void
gst_dirac_parse_finalize(GObject * object)184 gst_dirac_parse_finalize (GObject * object)
185 {
186 g_return_if_fail (GST_IS_DIRAC_PARSE (object));
187
188 /* clean up object here */
189
190 G_OBJECT_CLASS (parent_class)->finalize (object);
191 }
192
193
194 static gboolean
gst_dirac_parse_start(GstBaseParse * parse)195 gst_dirac_parse_start (GstBaseParse * parse)
196 {
197 GstDiracParse *diracparse = GST_DIRAC_PARSE (parse);
198
199 gst_base_parse_set_min_frame_size (parse, 13);
200
201 diracparse->sent_codec_tag = FALSE;
202
203 return TRUE;
204 }
205
206 static gboolean
gst_dirac_parse_stop(GstBaseParse * parse)207 gst_dirac_parse_stop (GstBaseParse * parse)
208 {
209 return TRUE;
210 }
211
212 static gboolean
gst_dirac_parse_set_sink_caps(GstBaseParse * parse,GstCaps * caps)213 gst_dirac_parse_set_sink_caps (GstBaseParse * parse, GstCaps * caps)
214 {
215 /* Called when sink caps are set */
216 return TRUE;
217 }
218
219 static const gchar *
get_profile_name(int profile)220 get_profile_name (int profile)
221 {
222 switch (profile) {
223 case 0:
224 return "vc2-low-delay";
225 case 1:
226 return "vc2-simple";
227 case 2:
228 return "vc2-main";
229 case 8:
230 return "main";
231 default:
232 break;
233 }
234 return "unknown";
235 }
236
237 static const gchar *
get_level_name(int level)238 get_level_name (int level)
239 {
240 switch (level) {
241 case 0:
242 return "0";
243 case 1:
244 return "1";
245 case 128:
246 return "128";
247 default:
248 break;
249 }
250 /* need to add it to template caps, so return 0 for now */
251 GST_WARNING ("unhandled dirac level %u", level);
252 return "0";
253 }
254
255 static GstFlowReturn
gst_dirac_parse_handle_frame(GstBaseParse * parse,GstBaseParseFrame * frame,gint * skipsize)256 gst_dirac_parse_handle_frame (GstBaseParse * parse,
257 GstBaseParseFrame * frame, gint * skipsize)
258 {
259 int off;
260 guint32 next_header;
261 GstMapInfo map;
262 guint8 *data;
263 gsize size;
264 gboolean have_picture = FALSE;
265 int offset;
266 guint framesize = 0;
267
268 gst_buffer_map (frame->buffer, &map, GST_MAP_READ);
269 data = map.data;
270 size = map.size;
271
272 if (G_UNLIKELY (size < 13)) {
273 *skipsize = 1;
274 goto out;
275 }
276
277 GST_DEBUG ("%" G_GSIZE_FORMAT ": %02x %02x %02x %02x", size, data[0], data[1],
278 data[2], data[3]);
279
280 if (GST_READ_UINT32_BE (data) != 0x42424344) {
281 GstByteReader reader;
282
283 gst_byte_reader_init (&reader, data, size);
284 off = gst_byte_reader_masked_scan_uint32 (&reader, 0xffffffff,
285 0x42424344, 0, size);
286
287 if (off < 0) {
288 *skipsize = size - 3;
289 goto out;
290 }
291
292 GST_LOG_OBJECT (parse, "possible sync at buffer offset %d", off);
293
294 GST_DEBUG ("skipping %d", off);
295 *skipsize = off;
296 goto out;
297 }
298
299 /* have sync, parse chunks */
300
301 offset = 0;
302 while (!have_picture) {
303 GST_DEBUG ("offset %d:", offset);
304
305 if (offset + 13 >= size) {
306 framesize = offset + 13;
307 goto out;
308 }
309
310 GST_DEBUG ("chunk type %02x", data[offset + 4]);
311
312 if (GST_READ_UINT32_BE (data + offset) != 0x42424344) {
313 GST_DEBUG ("bad header");
314 *skipsize = 3;
315 goto out;
316 }
317
318 next_header = GST_READ_UINT32_BE (data + offset + 5);
319 GST_DEBUG ("next_header %d", next_header);
320 if (next_header == 0)
321 next_header = 13;
322
323 if (SCHRO_PARSE_CODE_IS_PICTURE (data[offset + 4])) {
324 have_picture = TRUE;
325 }
326
327 offset += next_header;
328 if (offset >= size) {
329 framesize = offset;
330 goto out;
331 }
332 }
333
334 gst_buffer_unmap (frame->buffer, &map);
335
336 framesize = offset;
337 GST_DEBUG ("framesize %d", framesize);
338
339 g_assert (framesize <= size);
340
341 if (data[4] == SCHRO_PARSE_CODE_SEQUENCE_HEADER) {
342 GstCaps *caps;
343 GstDiracParse *diracparse = GST_DIRAC_PARSE (parse);
344 DiracSequenceHeader sequence_header;
345 int ret;
346
347 ret = dirac_sequence_header_parse (&sequence_header, data + 13, size - 13);
348 if (ret) {
349 memcpy (&diracparse->sequence_header, &sequence_header,
350 sizeof (sequence_header));
351 caps = gst_caps_new_simple ("video/x-dirac",
352 "width", G_TYPE_INT, sequence_header.width,
353 "height", G_TYPE_INT, sequence_header.height,
354 "framerate", GST_TYPE_FRACTION,
355 sequence_header.frame_rate_numerator,
356 sequence_header.frame_rate_denominator,
357 "pixel-aspect-ratio", GST_TYPE_FRACTION,
358 sequence_header.aspect_ratio_numerator,
359 sequence_header.aspect_ratio_denominator,
360 "interlace-mode", G_TYPE_STRING,
361 sequence_header.interlaced ? "interleaved" : "progressive",
362 "profile", G_TYPE_STRING, get_profile_name (sequence_header.profile),
363 "level", G_TYPE_STRING, get_level_name (sequence_header.level), NULL);
364 gst_pad_set_caps (GST_BASE_PARSE_SRC_PAD (parse), caps);
365 gst_caps_unref (caps);
366
367 gst_base_parse_set_frame_rate (parse,
368 sequence_header.frame_rate_numerator,
369 sequence_header.frame_rate_denominator, 0, 0);
370 }
371 }
372
373 gst_base_parse_set_min_frame_size (parse, 13);
374
375 return gst_base_parse_finish_frame (parse, frame, framesize);
376
377 out:
378 gst_buffer_unmap (frame->buffer, &map);
379 if (framesize)
380 gst_base_parse_set_min_frame_size (parse, framesize);
381 return GST_FLOW_OK;
382 }
383
384 static gboolean
gst_dirac_parse_convert(GstBaseParse * parse,GstFormat src_format,gint64 src_value,GstFormat dest_format,gint64 * dest_value)385 gst_dirac_parse_convert (GstBaseParse * parse, GstFormat src_format,
386 gint64 src_value, GstFormat dest_format, gint64 * dest_value)
387 {
388 /* Convert between formats */
389
390 return FALSE;
391 }
392
393 static GstFlowReturn
gst_dirac_parse_pre_push_frame(GstBaseParse * parse,GstBaseParseFrame * frame)394 gst_dirac_parse_pre_push_frame (GstBaseParse * parse, GstBaseParseFrame * frame)
395 {
396 GstDiracParse *diracparse = GST_DIRAC_PARSE (parse);
397
398 if (!diracparse->sent_codec_tag) {
399 GstTagList *taglist;
400 GstCaps *caps;
401
402 /* codec tag */
403 caps = gst_pad_get_current_caps (GST_BASE_PARSE_SRC_PAD (parse));
404 if (G_UNLIKELY (caps == NULL)) {
405 if (GST_PAD_IS_FLUSHING (GST_BASE_PARSE_SRC_PAD (parse))) {
406 GST_INFO_OBJECT (parse, "Src pad is flushing");
407 return GST_FLOW_FLUSHING;
408 } else {
409 GST_INFO_OBJECT (parse, "Src pad is not negotiated!");
410 return GST_FLOW_NOT_NEGOTIATED;
411 }
412 }
413
414 taglist = gst_tag_list_new_empty ();
415 gst_pb_utils_add_codec_description_to_tag_list (taglist,
416 GST_TAG_VIDEO_CODEC, caps);
417 gst_caps_unref (caps);
418
419 gst_base_parse_merge_tags (parse, taglist, GST_TAG_MERGE_REPLACE);
420 gst_tag_list_unref (taglist);
421
422 /* also signals the end of first-frame processing */
423 diracparse->sent_codec_tag = TRUE;
424 }
425
426 return GST_FLOW_OK;
427 }
428
429 static void
remove_fields(GstCaps * caps)430 remove_fields (GstCaps * caps)
431 {
432 guint i, n;
433
434 n = gst_caps_get_size (caps);
435 for (i = 0; i < n; i++) {
436 GstStructure *s = gst_caps_get_structure (caps, i);
437
438 gst_structure_remove_field (s, "parsed");
439 }
440 }
441
442 static GstCaps *
gst_dirac_parse_get_sink_caps(GstBaseParse * parse,GstCaps * filter)443 gst_dirac_parse_get_sink_caps (GstBaseParse * parse, GstCaps * filter)
444 {
445 GstCaps *peercaps, *templ;
446 GstCaps *res;
447
448 templ = gst_pad_get_pad_template_caps (GST_BASE_PARSE_SINK_PAD (parse));
449 if (filter) {
450 GstCaps *fcopy = gst_caps_copy (filter);
451 /* Remove the fields we convert */
452 remove_fields (fcopy);
453 peercaps = gst_pad_peer_query_caps (GST_BASE_PARSE_SRC_PAD (parse), fcopy);
454 gst_caps_unref (fcopy);
455 } else
456 peercaps = gst_pad_peer_query_caps (GST_BASE_PARSE_SRC_PAD (parse), NULL);
457
458 if (peercaps) {
459 /* Remove the parsed field */
460 peercaps = gst_caps_make_writable (peercaps);
461 remove_fields (peercaps);
462
463 res = gst_caps_intersect_full (peercaps, templ, GST_CAPS_INTERSECT_FIRST);
464 gst_caps_unref (peercaps);
465 gst_caps_unref (templ);
466 } else {
467 res = templ;
468 }
469
470 if (filter) {
471 GstCaps *intersection;
472
473 intersection =
474 gst_caps_intersect_full (filter, res, GST_CAPS_INTERSECT_FIRST);
475 gst_caps_unref (res);
476 res = intersection;
477 }
478
479 return res;
480 }
481