• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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