1 /* GStreamer CDXA sync strippper / VCD parser
2 * Copyright (C) 2004 Ronald Bultje <rbultje@ronald.bitfreak.net>
3 * Copyright (C) 2008 Tim-Philipp Müller <tim centricular net>
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Library General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Library General Public License for more details.
15 * You should have received a copy of the GNU Library General Public
16 * License along with this library; if not, write to the
17 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 * Boston, MA 02111-1307, USA.
27 #include "gstvcdparse.h"
29 GST_DEBUG_CATEGORY_EXTERN (vcdparse_debug);
30 #define GST_CAT_DEFAULT vcdparse_debug
32 static gboolean gst_vcd_parse_sink_event (GstPad * pad, GstEvent * event);
33 static gboolean gst_vcd_parse_src_event (GstPad * pad, GstEvent * event);
34 static gboolean gst_vcd_parse_src_query (GstPad * pad, GstQuery * query);
35 static GstFlowReturn gst_vcd_parse_chain (GstPad * pad, GstBuffer * buf);
36 static GstStateChangeReturn gst_vcd_parse_change_state (GstElement * element,
37 GstStateChange transition);
39 static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE ("sink",
42 GST_STATIC_CAPS ("video/x-vcd")
45 static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src",
48 GST_STATIC_CAPS ("video/mpeg, systemstream = (boolean) TRUE")
51 GST_BOILERPLATE (GstVcdParse, gst_vcd_parse, GstElement, GST_TYPE_ELEMENT);
54 gst_vcd_parse_base_init (gpointer klass)
56 GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
58 gst_element_class_add_static_pad_template (element_class, &sink_factory);
59 gst_element_class_add_static_pad_template (element_class, &src_factory);
61 gst_element_class_set_details_simple (element_class, "(S)VCD stream parser",
62 "Codec/Parser", "Strip (S)VCD stream from its sync headers",
63 "Tim-Philipp Müller <tim centricular net>, "
64 "Ronald Bultje <rbultje@ronald.bitfreak.net>");
68 gst_vcd_parse_class_init (GstVcdParseClass * klass)
70 GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
72 element_class->change_state = GST_DEBUG_FUNCPTR (gst_vcd_parse_change_state);
76 gst_vcd_parse_init (GstVcdParse * vcd, GstVcdParseClass * klass)
78 vcd->sinkpad = gst_pad_new_from_static_template (&sink_factory, "sink");
79 gst_pad_set_chain_function (vcd->sinkpad,
80 GST_DEBUG_FUNCPTR (gst_vcd_parse_chain));
81 gst_pad_set_event_function (vcd->sinkpad,
82 GST_DEBUG_FUNCPTR (gst_vcd_parse_sink_event));
83 gst_element_add_pad (GST_ELEMENT (vcd), vcd->sinkpad);
85 vcd->srcpad = gst_pad_new_from_static_template (&src_factory, "src");
86 gst_pad_set_event_function (vcd->srcpad,
87 GST_DEBUG_FUNCPTR (gst_vcd_parse_src_event));
88 gst_pad_set_query_function (vcd->srcpad,
89 GST_DEBUG_FUNCPTR (gst_vcd_parse_src_query));
90 gst_pad_use_fixed_caps (vcd->srcpad);
91 gst_pad_set_caps (vcd->srcpad,
92 gst_static_pad_template_get_caps (&src_factory));
93 gst_element_add_pad (GST_ELEMENT (vcd), vcd->srcpad);
96 /* These conversion functions assume there's no junk between sectors */
99 gst_vcd_parse_get_out_offset (gint64 in_offset)
101 gint64 out_offset, chunknum, rest;
106 if (G_UNLIKELY (in_offset < -1)) {
107 GST_WARNING ("unexpected/invalid in_offset %" G_GINT64_FORMAT, in_offset);
111 chunknum = in_offset / GST_CDXA_SECTOR_SIZE;
112 rest = in_offset % GST_CDXA_SECTOR_SIZE;
114 out_offset = chunknum * GST_CDXA_DATA_SIZE;
115 if (rest > GST_CDXA_HEADER_SIZE) {
116 if (rest >= GST_CDXA_HEADER_SIZE + GST_CDXA_DATA_SIZE)
117 out_offset += GST_CDXA_DATA_SIZE;
119 out_offset += rest - GST_CDXA_HEADER_SIZE;
122 GST_LOG ("transformed in_offset %" G_GINT64_FORMAT " to out_offset %"
123 G_GINT64_FORMAT, in_offset, out_offset);
129 gst_vcd_parse_get_in_offset (gint64 out_offset)
131 gint64 in_offset, chunknum, rest;
133 if (out_offset == -1)
136 if (G_UNLIKELY (out_offset < -1)) {
137 GST_WARNING ("unexpected/invalid out_offset %" G_GINT64_FORMAT, out_offset);
141 chunknum = out_offset / GST_CDXA_DATA_SIZE;
142 rest = out_offset % GST_CDXA_DATA_SIZE;
144 in_offset = chunknum * GST_CDXA_SECTOR_SIZE;
146 in_offset += GST_CDXA_HEADER_SIZE + rest;
148 GST_LOG ("transformed out_offset %" G_GINT64_FORMAT " to in_offset %"
149 G_GINT64_FORMAT, out_offset, in_offset);
155 gst_vcd_parse_src_query (GstPad * pad, GstQuery * query)
157 GstVcdParse *vcd = GST_VCD_PARSE (gst_pad_get_parent (pad));
158 gboolean res = FALSE;
160 switch (GST_QUERY_TYPE (query)) {
161 case GST_QUERY_DURATION:{
165 /* first try upstream */
166 if (!gst_pad_query_default (pad, query))
169 /* we can only handle BYTES */
170 gst_query_parse_duration (query, &format, &dur);
171 if (format != GST_FORMAT_BYTES)
174 gst_query_set_duration (query, GST_FORMAT_BYTES,
175 gst_vcd_parse_get_out_offset (dur));
180 case GST_QUERY_POSITION:{
184 /* first try upstream */
185 if (!gst_pad_query_default (pad, query))
188 /* we can only handle BYTES */
189 gst_query_parse_position (query, &format, &pos);
190 if (format != GST_FORMAT_BYTES)
193 gst_query_set_position (query, GST_FORMAT_BYTES,
194 gst_vcd_parse_get_out_offset (pos));
200 res = gst_pad_query_default (pad, query);
204 gst_object_unref (vcd);
209 gst_vcd_parse_sink_event (GstPad * pad, GstEvent * event)
211 GstVcdParse *vcd = GST_VCD_PARSE (gst_pad_get_parent (pad));
214 switch (GST_EVENT_TYPE (event)) {
215 case GST_EVENT_NEWSEGMENT:{
218 gdouble rate, applied_rate;
219 gint64 start, stop, position;
221 gst_event_parse_new_segment_full (event, &update, &rate, &applied_rate,
222 &format, &start, &stop, &position);
224 if (format == GST_FORMAT_BYTES) {
225 gst_event_unref (event);
226 event = gst_event_new_new_segment_full (update, rate, applied_rate,
227 GST_FORMAT_BYTES, gst_vcd_parse_get_out_offset (start),
228 gst_vcd_parse_get_out_offset (stop), position);
230 GST_WARNING_OBJECT (vcd, "newsegment event in non-byte format");
232 res = gst_pad_event_default (pad, event);
235 case GST_EVENT_FLUSH_START:
236 gst_adapter_clear (vcd->adapter);
239 res = gst_pad_event_default (pad, event);
243 gst_object_unref (vcd);
248 gst_vcd_parse_src_event (GstPad * pad, GstEvent * event)
250 GstVcdParse *vcd = GST_VCD_PARSE (gst_pad_get_parent (pad));
253 switch (GST_EVENT_TYPE (event)) {
254 case GST_EVENT_SEEK:{
255 GstSeekType start_type, stop_type;
261 gst_event_parse_seek (event, &rate, &format, &flags, &start_type,
262 &start, &stop_type, &stop);
264 if (format == GST_FORMAT_BYTES) {
265 gst_event_unref (event);
266 if (start_type != GST_SEEK_TYPE_NONE)
267 start = gst_vcd_parse_get_in_offset (start);
268 if (stop_type != GST_SEEK_TYPE_NONE)
269 stop = gst_vcd_parse_get_in_offset (stop);
270 event = gst_event_new_seek (rate, GST_FORMAT_BYTES, flags, start_type,
271 start, stop_type, stop);
273 GST_WARNING_OBJECT (vcd, "seek event in non-byte format");
275 res = gst_pad_event_default (pad, event);
279 res = gst_pad_event_default (pad, event);
283 gst_object_unref (vcd);
287 /* -1 = no sync (discard buffer),
288 * otherwise offset indicates sync point in buffer */
290 gst_vcd_parse_sync (const guint8 * data, guint size)
292 const guint8 sync_marker[12] = { 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
293 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00
298 if (memcmp (data, sync_marker, 12) == 0)
309 gst_vcd_parse_chain (GstPad * pad, GstBuffer * buf)
311 GstVcdParse *vcd = GST_VCD_PARSE (GST_PAD_PARENT (pad));
312 GstFlowReturn flow = GST_FLOW_OK;
314 gst_adapter_push (vcd->adapter, buf);
317 while (gst_adapter_available (vcd->adapter) >= GST_CDXA_SECTOR_SIZE) {
319 guint8 header[4 + 8];
322 /* find sync (we could peek any size though really) */
323 data = gst_adapter_peek (vcd->adapter, GST_CDXA_SECTOR_SIZE);
324 sync_offset = gst_vcd_parse_sync (data, GST_CDXA_SECTOR_SIZE);
325 GST_LOG_OBJECT (vcd, "sync offset = %d", sync_offset);
327 if (sync_offset < 0) {
328 gst_adapter_flush (vcd->adapter, GST_CDXA_SECTOR_SIZE - 12);
329 continue; /* try again */
332 gst_adapter_flush (vcd->adapter, sync_offset);
334 if (gst_adapter_available (vcd->adapter) < GST_CDXA_SECTOR_SIZE) {
335 GST_LOG_OBJECT (vcd, "not enough data in adapter, waiting for more");
339 GST_LOG_OBJECT (vcd, "have full sector");
341 /* have one sector: a sector is 2352 bytes long and is composed of:
343 * +-------------------------------------------------------+
344 * ! sync ! header ! subheader ! data ... ! edc !
345 * ! 12 bytes ! 4 bytes ! 8 bytes ! 2324 bytes ! 4 bytes !
346 * +-------------------------------------------------------+
348 * We strip the data out of it and send it to the srcpad.
350 * sync : 00 FF FF FF FF FF FF FF FF FF FF 00
351 * header : hour minute second mode
352 * sub-header : track channel sub_mode coding repeat (4 bytes)
356 /* Skip CDXA header and edc footer, only keep data in the middle */
357 gst_adapter_copy (vcd->adapter, header, 12, sizeof (header));
358 gst_adapter_flush (vcd->adapter, GST_CDXA_HEADER_SIZE);
359 buf = gst_adapter_take_buffer (vcd->adapter, GST_CDXA_DATA_SIZE);
360 gst_adapter_flush (vcd->adapter, 4);
362 /* we could probably do something clever to keep track of buffer offsets */
363 buf = gst_buffer_make_metadata_writable (buf);
364 GST_BUFFER_OFFSET (buf) = GST_BUFFER_OFFSET_NONE;
365 GST_BUFFER_TIMESTAMP (buf) = GST_CLOCK_TIME_NONE;
366 gst_buffer_set_caps (buf, GST_PAD_CAPS (vcd->srcpad));
368 flow = gst_pad_push (vcd->srcpad, buf);
371 if (G_UNLIKELY (flow != GST_FLOW_OK)) {
372 GST_DEBUG_OBJECT (vcd, "flow: %s", gst_flow_get_name (flow));
380 static GstStateChangeReturn
381 gst_vcd_parse_change_state (GstElement * element, GstStateChange transition)
383 GstStateChangeReturn res = GST_STATE_CHANGE_SUCCESS;
384 GstVcdParse *vcd = GST_VCD_PARSE (element);
386 switch (transition) {
387 case GST_STATE_CHANGE_READY_TO_PAUSED:
388 vcd->adapter = gst_adapter_new ();
394 res = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
396 switch (transition) {
397 case GST_STATE_CHANGE_PAUSED_TO_READY:
398 case GST_STATE_CHANGE_READY_TO_NULL:
400 g_object_unref (vcd->adapter);