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