2 * Copyright (C) 2003 Martin Soto <martinsoto@users.sourceforge.net>
4 * dxr3spusink.h: Subpicture sink for em8300 based cards.
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Library General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Library General Public License for more details.
16 * You should have received a copy of the GNU Library General Public
17 * License along with this library; if not, write to the
18 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19 * Boston, MA 02111-1307, USA.
29 #include <sys/ioctl.h>
31 #include <linux/em8300.h>
33 #include <gst/gst-i18n-plugin.h>
36 #include "dxr3spusink.h"
37 #include "dxr3marshal.h"
39 #include "dxr3common.h"
41 /* Dxr3SpuSink signals and args */
57 static GstStaticPadTemplate dxr3spusink_sink_factory =
58 GST_STATIC_PAD_TEMPLATE ("sink",
64 static void dxr3spusink_class_init (Dxr3SpuSinkClass * klass);
65 static void dxr3spusink_base_init (Dxr3SpuSinkClass * klass);
66 static void dxr3spusink_init (Dxr3SpuSink * dxr3spusink);
68 static void dxr3spusink_set_property (GObject * object,
69 guint prop_id, const GValue * value, GParamSpec * pspec);
70 static void dxr3spusink_get_property (GObject * object,
71 guint prop_id, GValue * value, GParamSpec * pspec);
73 static gboolean dxr3spusink_open (Dxr3SpuSink * sink);
74 static void dxr3spusink_close (Dxr3SpuSink * sink);
75 static void dxr3spusink_set_clock (GstElement * element, GstClock * clock);
77 static gboolean dxr3spusink_handle_event (GstPad * pad, GstEvent * event);
78 static void dxr3spusink_chain (GstPad * pad, GstData * _data);
80 static GstStateChangeReturn dxr3spusink_change_state (GstElement * element,
81 GstStateChange transition);
83 /* static void dxr3spusink_wait (Dxr3SpuSink *sink, */
84 /* GstClockTime time); */
86 static void dxr3spusink_set_clut (Dxr3SpuSink * sink, const guint32 * clut);
87 static void dxr3spusink_highlight_on (Dxr3SpuSink * sink,
89 unsigned sx, unsigned sy, unsigned ex, unsigned ey, unsigned pts);
90 static void dxr3spusink_highlight_off (Dxr3SpuSink * sink);
92 static void dxr3spusink_flushed (Dxr3SpuSink * sink);
95 static GstElementClass *parent_class = NULL;
96 static guint dxr3spusink_signals[LAST_SIGNAL] = { 0 };
100 dxr3spusink_get_type (void)
102 static GType dxr3spusink_type = 0;
104 if (!dxr3spusink_type) {
105 static const GTypeInfo dxr3spusink_info = {
106 sizeof (Dxr3SpuSinkClass),
107 (GBaseInitFunc) dxr3spusink_base_init,
109 (GClassInitFunc) dxr3spusink_class_init,
112 sizeof (Dxr3SpuSink),
114 (GInstanceInitFunc) dxr3spusink_init,
117 dxr3spusink_type = g_type_register_static (GST_TYPE_ELEMENT,
118 "Dxr3SpuSink", &dxr3spusink_info, 0);
120 return dxr3spusink_type;
125 dxr3spusink_base_init (Dxr3SpuSinkClass * klass)
127 GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
129 gst_element_class_add_pad_template (element_class,
130 gst_static_pad_template_get (&dxr3spusink_sink_factory));
131 gst_element_class_set_details_simple (element_class,
132 "dxr3/Hollywood+ mpeg decoder board subpicture element", "Sink/Video",
133 "Feeds subpicture information to Sigma Designs em8300 based boards",
134 "Martin Soto <martinsoto@users.sourceforge.net>");
138 dxr3spusink_class_init (Dxr3SpuSinkClass * klass)
140 GObjectClass *gobject_class;
141 GstElementClass *gstelement_class;
143 gobject_class = (GObjectClass *) klass;
144 gstelement_class = (GstElementClass *) klass;
146 parent_class = g_type_class_peek_parent (klass);
148 dxr3spusink_signals[SET_CLUT_SIGNAL] =
149 g_signal_new ("set-clut",
150 G_TYPE_FROM_CLASS (klass),
151 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
152 G_STRUCT_OFFSET (Dxr3SpuSinkClass, set_clut),
153 NULL, NULL, dxr3_marshal_VOID__POINTER, G_TYPE_NONE, 1, G_TYPE_POINTER);
155 dxr3spusink_signals[HIGHLIGHT_ON_SIGNAL] =
156 g_signal_new ("highlight-on",
157 G_TYPE_FROM_CLASS (klass),
158 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
159 G_STRUCT_OFFSET (Dxr3SpuSinkClass, highlight_on),
161 dxr3_marshal_VOID__UINT_UINT_UINT_UINT_UINT_UINT,
163 G_TYPE_UINT, G_TYPE_UINT, G_TYPE_UINT, G_TYPE_UINT,
164 G_TYPE_UINT, G_TYPE_UINT);
166 dxr3spusink_signals[HIGHLIGHT_OFF_SIGNAL] =
167 g_signal_new ("highlight-off",
168 G_TYPE_FROM_CLASS (klass),
169 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
170 G_STRUCT_OFFSET (Dxr3SpuSinkClass, highlight_off),
171 NULL, NULL, dxr3_marshal_VOID__VOID, G_TYPE_NONE, 0);
173 dxr3spusink_signals[SIGNAL_FLUSHED] =
174 g_signal_new ("flushed", G_TYPE_FROM_CLASS (klass),
176 G_STRUCT_OFFSET (Dxr3SpuSinkClass, flushed),
177 NULL, NULL, dxr3_marshal_VOID__VOID, G_TYPE_NONE, 0);
179 klass->set_clut = dxr3spusink_set_clut;
180 klass->highlight_on = dxr3spusink_highlight_on;
181 klass->highlight_off = dxr3spusink_highlight_off;
182 klass->flushed = dxr3spusink_flushed;
184 gobject_class->set_property = dxr3spusink_set_property;
185 gobject_class->get_property = dxr3spusink_get_property;
187 gstelement_class->change_state = dxr3spusink_change_state;
188 gstelement_class->set_clock = dxr3spusink_set_clock;
193 dxr3spusink_init (Dxr3SpuSink * sink)
197 pad = gst_pad_new_from_static_template (&dxr3spusink_sink_factory, "sink");
198 gst_element_add_pad (GST_ELEMENT (sink), pad);
199 gst_pad_set_chain_function (pad, dxr3spusink_chain);
201 GST_OBJECT_FLAG_SET (GST_ELEMENT (sink), GST_ELEMENT_EVENT_AWARE);
203 sink->card_number = 0;
205 sink->spu_filename = NULL;
207 sink->control_filename = NULL;
208 sink->control_fd = -1;
215 dxr3spusink_set_property (GObject * object, guint prop_id,
216 const GValue * value, GParamSpec * pspec)
220 sink = DXR3SPUSINK (object);
230 dxr3spusink_get_property (GObject * object, guint prop_id,
231 GValue * value, GParamSpec * pspec)
235 g_return_if_fail (GST_IS_DXR3SPUSINK (object));
237 sink = DXR3SPUSINK (object);
241 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
248 dxr3spusink_open (Dxr3SpuSink * sink)
250 g_return_val_if_fail (!GST_OBJECT_FLAG_IS_SET (sink, DXR3SPUSINK_OPEN),
253 /* Compute the name of the spu device file. */
254 sink->spu_filename = g_strdup_printf ("/dev/em8300_sp-%d", sink->card_number);
256 sink->spu_fd = open (sink->spu_filename, O_WRONLY);
257 if (sink->spu_fd < 0) {
258 GST_ELEMENT_ERROR (sink, RESOURCE, OPEN_WRITE,
259 (_("Could not open spu device \"%s\" for writing."),
260 sink->spu_filename), GST_ERROR_SYSTEM);
264 /* Open the control device. */
265 sink->control_filename = g_strdup_printf ("/dev/em8300-%d",
268 sink->control_fd = open (sink->control_filename, O_WRONLY);
269 if (sink->control_fd < 0) {
270 GST_ELEMENT_ERROR (sink, RESOURCE, OPEN_WRITE,
271 (_("Could not open control device \"%s\" for writing."),
272 sink->control_filename), GST_ERROR_SYSTEM);
276 GST_OBJECT_FLAG_SET (sink, DXR3SPUSINK_OPEN);
283 dxr3spusink_close (Dxr3SpuSink * sink)
285 g_return_if_fail (GST_OBJECT_FLAG_IS_SET (sink, DXR3SPUSINK_OPEN));
287 if (close (sink->spu_fd) != 0) {
288 GST_ELEMENT_ERROR (sink, RESOURCE, CLOSE,
289 (_("Could not close spu device \"%s\"."), sink->spu_filename),
294 if (close (sink->control_fd) != 0) {
295 GST_ELEMENT_ERROR (sink, RESOURCE, CLOSE,
296 (_("Could not close control device \"%s\"."), sink->control_filename),
301 GST_OBJECT_FLAG_UNSET (sink, DXR3SPUSINK_OPEN);
303 free (sink->spu_filename);
304 sink->spu_filename = NULL;
309 dxr3spusink_set_clock (GstElement * element, GstClock * clock)
311 Dxr3SpuSink *src = DXR3SPUSINK (element);
318 dxr3spusink_handle_event (GstPad * pad, GstEvent * event)
323 sink = DXR3SPUSINK (gst_pad_get_parent (pad));
325 type = event ? GST_EVENT_TYPE (event) : GST_EVENT_UNKNOWN;
328 case GST_EVENT_FLUSH:
329 if (sink->control_fd >= 0) {
332 subdevice = EM8300_SUBDEVICE_SUBPICTURE;
333 ioctl (sink->control_fd, EM8300_IOCTL_FLUSH, &subdevice);
335 /* FIXME: There should be a nicer way to do this, but I tried
336 everything and nothing else seems to really reset the video
338 /* dxr3spusink_close (sink); */
339 /* dxr3spusink_open (sink); */
341 /* Report the flush operation. */
342 g_signal_emit (G_OBJECT (sink), dxr3spusink_signals[SIGNAL_FLUSHED], 0);
346 gst_pad_event_default (pad, event);
355 dxr3spusink_chain (GstPad * pad, GstData * _data)
357 GstBuffer *buf = GST_BUFFER (_data);
359 gint bytes_written = 0;
361 g_return_if_fail (pad != NULL);
362 g_return_if_fail (GST_IS_PAD (pad));
363 g_return_if_fail (buf != NULL);
365 sink = DXR3SPUSINK (gst_pad_get_parent (pad));
367 if (GST_IS_EVENT (buf)) {
368 dxr3spusink_handle_event (pad, GST_EVENT (buf));
372 if (GST_OBJECT_FLAG_IS_SET (sink, DXR3SPUSINK_OPEN)) {
373 /* If we have PTS information for the SPU unit, register it now.
374 The card needs the PTS to be written *before* the actual data. */
375 if (GST_BUFFER_TIMESTAMP (buf) != GST_CLOCK_TIME_NONE) {
376 guint pts = (guint) GSTTIME_TO_MPEGTIME (GST_BUFFER_TIMESTAMP (buf));
378 ioctl (sink->spu_fd, EM8300_IOCTL_SPU_SETPTS, &pts);
381 bytes_written = write (sink->spu_fd, GST_BUFFER_DATA (buf),
382 GST_BUFFER_SIZE (buf));
383 if (bytes_written < GST_BUFFER_SIZE (buf)) {
384 fprintf (stderr, "dxr3spusink: Warning: %d bytes should be written,"
385 " only %d bytes written\n", GST_BUFFER_SIZE (buf), bytes_written);
389 gst_buffer_unref (buf);
393 static GstStateChangeReturn
394 dxr3spusink_change_state (GstElement * element, GstStateChange transition)
396 g_return_val_if_fail (GST_IS_DXR3SPUSINK (element), GST_STATE_CHANGE_FAILURE);
398 switch (transition) {
399 case GST_STATE_CHANGE_NULL_TO_READY:
400 if (!GST_OBJECT_FLAG_IS_SET (element, DXR3SPUSINK_OPEN)) {
401 if (!dxr3spusink_open (DXR3SPUSINK (element))) {
402 return GST_STATE_CHANGE_FAILURE;
406 case GST_STATE_CHANGE_READY_TO_PAUSED:
408 case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
410 case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
412 case GST_STATE_CHANGE_PAUSED_TO_READY:
414 case GST_STATE_CHANGE_READY_TO_NULL:
415 if (GST_OBJECT_FLAG_IS_SET (element, DXR3SPUSINK_OPEN)) {
416 dxr3spusink_close (DXR3SPUSINK (element));
421 if (GST_ELEMENT_CLASS (parent_class)->change_state) {
422 return GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
425 return GST_STATE_CHANGE_SUCCESS;
432 * Make the sink wait the specified amount of time.
435 dxr3spusink_wait (Dxr3SpuSink * sink, GstClockTime time)
438 GstClockTimeDiff jitter;
440 GstClockTime current_time = gst_clock_get_time (sink->clock);
442 id = gst_clock_new_single_shot_id (sink->clock, current_time + time);
443 ret = gst_clock_id_wait (id, &jitter);
444 gst_clock_id_free (id);
449 * dxr3spusink_set_clut:
451 * Set a new SPU color lookup table (clut) in the dxr3 card.
454 dxr3spusink_set_clut (Dxr3SpuSink * sink, const guint32 * clut)
456 guint32 clut_fixed[16];
459 /* Fix the byte order of the table. */
460 for (i = 0; i < 16; i++) {
461 clut_fixed[i] = GUINT32_TO_LE (clut[i]);
464 if (ioctl (sink->spu_fd, EM8300_IOCTL_SPU_SETPALETTE, clut_fixed))
465 fprintf (stderr, "dxr3spusink: failed to set CLUT (%s)\n",
471 dxr3spusink_highlight_on (Dxr3SpuSink * sink, unsigned palette,
472 unsigned sx, unsigned sy, unsigned ex, unsigned ey, unsigned pts)
476 btn.color = palette >> 16;
477 btn.contrast = palette;
483 if (ioctl (sink->spu_fd, EM8300_IOCTL_SPU_BUTTON, &btn)) {
484 fprintf (stderr, "dxr3spusink: failed to set spu button (%s)\n",
491 dxr3spusink_highlight_off (Dxr3SpuSink * sink)
493 if (ioctl (sink->spu_fd, EM8300_IOCTL_SPU_BUTTON, NULL)) {
494 fprintf (stderr, "dxr3spusink: failed to set spu button (%s)\n",
501 * dxr3spusink_flushed:
503 * Default do nothing implementation for the "flushed" signal. The
504 * "flushed" signal will be fired right after flushing the hardware
505 * queues due to a received flush event
508 dxr3spusink_flushed (Dxr3SpuSink * sink)