2 * Copyright (C) 2010 David Schleef <ds@schleef.org>
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.
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.
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., 59 Temple Place - Suite 330,
17 * Boston, MA 02111-1307, USA.
20 * SECTION:element-gstsdidemux
22 * The gstsdidemux element does FIXME stuff.
25 * <title>Example launch line</title>
27 * gst-launch -v fakesrc ! gstsdidemux ! FIXME ! fakesink
29 * FIXME Describe what the pipeline does.
40 #include "gstsdidemux.h"
45 static void gst_sdi_demux_set_property (GObject * object,
46 guint property_id, const GValue * value, GParamSpec * pspec);
47 static void gst_sdi_demux_get_property (GObject * object,
48 guint property_id, GValue * value, GParamSpec * pspec);
49 static void gst_sdi_demux_dispose (GObject * object);
50 static void gst_sdi_demux_finalize (GObject * object);
52 static GstStateChangeReturn
53 gst_sdi_demux_change_state (GstElement * element, GstStateChange transition);
54 static GstFlowReturn gst_sdi_demux_chain (GstPad * pad, GstBuffer * buffer);
55 static gboolean gst_sdi_demux_sink_event (GstPad * pad, GstEvent * event);
56 static gboolean gst_sdi_demux_src_event (GstPad * pad, GstEvent * event);
57 static GstCaps *gst_sdi_demux_src_getcaps (GstPad * pad);
67 #define GST_VIDEO_CAPS_NTSC(fourcc) \
68 "video/x-raw-yuv,format=(fourcc)" fourcc ",width=720,height=480," \
69 "framerate=30000/1001,interlaced=TRUE,pixel-aspect-ratio=10/11," \
70 "chroma-site=mpeg2,color-matrix=sdtv"
71 #define GST_VIDEO_CAPS_NTSC_WIDE(fourcc) \
72 "video/x-raw-yuv,format=(fourcc)" fourcc ",width=720,height=480," \
73 "framerate=30000/1001,interlaced=TRUE,pixel-aspect-ratio=40/33," \
74 "chroma-site=mpeg2,color-matrix=sdtv"
75 #define GST_VIDEO_CAPS_PAL(fourcc) \
76 "video/x-raw-yuv,format=(fourcc)" fourcc ",width=720,height=576," \
77 "framerate=25/1,interlaced=TRUE,pixel-aspect-ratio=12/11," \
78 "chroma-site=mpeg2,color-matrix=sdtv"
79 #define GST_VIDEO_CAPS_PAL_WIDE(fourcc) \
80 "video/x-raw-yuv,format=(fourcc)" fourcc ",width=720,height=576," \
81 "framerate=25/1,interlaced=TRUE,pixel-aspect-ratio=16/11," \
82 "chroma-site=mpeg2,color-matrix=sdtv"
84 static GstStaticPadTemplate gst_sdi_demux_sink_template =
85 GST_STATIC_PAD_TEMPLATE ("sink",
88 GST_STATIC_CAPS ("application/x-raw-sdi")
91 static GstStaticPadTemplate gst_sdi_demux_src_template =
92 GST_STATIC_PAD_TEMPLATE ("src",
95 GST_STATIC_CAPS (GST_VIDEO_CAPS_NTSC ("UYVY") ";"
96 GST_VIDEO_CAPS_PAL ("UYVY"))
99 /* class initialization */
101 GST_BOILERPLATE (GstSdiDemux, gst_sdi_demux, GstElement, GST_TYPE_ELEMENT);
104 gst_sdi_demux_base_init (gpointer g_class)
106 GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
108 gst_element_class_add_static_pad_template (element_class,
109 &gst_sdi_demux_src_template);
110 gst_element_class_add_static_pad_template (element_class,
111 &gst_sdi_demux_sink_template);
113 gst_element_class_set_details_simple (element_class,
116 "Demultiplex SDI streams into raw audio and video",
117 "David Schleef <ds@schleef.org>");
121 gst_sdi_demux_class_init (GstSdiDemuxClass * klass)
123 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
124 GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
126 gobject_class->set_property = gst_sdi_demux_set_property;
127 gobject_class->get_property = gst_sdi_demux_get_property;
128 gobject_class->dispose = gst_sdi_demux_dispose;
129 gobject_class->finalize = gst_sdi_demux_finalize;
131 element_class->change_state =
132 GST_DEBUG_FUNCPTR (gst_sdi_demux_change_state);
137 gst_sdi_demux_init (GstSdiDemux * sdidemux, GstSdiDemuxClass * sdidemux_class)
141 gst_pad_new_from_static_template (&gst_sdi_demux_sink_template, "sink");
142 gst_pad_set_event_function (sdidemux->sinkpad,
143 GST_DEBUG_FUNCPTR (gst_sdi_demux_sink_event));
144 gst_pad_set_chain_function (sdidemux->sinkpad,
145 GST_DEBUG_FUNCPTR (gst_sdi_demux_chain));
146 gst_element_add_pad (GST_ELEMENT (sdidemux), sdidemux->sinkpad);
149 gst_pad_new_from_static_template (&gst_sdi_demux_src_template, "src");
150 gst_pad_set_event_function (sdidemux->srcpad,
151 GST_DEBUG_FUNCPTR (gst_sdi_demux_src_event));
152 gst_pad_set_getcaps_function (sdidemux->srcpad,
153 GST_DEBUG_FUNCPTR (gst_sdi_demux_src_getcaps));
154 gst_element_add_pad (GST_ELEMENT (sdidemux), sdidemux->srcpad);
160 gst_sdi_demux_set_property (GObject * object, guint property_id,
161 const GValue * value, GParamSpec * pspec)
163 g_return_if_fail (GST_IS_SDI_DEMUX (object));
165 switch (property_id) {
167 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
173 gst_sdi_demux_get_property (GObject * object, guint property_id,
174 GValue * value, GParamSpec * pspec)
176 g_return_if_fail (GST_IS_SDI_DEMUX (object));
178 switch (property_id) {
180 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
186 gst_sdi_demux_dispose (GObject * object)
188 g_return_if_fail (GST_IS_SDI_DEMUX (object));
190 /* clean up as possible. may be called multiple times */
192 G_OBJECT_CLASS (parent_class)->dispose (object);
196 gst_sdi_demux_finalize (GObject * object)
198 g_return_if_fail (GST_IS_SDI_DEMUX (object));
200 /* clean up object here */
202 G_OBJECT_CLASS (parent_class)->finalize (object);
206 static GstStateChangeReturn
207 gst_sdi_demux_change_state (GstElement * element, GstStateChange transition)
210 return GST_STATE_CHANGE_SUCCESS;
214 gst_sdi_demux_src_getcaps (GstPad * pad)
216 return gst_caps_from_string (GST_VIDEO_CAPS_NTSC ("UYVY"));
220 gst_sdi_demux_get_output_buffer (GstSdiDemux * sdidemux)
222 sdidemux->output_buffer =
223 gst_buffer_new_and_alloc (720 * sdidemux->format->active_lines * 2);
224 gst_buffer_set_caps (sdidemux->output_buffer,
225 gst_caps_from_string (GST_VIDEO_CAPS_PAL ("UYVY")));
226 GST_BUFFER_TIMESTAMP (sdidemux->output_buffer) =
227 GST_SECOND * sdidemux->frame_number;
228 sdidemux->frame_number++;
232 get_word10 (guint8 * ptr)
236 a = (((ptr[0] >> 2) | (ptr[1] << 6)) & 0xff) << 24;
237 a |= (((ptr[1] >> 4) | (ptr[2] << 4)) & 0xff) << 16;
238 a |= (((ptr[2] >> 6) | (ptr[3] << 2)) & 0xff) << 8;
245 line10_copy (guint8 * dest, guint8 * src, int n)
249 for (i = 0; i < n; i++) {
250 a = get_word10 (src);
251 GST_WRITE_UINT32_BE (dest, a);
260 copy_line (GstSdiDemux * sdidemux, guint8 * line)
263 GstFlowReturn ret = GST_FLOW_OK;
264 GstSdiFormat *format = sdidemux->format;
266 output_data = GST_BUFFER_DATA (sdidemux->output_buffer);
268 /* line is one less than the video line */
269 if (sdidemux->line >= format->start0 - 1 &&
270 sdidemux->line < format->start0 - 1 + format->active_lines / 2) {
272 memcpy (output_data + 720 * 2 * ((sdidemux->line -
273 (format->start0 - 1)) * 2 + (!format->tff)),
274 line + (format->width - 720) * 2, 720 * 2);
276 line10_copy (output_data + 720 * 2 * ((sdidemux->line -
277 (format->start0 - 1)) * 2 + (!format->tff)),
278 line + (format->width - 720) / 2 * 5, 720 / 2);
281 if (sdidemux->line >= format->start1 - 1 &&
282 sdidemux->line < format->start1 - 1 + format->active_lines / 2) {
284 memcpy (output_data + 720 * 2 * ((sdidemux->line -
285 (format->start1 - 1)) * 2 + (format->tff)),
286 line + (format->width - 720) * 2, 720 * 2);
288 line10_copy (output_data + 720 * 2 * ((sdidemux->line -
289 (format->start1 - 1)) * 2 + (format->tff)),
290 line + (format->width - 720) / 2 * 5, 720 / 2);
294 sdidemux->offset = 0;
296 if (sdidemux->line == format->lines) {
297 ret = gst_pad_push (sdidemux->srcpad, sdidemux->output_buffer);
298 gst_sdi_demux_get_output_buffer (sdidemux);
305 #define SDI_IS_SYNC(a) (((a)&0xffffff80) == 0xff000080)
306 #define SDI_SYNC_F(a) (((a)>>6)&1)
307 #define SDI_SYNC_V(a) (((a)>>5)&1)
308 #define SDI_SYNC_H(a) (((a)>>4)&1)
310 GstSdiFormat sd_ntsc = { 525, 480, 858, 20, 283, 0 };
311 GstSdiFormat sd_pal = { 625, 576, 864, 23, 336, 1 };
314 gst_sdi_demux_chain (GstPad * pad, GstBuffer * buffer)
316 GstSdiDemux *sdidemux;
318 guint8 *data = GST_BUFFER_DATA (buffer);
319 int size = GST_BUFFER_SIZE (buffer);
320 GstFlowReturn ret = GST_FLOW_OK;
321 GstSdiFormat *format;
323 sdidemux = GST_SDI_DEMUX (gst_pad_get_parent (pad));
324 sdidemux->format = &sd_pal;
325 format = sdidemux->format;
327 GST_DEBUG_OBJECT (sdidemux, "chain");
329 if (GST_BUFFER_IS_DISCONT (buffer)) {
330 sdidemux->have_hsync = FALSE;
331 sdidemux->have_vsync = FALSE;
334 if (!sdidemux->have_hsync) {
336 for (offset = 0; offset < size; offset += 4) {
337 guint32 sync = READ_UINT32_BE (data + offset);
338 GST_ERROR ("sync value %08x", sync);
339 if (SDI_IS_SYNC (sync) && SDI_SYNC_H (sync)) {
340 sdidemux->have_hsync = TRUE;
342 sdidemux->offset = 0;
347 for (offset = 0; offset < size; offset += 5) {
348 guint32 sync = get_word10 (data + offset);
349 //GST_ERROR("sync value %08x", sync);
350 if (SDI_IS_SYNC (sync) && SDI_SYNC_H (sync)) {
351 sdidemux->have_hsync = TRUE;
353 sdidemux->offset = 0;
358 if (!sdidemux->have_hsync) {
359 GST_ERROR ("no sync");
364 if (sdidemux->output_buffer == NULL) {
365 gst_sdi_demux_get_output_buffer (sdidemux);
368 if (sdidemux->offset) {
371 /* second half of a line */
372 n = MIN (size - offset, format->width * 2 - sdidemux->offset);
374 memcpy (sdidemux->stored_line + sdidemux->offset, data + offset, n);
377 sdidemux->offset += n;
379 if (sdidemux->offset == format->width * 2) {
381 GST_READ_UINT32_BE (data + offset + (format->width - 720 - 2) * 2);
383 //GST_ERROR("%08x", sync);
384 if (!sdidemux->have_vsync) {
385 //GST_ERROR("%08x", GST_READ_UINT32_BE(data+offset));
386 if (SDI_IS_SYNC (sync) && !SDI_SYNC_F (sync) &&
387 SDI_SYNC_F (sdidemux->last_sync)) {
388 sdidemux->have_vsync = TRUE;
393 ret = copy_line (sdidemux, sdidemux->stored_line);
395 sdidemux->last_sync = sync;
399 while (size - offset >= format->width * 2) {
401 GST_READ_UINT32_BE (data + offset + (format->width - 720 - 2) * 2);
403 //GST_ERROR("%08x", sync);
404 if (!sdidemux->have_vsync) {
405 if (SDI_IS_SYNC (sync) && !SDI_SYNC_F (sync) &&
406 SDI_SYNC_F (sdidemux->last_sync)) {
407 sdidemux->have_vsync = TRUE;
412 ret = copy_line (sdidemux, data + offset);
413 offset += format->width * 2;
415 sdidemux->last_sync = sync;
418 if (size - offset > 0) {
419 memcpy (sdidemux->stored_line, data + offset, size - offset);
420 sdidemux->offset = size - offset;
423 if (sdidemux->offset) {
426 /* second half of a line */
427 n = MIN (size - offset, format->width / 2 * 5 - sdidemux->offset);
429 memcpy (sdidemux->stored_line + sdidemux->offset, data + offset, n);
432 sdidemux->offset += n;
434 if (sdidemux->offset == (format->width / 2) * 5) {
436 get_word10 (data + offset + ((format->width - 720 - 2) / 2) * 5);
438 if (!sdidemux->have_vsync) {
439 //GST_ERROR("%08x", GST_READ_UINT32_BE(data+offset));
440 if (SDI_IS_SYNC (sync) && !SDI_SYNC_F (sync) &&
441 SDI_SYNC_F (sdidemux->last_sync)) {
442 sdidemux->have_vsync = TRUE;
447 ret = copy_line (sdidemux, sdidemux->stored_line);
449 sdidemux->last_sync = sync;
453 while (size - offset >= format->width / 2 * 5) {
455 get_word10 (data + offset + ((format->width - 720 - 2) / 2) * 5);
457 //GST_ERROR("%08x", sync);
458 if (!sdidemux->have_vsync) {
459 if (SDI_IS_SYNC (sync) && !SDI_SYNC_F (sync) &&
460 SDI_SYNC_F (sdidemux->last_sync)) {
461 sdidemux->have_vsync = TRUE;
466 ret = copy_line (sdidemux, data + offset);
467 offset += (format->width / 2) * 5;
469 sdidemux->last_sync = sync;
472 if (size - offset > 0) {
473 memcpy (sdidemux->stored_line, data + offset, size - offset);
474 sdidemux->offset = size - offset;
479 gst_buffer_unref (buffer);
480 gst_object_unref (sdidemux);
485 gst_sdi_demux_sink_event (GstPad * pad, GstEvent * event)
488 GstSdiDemux *sdidemux;
490 sdidemux = GST_SDI_DEMUX (gst_pad_get_parent (pad));
492 GST_DEBUG_OBJECT (sdidemux, "event");
494 switch (GST_EVENT_TYPE (event)) {
495 case GST_EVENT_FLUSH_START:
496 res = gst_pad_push_event (sdidemux->srcpad, event);
498 case GST_EVENT_FLUSH_STOP:
499 res = gst_pad_push_event (sdidemux->srcpad, event);
501 case GST_EVENT_NEWSEGMENT:
502 res = gst_pad_push_event (sdidemux->srcpad, event);
505 res = gst_pad_push_event (sdidemux->srcpad, event);
508 res = gst_pad_push_event (sdidemux->srcpad, event);
512 gst_object_unref (sdidemux);
517 gst_sdi_demux_src_event (GstPad * pad, GstEvent * event)
520 GstSdiDemux *sdidemux;
522 sdidemux = GST_SDI_DEMUX (gst_pad_get_parent (pad));
524 GST_DEBUG_OBJECT (sdidemux, "event");
526 switch (GST_EVENT_TYPE (event)) {
528 res = gst_pad_push_event (sdidemux->sinkpad, event);
531 res = gst_pad_push_event (sdidemux->sinkpad, event);
535 gst_object_unref (sdidemux);