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