2 * Copyright (C) <2005> Jan Schmidt <jan@fluendo.com>
3 * Copyright (C) <2002> Wim Taymans <wim@fluendo.com>
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., 51 Franklin St, Fifth Floor,
18 * Boston, MA 02110-1301, USA.
25 #include "gstdvdsubdec.h"
26 #include "gstdvdsubparse.h"
29 GST_DEBUG_CATEGORY_STATIC (gst_dvd_sub_dec_debug);
30 #define GST_CAT_DEFAULT (gst_dvd_sub_dec_debug)
32 #define gst_dvd_sub_dec_parent_class parent_class
33 G_DEFINE_TYPE (GstDvdSubDec, gst_dvd_sub_dec, GST_TYPE_ELEMENT);
34 GST_ELEMENT_REGISTER_DEFINE_WITH_CODE (dvdsubdec, "dvdsubdec", GST_RANK_NONE,
35 GST_TYPE_DVD_SUB_DEC, GST_DEBUG_CATEGORY_INIT (gst_dvd_sub_dec_debug,
36 "dvdsubdec", 0, "DVD subtitle decoder"));
38 static gboolean gst_dvd_sub_dec_src_event (GstPad * srcpad, GstObject * parent,
40 static GstFlowReturn gst_dvd_sub_dec_chain (GstPad * pad, GstObject * parent,
43 static gboolean gst_dvd_sub_dec_handle_dvd_event (GstDvdSubDec * dec,
45 static void gst_dvd_sub_dec_finalize (GObject * gobject);
46 static void gst_setup_palette (GstDvdSubDec * dec);
47 static void gst_dvd_sub_dec_merge_title (GstDvdSubDec * dec,
48 GstVideoFrame * frame);
49 static GstClockTime gst_dvd_sub_dec_get_event_delay (GstDvdSubDec * dec);
50 static gboolean gst_dvd_sub_dec_sink_event (GstPad * pad, GstObject * parent,
52 static gboolean gst_dvd_sub_dec_sink_setcaps (GstPad * pad, GstCaps * caps);
54 static GstFlowReturn gst_send_subtitle_frame (GstDvdSubDec * dec,
57 static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src",
60 GST_STATIC_CAPS ("video/x-raw, format = (string) { AYUV, ARGB },"
61 "width = (int) 720, height = (int) 576, framerate = (fraction) 0/1")
64 static GstStaticPadTemplate subtitle_template = GST_STATIC_PAD_TEMPLATE ("sink",
67 GST_STATIC_CAPS ("subpicture/x-dvd")
73 SPU_FORCE_DISPLAY = 0x00,
76 SPU_SET_PALETTE = 0x03,
79 SPU_SET_OFFSETS = 0x06,
84 static const guint32 default_clut[16] = {
85 0xb48080, 0x248080, 0x628080, 0xd78080,
86 0x808080, 0x808080, 0x808080, 0x808080,
87 0x808080, 0x808080, 0x808080, 0x808080,
88 0x808080, 0x808080, 0x808080, 0x808080
91 typedef struct RLE_state
106 gst_dvd_sub_dec_class_init (GstDvdSubDecClass * klass)
108 GObjectClass *gobject_class;
109 GstElementClass *gstelement_class;
111 gobject_class = (GObjectClass *) klass;
112 gstelement_class = (GstElementClass *) klass;
114 gobject_class->finalize = gst_dvd_sub_dec_finalize;
116 gst_element_class_add_static_pad_template (gstelement_class, &src_template);
117 gst_element_class_add_static_pad_template (gstelement_class,
120 gst_element_class_set_static_metadata (gstelement_class,
121 "DVD subtitle decoder", "Codec/Decoder/Video",
122 "Decodes DVD subtitles into AYUV video frames",
123 "Wim Taymans <wim.taymans@gmail.com>, "
124 "Jan Schmidt <thaytan@mad.scientist.com>");
128 gst_dvd_sub_dec_init (GstDvdSubDec * dec)
130 GstPadTemplate *tmpl;
132 dec->sinkpad = gst_pad_new_from_static_template (&subtitle_template, "sink");
133 gst_pad_set_chain_function (dec->sinkpad,
134 GST_DEBUG_FUNCPTR (gst_dvd_sub_dec_chain));
135 gst_pad_set_event_function (dec->sinkpad,
136 GST_DEBUG_FUNCPTR (gst_dvd_sub_dec_sink_event));
137 gst_element_add_pad (GST_ELEMENT (dec), dec->sinkpad);
139 tmpl = gst_static_pad_template_get (&src_template);
140 dec->srcpad = gst_pad_new_from_template (tmpl, "src");
141 gst_pad_set_event_function (dec->srcpad,
142 GST_DEBUG_FUNCPTR (gst_dvd_sub_dec_src_event));
143 gst_pad_use_fixed_caps (dec->srcpad);
144 gst_object_unref (tmpl);
145 gst_element_add_pad (GST_ELEMENT (dec), dec->srcpad);
147 /* FIXME: aren't there more possible sizes? (tpm) */
149 dec->in_height = 576;
151 dec->partialbuf = NULL;
152 dec->have_title = FALSE;
153 dec->parse_pos = NULL;
154 dec->forced_display = FALSE;
155 dec->visible = FALSE;
157 memcpy (dec->current_clut, default_clut, sizeof (guint32) * 16);
159 gst_setup_palette (dec);
162 dec->next_event_ts = GST_CLOCK_TIME_NONE;
164 dec->buf_dirty = TRUE;
165 dec->use_ARGB = FALSE;
169 gst_dvd_sub_dec_finalize (GObject * gobject)
171 GstDvdSubDec *dec = GST_DVD_SUB_DEC (gobject);
173 if (dec->partialbuf) {
174 gst_buffer_unmap (dec->partialbuf, &dec->partialmap);
175 gst_buffer_unref (dec->partialbuf);
176 dec->partialbuf = NULL;
179 G_OBJECT_CLASS (parent_class)->finalize (gobject);
183 gst_dvd_sub_dec_src_event (GstPad * pad, GstObject * parent, GstEvent * event)
185 gboolean res = FALSE;
187 switch (GST_EVENT_TYPE (event)) {
189 res = gst_pad_event_default (pad, parent, event);
197 gst_dvd_sub_dec_get_event_delay (GstDvdSubDec * dec)
201 GstClockTime event_delay;
203 /* If starting a new buffer, follow the first DCSQ ptr */
204 if (dec->parse_pos == dec->partialmap.data) {
205 buf = dec->parse_pos + dec->data_size;
207 buf = dec->parse_pos;
210 ticks = GST_READ_UINT16_BE (buf);
211 event_delay = gst_util_uint64_scale (ticks, 1024 * GST_SECOND, 90000);
213 GST_DEBUG_OBJECT (dec, "returning delay %" GST_TIME_FORMAT " from offset %u",
214 GST_TIME_ARGS (event_delay), (guint) (buf - dec->parse_pos));
220 * Parse the next event time in the current subpicture buffer, stopping
221 * when time advances to the next state.
224 gst_dvd_sub_dec_parse_subpic (GstDvdSubDec * dec)
226 #define PARSE_BYTES_NEEDED(x) if ((buf+(x)) >= end) \
227 { GST_WARNING("Subtitle stream broken parsing %c", *buf); \
228 broken = TRUE; break; }
230 guchar *start = dec->partialmap.data;
233 gboolean broken = FALSE;
234 gboolean last_seq = FALSE;
235 guchar *next_seq = NULL;
236 GstClockTime event_time;
238 /* nothing to do if we finished this buffer already */
239 if (dec->parse_pos == NULL)
242 g_return_if_fail (dec->packet_size >= 4);
244 end = start + dec->packet_size;
245 if (dec->parse_pos == start) {
246 buf = dec->parse_pos + dec->data_size;
248 buf = dec->parse_pos;
251 g_assert (buf >= start && buf < end);
253 /* If the next control sequence is at the current offset, this is
255 next_seq = start + GST_READ_UINT16_BE (buf + 2);
256 last_seq = (next_seq == buf);
259 while ((buf < end) && (!broken)) {
261 case SPU_FORCE_DISPLAY: /* Forced display menu subtitle */
262 dec->forced_display = TRUE;
263 dec->buf_dirty = TRUE;
264 GST_DEBUG_OBJECT (dec, "SPU FORCE_DISPLAY");
267 case SPU_SHOW: /* Show the subtitle in this packet */
269 dec->buf_dirty = TRUE;
270 GST_DEBUG_OBJECT (dec, "SPU SHOW at %" GST_TIME_FORMAT,
271 GST_TIME_ARGS (dec->next_event_ts));
275 /* 02 ff (ff) is the end of the packet, hide the subpicture */
276 dec->visible = FALSE;
277 dec->buf_dirty = TRUE;
279 GST_DEBUG_OBJECT (dec, "SPU HIDE at %" GST_TIME_FORMAT,
280 GST_TIME_ARGS (dec->next_event_ts));
283 case SPU_SET_PALETTE: /* palette */
284 PARSE_BYTES_NEEDED (3);
286 GST_DEBUG_OBJECT (dec, "SPU SET_PALETTE");
288 dec->subtitle_index[3] = buf[1] >> 4;
289 dec->subtitle_index[2] = buf[1] & 0xf;
290 dec->subtitle_index[1] = buf[2] >> 4;
291 dec->subtitle_index[0] = buf[2] & 0xf;
292 gst_setup_palette (dec);
294 dec->buf_dirty = TRUE;
297 case SPU_SET_ALPHA: /* transparency palette */
298 PARSE_BYTES_NEEDED (3);
300 GST_DEBUG_OBJECT (dec, "SPU SET_ALPHA");
302 dec->subtitle_alpha[3] = buf[1] >> 4;
303 dec->subtitle_alpha[2] = buf[1] & 0xf;
304 dec->subtitle_alpha[1] = buf[2] >> 4;
305 dec->subtitle_alpha[0] = buf[2] & 0xf;
306 gst_setup_palette (dec);
308 dec->buf_dirty = TRUE;
311 case SPU_SET_SIZE: /* image coordinates */
312 PARSE_BYTES_NEEDED (7);
314 dec->top = ((buf[4] & 0x3f) << 4) | ((buf[5] & 0xe0) >> 4);
315 dec->left = ((buf[1] & 0x3f) << 4) | ((buf[2] & 0xf0) >> 4);
316 dec->right = ((buf[2] & 0x03) << 8) | buf[3];
317 dec->bottom = ((buf[5] & 0x03) << 8) | buf[6];
319 GST_DEBUG_OBJECT (dec, "SPU SET_SIZE left %d, top %d, right %d, "
320 "bottom %d", dec->left, dec->top, dec->right, dec->bottom);
322 dec->buf_dirty = TRUE;
325 case SPU_SET_OFFSETS: /* image 1 / image 2 offsets */
326 PARSE_BYTES_NEEDED (5);
328 dec->offset[0] = (((guint) buf[1]) << 8) | buf[2];
329 dec->offset[1] = (((guint) buf[3]) << 8) | buf[4];
330 GST_DEBUG_OBJECT (dec, "Offset1 %d, Offset2 %d",
331 dec->offset[0], dec->offset[1]);
333 dec->buf_dirty = TRUE;
340 PARSE_BYTES_NEEDED (3);
342 GST_WARNING_OBJECT (dec, "SPU_WIPE not yet implemented");
344 length = (buf[1] << 8) | (buf[2]);
347 dec->buf_dirty = TRUE;
351 buf = (last_seq) ? end : next_seq;
353 /* Start a new control sequence */
355 guint16 ticks = GST_READ_UINT16_BE (buf);
357 event_time = gst_util_uint64_scale (ticks, 1024 * GST_SECOND, 90000);
359 GST_DEBUG_OBJECT (dec,
360 "Next DCSQ at offset %u, delay %g secs (%d ticks)",
361 (guint) (buf - start),
362 gst_util_guint64_to_gdouble (event_time / GST_SECOND), ticks);
364 dec->parse_pos = buf;
365 if (event_time > 0) {
366 dec->next_event_ts += event_time;
368 GST_LOG_OBJECT (dec, "Exiting parse loop with time %g",
369 gst_guint64_to_gdouble (dec->next_event_ts) /
370 gst_guint64_to_gdouble (GST_SECOND));
375 dec->parse_pos = NULL;
376 dec->next_event_ts = GST_CLOCK_TIME_NONE;
377 GST_LOG_OBJECT (dec, "Finished all cmds. Exiting parse loop");
382 ("Invalid sequence in subtitle packet header (%.2x). Skipping",
385 dec->parse_pos = NULL;
392 gst_get_nibble (guchar * buffer, RLE_state * state)
394 if (state->aligned) {
395 state->next = buffer[state->offset[state->id]++];
397 return state->next >> 4;
400 return state->next & 0xf;
404 /* Premultiply the current lookup table into the "target" cache */
406 gst_setup_palette (GstDvdSubDec * dec)
410 Color_val *target_yuv = dec->palette_cache_yuv;
411 Color_val *target2_yuv = dec->hl_palette_cache_yuv;
412 Color_val *target_rgb = dec->palette_cache_rgb;
413 Color_val *target2_rgb = dec->hl_palette_cache_rgb;
415 for (i = 0; i < 4; i++, target2_yuv++, target_yuv++) {
416 col = dec->current_clut[dec->subtitle_index[i]];
417 target_yuv->Y_R = (col >> 16) & 0xff;
418 target_yuv->V_B = (col >> 8) & 0xff;
419 target_yuv->U_G = col & 0xff;
420 target_yuv->A = dec->subtitle_alpha[i] * 0xff / 0xf;
422 col = dec->current_clut[dec->menu_index[i]];
423 target2_yuv->Y_R = (col >> 16) & 0xff;
424 target2_yuv->V_B = (col >> 8) & 0xff;
425 target2_yuv->U_G = col & 0xff;
426 target2_yuv->A = dec->menu_alpha[i] * 0xff / 0xf;
428 /* If ARGB flag set, then convert YUV palette to RGB */
429 /* Using integer arithmetic */
431 guchar C = target_yuv->Y_R - 16;
432 guchar D = target_yuv->U_G - 128;
433 guchar E = target_yuv->V_B - 128;
435 target_rgb->Y_R = CLAMP (((298 * C + 409 * E + 128) >> 8), 0, 255);
437 CLAMP (((298 * C - 100 * D - 128 * E + 128) >> 8), 0, 255);
438 target_rgb->V_B = CLAMP (((298 * C + 516 * D + 128) >> 8), 0, 255);
439 target_rgb->A = target_yuv->A;
441 C = target2_yuv->Y_R - 16;
442 D = target2_yuv->U_G - 128;
443 E = target2_yuv->V_B - 128;
445 target2_rgb->Y_R = CLAMP (((298 * C + 409 * E + 128) >> 8), 0, 255);
447 CLAMP (((298 * C - 100 * D - 128 * E + 128) >> 8), 0, 255);
448 target2_rgb->V_B = CLAMP (((298 * C + 516 * D + 128) >> 8), 0, 255);
449 target2_rgb->A = target2_yuv->A;
457 gst_get_rle_code (guchar * buffer, RLE_state * state)
461 code = gst_get_nibble (buffer, state);
462 if (code < 0x4) { /* 4 .. f */
463 code = (code << 4) | gst_get_nibble (buffer, state);
464 if (code < 0x10) { /* 1x .. 3x */
465 code = (code << 4) | gst_get_nibble (buffer, state);
466 if (code < 0x40) { /* 04x .. 0fx */
467 code = (code << 4) | gst_get_nibble (buffer, state);
474 #define DRAW_RUN(target,len,c) \
478 for (i = 0; i < (len); i++) { \
479 *(target)++ = (c)->A; \
480 *(target)++ = (c)->Y_R; \
481 *(target)++ = (c)->U_G; \
482 *(target)++ = (c)->V_B; \
485 (target) += 4 * (len); \
490 * This function steps over each run-length segment, drawing
491 * into the YUVA/ARGB buffers as it goes. UV are composited and then output
492 * at half width/height
495 gst_draw_rle_line (GstDvdSubDec * dec, guchar * buffer, RLE_state * state)
497 gint length, colourid;
502 target = state->target;
505 right = dec->right + 1;
509 const Color_val *colour_entry;
511 code = gst_get_rle_code (buffer, state);
515 colour_entry = dec->palette_cache_rgb + colourid;
517 colour_entry = dec->palette_cache_yuv + colourid;
519 /* Length = 0 implies fill to the end of the line */
520 /* Restrict the colour run to the end of the line */
521 if (length == 0 || x + length > right)
524 /* Check if this run of colour touches the highlight region */
525 in_hl = ((x <= state->hl_right) && (x + length) >= state->hl_left);
529 /* Draw to the left of the highlight */
530 if (x <= state->hl_left) {
531 run = MIN (length, state->hl_left - x + 1);
533 DRAW_RUN (target, run, colour_entry);
538 /* Draw across the highlight region */
539 if (x <= state->hl_right) {
540 const Color_val *hl_colour;
542 hl_colour = dec->hl_palette_cache_rgb + colourid;
544 hl_colour = dec->hl_palette_cache_yuv + colourid;
546 run = MIN (length, state->hl_right - x + 1);
548 DRAW_RUN (target, run, hl_colour);
554 /* Draw the rest of the run */
556 DRAW_RUN (target, length, colour_entry);
563 * Decode the RLE subtitle image and blend with the current
567 gst_dvd_sub_dec_merge_title (GstDvdSubDec * dec, GstVideoFrame * frame)
571 guchar *buffer = dec->partialmap.data;
572 gint hl_top, hl_bottom;
577 GST_DEBUG_OBJECT (dec, "Merging subtitle on frame");
579 Y_data = GST_VIDEO_FRAME_PLANE_DATA (frame, 0);
580 Y_stride = GST_VIDEO_FRAME_PLANE_STRIDE (frame, 0);
585 state.offset[0] = dec->offset[0];
586 state.offset[1] = dec->offset[1];
588 /* center the image when display rectangle exceeds the video width */
589 if (dec->in_width <= dec->right) {
590 gint left, disp_width;
592 disp_width = dec->right - dec->left + 1;
593 left = (dec->in_width - disp_width) / 2;
595 dec->right = left + disp_width - 1;
597 /* if it clips to the right, shift it left, but only till zero */
598 if (dec->right >= dec->in_width) {
599 gint shift = dec->right - dec->in_width - 1;
600 if (shift > dec->left)
606 GST_DEBUG_OBJECT (dec, "clipping width to %d,%d",
607 dec->left, dec->in_width - 1);
610 /* for the height, bring it up till it fits as well as it can. We
611 * assume the picture is in the lower part. We should better check where it
612 * is and do something more clever. */
613 if (dec->in_height <= dec->bottom) {
615 /* shift it up, but only till zero */
616 gint shift = dec->bottom - dec->in_height - 1;
617 if (shift > dec->top)
620 dec->bottom -= shift;
622 /* start on even line */
628 GST_DEBUG_OBJECT (dec, "clipping height to %d,%d",
629 dec->top, dec->in_height - 1);
632 if (dec->current_button) {
633 hl_top = dec->hl_top;
634 hl_bottom = dec->hl_bottom;
639 last_y = MIN (dec->bottom, dec->in_height);
642 state.target = Y_data + 4 * dec->left + (y * Y_stride);
644 /* Now draw scanlines until we hit last_y or end of RLE data */
645 for (; ((state.offset[1] < dec->data_size + 2) && (y <= last_y)); y++) {
646 /* Set up to draw the highlight if we're in the right scanlines */
647 if (y > hl_bottom || y < hl_top) {
651 state.hl_left = dec->hl_left;
652 state.hl_right = dec->hl_right;
654 gst_draw_rle_line (dec, buffer, &state);
656 state.target += Y_stride;
658 /* Realign the RLE state for the next line */
660 gst_get_nibble (buffer, &state);
661 state.id = !state.id;
666 gst_send_empty_fill (GstDvdSubDec * dec, GstClockTime ts)
668 if (dec->next_ts < ts) {
669 GST_LOG_OBJECT (dec, "Sending GAP event update to advance time to %"
670 GST_TIME_FORMAT, GST_TIME_ARGS (ts));
672 gst_pad_push_event (dec->srcpad,
673 gst_event_new_gap (dec->next_ts, ts - dec->next_ts));
679 gst_send_subtitle_frame (GstDvdSubDec * dec, GstClockTime end_ts)
686 static GstAllocationParams params = { 0, 3, 0, 0, };
688 g_assert (dec->have_title);
689 g_assert (dec->next_ts <= end_ts);
691 /* Check if we need to redraw the output buffer */
692 if (!dec->buf_dirty) {
698 gst_buffer_new_allocate (NULL, GST_VIDEO_INFO_SIZE (&dec->info), ¶ms);
699 gst_video_frame_map (&frame, &dec->info, out_buf, GST_MAP_READWRITE);
701 data = GST_VIDEO_FRAME_PLANE_DATA (&frame, 0);
703 /* Clear the buffer */
704 /* FIXME - move this into the buffer rendering code */
705 for (y = 0; y < dec->in_height; y++) {
706 guchar *line = data + 4 * dec->in_width * y;
708 for (x = 0; x < dec->in_width; x++) {
710 if (!dec->use_ARGB) {
711 line[1] = 16; /* Y */
712 line[2] = 128; /* U */
713 line[3] = 128; /* V */
724 /* FIXME: do we really want to honour the forced_display flag
725 * for subtitles streans? */
726 if (dec->visible || dec->forced_display) {
727 gst_dvd_sub_dec_merge_title (dec, &frame);
730 gst_video_frame_unmap (&frame);
732 dec->buf_dirty = FALSE;
734 GST_BUFFER_TIMESTAMP (out_buf) = dec->next_ts;
735 if (GST_CLOCK_TIME_IS_VALID (dec->next_event_ts)) {
736 GST_BUFFER_DURATION (out_buf) = GST_CLOCK_DIFF (dec->next_ts,
739 GST_BUFFER_DURATION (out_buf) = GST_CLOCK_TIME_NONE;
742 GST_DEBUG_OBJECT (dec, "Sending subtitle buffer with ts %"
743 GST_TIME_FORMAT ", dur %" G_GINT64_FORMAT,
744 GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (out_buf)),
745 GST_BUFFER_DURATION (out_buf));
747 flow = gst_pad_push (dec->srcpad, out_buf);
750 dec->next_ts = end_ts;
754 /* Walk time forward, processing any subtitle events as needed. */
756 gst_dvd_sub_dec_advance_time (GstDvdSubDec * dec, GstClockTime new_ts)
758 GstFlowReturn ret = GST_FLOW_OK;
760 GST_LOG_OBJECT (dec, "Advancing time to %" GST_TIME_FORMAT,
761 GST_TIME_ARGS (new_ts));
763 if (!dec->have_title) {
764 gst_send_empty_fill (dec, new_ts);
768 while (dec->next_ts < new_ts) {
769 GstClockTime next_ts = new_ts;
771 if (GST_CLOCK_TIME_IS_VALID (dec->next_event_ts) &&
772 dec->next_event_ts < next_ts) {
773 /* We might need to process the subtitle cmd queue */
774 next_ts = dec->next_event_ts;
778 * Now, either output a filler or a frame spanning
779 * dec->next_ts to next_ts
781 if (dec->visible || dec->forced_display) {
782 ret = gst_send_subtitle_frame (dec, next_ts);
784 gst_send_empty_fill (dec, next_ts);
788 * and then process some subtitle cmds if we need
790 if (next_ts == dec->next_event_ts)
791 gst_dvd_sub_dec_parse_subpic (dec);
798 gst_dvd_sub_dec_chain (GstPad * pad, GstObject * parent, GstBuffer * buf)
800 GstFlowReturn ret = GST_FLOW_OK;
805 dec = GST_DVD_SUB_DEC (parent);
807 GST_DEBUG_OBJECT (dec, "Have buffer of size %" G_GSIZE_FORMAT ", ts %"
808 GST_TIME_FORMAT ", dur %" G_GINT64_FORMAT, gst_buffer_get_size (buf),
809 GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)), GST_BUFFER_DURATION (buf));
811 if (GST_BUFFER_TIMESTAMP_IS_VALID (buf)) {
812 if (!GST_CLOCK_TIME_IS_VALID (dec->next_ts)) {
813 dec->next_ts = GST_BUFFER_TIMESTAMP (buf);
816 /* Move time forward to the start of the new buffer */
817 ret = gst_dvd_sub_dec_advance_time (dec, GST_BUFFER_TIMESTAMP (buf));
820 if (dec->have_title) {
821 gst_buffer_unmap (dec->partialbuf, &dec->partialmap);
822 gst_buffer_unref (dec->partialbuf);
823 dec->partialbuf = NULL;
824 dec->have_title = FALSE;
827 GST_DEBUG_OBJECT (dec, "Got subtitle buffer, pts %" GST_TIME_FORMAT,
828 GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)));
830 /* deal with partial frame from previous buffer */
831 if (dec->partialbuf) {
832 gst_buffer_unmap (dec->partialbuf, &dec->partialmap);
833 dec->partialbuf = gst_buffer_append (dec->partialbuf, buf);
835 dec->partialbuf = buf;
838 gst_buffer_map (dec->partialbuf, &dec->partialmap, GST_MAP_READ);
840 data = dec->partialmap.data;
841 size = dec->partialmap.size;
844 dec->packet_size = GST_READ_UINT16_BE (data);
846 if (dec->packet_size == size) {
847 GST_LOG_OBJECT (dec, "Subtitle packet size %d, current size %ld",
848 dec->packet_size, size);
850 dec->data_size = GST_READ_UINT16_BE (data + 2);
852 /* Reset parameters for a new subtitle buffer */
853 dec->parse_pos = data;
854 dec->forced_display = FALSE;
855 dec->visible = FALSE;
857 dec->have_title = TRUE;
858 dec->next_event_ts = GST_BUFFER_TIMESTAMP (dec->partialbuf);
860 if (!GST_CLOCK_TIME_IS_VALID (dec->next_event_ts))
861 dec->next_event_ts = dec->next_ts;
863 dec->next_event_ts += gst_dvd_sub_dec_get_event_delay (dec);
871 gst_dvd_sub_dec_sink_setcaps (GstPad * pad, GstCaps * caps)
873 GstDvdSubDec *dec = GST_DVD_SUB_DEC (gst_pad_get_parent (pad));
874 gboolean ret = FALSE;
875 GstCaps *out_caps = NULL, *peer_caps = NULL;
877 GST_DEBUG_OBJECT (dec, "setcaps called with %" GST_PTR_FORMAT, caps);
879 out_caps = gst_caps_new_simple ("video/x-raw",
880 "format", G_TYPE_STRING, "AYUV",
881 "width", G_TYPE_INT, dec->in_width,
882 "height", G_TYPE_INT, dec->in_height,
883 "framerate", GST_TYPE_FRACTION, 0, 1, NULL);
885 peer_caps = gst_pad_get_allowed_caps (dec->srcpad);
886 if (G_LIKELY (peer_caps)) {
889 n = gst_caps_get_size (peer_caps);
890 GST_DEBUG_OBJECT (dec, "peer allowed caps (%u structure(s)) are %"
891 GST_PTR_FORMAT, n, peer_caps);
893 for (i = 0; i < n; i++) {
894 GstStructure *s = gst_caps_get_structure (peer_caps, i);
895 /* Check if the peer pad support ARGB format, if yes change caps */
896 if (gst_structure_has_name (s, "video/x-raw")) {
897 GstCaps *downstream_caps;
898 gst_caps_unref (out_caps);
899 GST_DEBUG_OBJECT (dec, "trying with ARGB");
901 out_caps = gst_caps_new_simple ("video/x-raw",
902 "format", G_TYPE_STRING, "ARGB",
903 "width", G_TYPE_INT, dec->in_width,
904 "height", G_TYPE_INT, dec->in_height,
905 "framerate", GST_TYPE_FRACTION, 0, 1, NULL);
907 downstream_caps = gst_pad_peer_query_caps (dec->srcpad, NULL);
908 if (gst_caps_can_intersect (downstream_caps, out_caps)) {
909 gst_caps_unref (downstream_caps);
910 GST_DEBUG_OBJECT (dec, "peer accepted ARGB");
911 /* If ARGB format then set the flag */
912 dec->use_ARGB = TRUE;
915 gst_caps_unref (downstream_caps);
918 gst_caps_unref (peer_caps);
920 GST_DEBUG_OBJECT (dec, "setting caps downstream to %" GST_PTR_FORMAT,
922 if (gst_pad_set_caps (dec->srcpad, out_caps)) {
923 gst_video_info_from_caps (&dec->info, out_caps);
925 GST_WARNING_OBJECT (dec, "failed setting downstream caps");
926 gst_caps_unref (out_caps);
930 gst_caps_unref (out_caps);
934 gst_object_unref (dec);
939 gst_dvd_sub_dec_sink_event (GstPad * pad, GstObject * parent, GstEvent * event)
941 GstDvdSubDec *dec = GST_DVD_SUB_DEC (parent);
942 gboolean ret = FALSE;
944 GST_LOG_OBJECT (dec, "%s event", GST_EVENT_TYPE_NAME (event));
946 switch (GST_EVENT_TYPE (event)) {
951 gst_event_parse_caps (event, &caps);
952 ret = gst_dvd_sub_dec_sink_setcaps (pad, caps);
953 gst_event_unref (event);
956 case GST_EVENT_CUSTOM_DOWNSTREAM:{
958 if (gst_event_has_name (event, "application/x-gst-dvd")) {
959 const GstStructure *s = gst_event_get_structure (event);
960 GstClockTime ts = GST_CLOCK_TIME_NONE;
962 if (gst_structure_get_clock_time (s, "ts", &ts)
963 && GST_CLOCK_TIME_IS_VALID (ts))
964 gst_dvd_sub_dec_advance_time (dec, ts);
966 if (gst_dvd_sub_dec_handle_dvd_event (dec, event)) {
967 /* gst_dvd_sub_dec_advance_time (dec, dec->next_ts + GST_SECOND / 30.0); */
968 gst_event_unref (event);
974 ret = gst_pad_event_default (pad, parent, event);
979 GstClockTime start, duration;
981 gst_event_parse_gap (event, &start, &duration);
982 if (GST_CLOCK_TIME_IS_VALID (start)) {
983 if (GST_CLOCK_TIME_IS_VALID (duration))
985 /* we do not expect another buffer until after gap,
986 * so that is our position now */
987 GST_DEBUG_OBJECT (dec, "Got GAP event, advancing time from %"
988 GST_TIME_FORMAT " to %" GST_TIME_FORMAT,
989 GST_TIME_ARGS (dec->next_ts), GST_TIME_ARGS (start));
991 gst_dvd_sub_dec_advance_time (dec, start);
993 GST_WARNING_OBJECT (dec, "Got GAP event with invalid position");
996 gst_event_unref (event);
1000 case GST_EVENT_SEGMENT:
1004 gst_event_copy_segment (event, &seg);
1008 /* Turn off forced highlight display */
1009 dec->forced_display = 0;
1010 dec->current_button = 0;
1012 if (dec->partialbuf) {
1013 gst_buffer_unmap (dec->partialbuf, &dec->partialmap);
1014 gst_buffer_unref (dec->partialbuf);
1015 dec->partialbuf = NULL;
1016 dec->have_title = FALSE;
1019 if (GST_CLOCK_TIME_IS_VALID (seg.time))
1020 dec->next_ts = seg.time;
1022 dec->next_ts = GST_CLOCK_TIME_NONE;
1024 GST_DEBUG_OBJECT (dec, "Got newsegment, new time = %"
1025 GST_TIME_FORMAT, GST_TIME_ARGS (dec->next_ts));
1027 ret = gst_pad_event_default (pad, parent, event);
1031 case GST_EVENT_FLUSH_STOP:{
1032 /* Turn off forced highlight display */
1033 dec->forced_display = 0;
1034 dec->current_button = 0;
1036 if (dec->partialbuf) {
1037 gst_buffer_unmap (dec->partialbuf, &dec->partialmap);
1038 gst_buffer_unref (dec->partialbuf);
1039 dec->partialbuf = NULL;
1040 dec->have_title = FALSE;
1043 ret = gst_pad_event_default (pad, parent, event);
1047 ret = gst_pad_event_default (pad, parent, event);
1055 gst_dvd_sub_dec_handle_dvd_event (GstDvdSubDec * dec, GstEvent * event)
1057 GstStructure *structure;
1058 const gchar *event_name;
1060 structure = (GstStructure *) gst_event_get_structure (event);
1062 if (structure == NULL)
1065 event_name = gst_structure_get_string (structure, "event");
1067 GST_LOG_OBJECT (dec,
1068 "DVD event %s with timestamp %" G_GINT64_FORMAT " on sub pad",
1069 GST_STR_NULL (event_name), GST_EVENT_TIMESTAMP (event));
1071 if (event_name == NULL)
1074 if (strcmp (event_name, "dvd-spu-highlight") == 0) {
1076 gint palette, sx, sy, ex, ey;
1079 /* Details for the highlight region to display */
1080 if (!gst_structure_get_int (structure, "button", &button) ||
1081 !gst_structure_get_int (structure, "palette", &palette) ||
1082 !gst_structure_get_int (structure, "sx", &sx) ||
1083 !gst_structure_get_int (structure, "sy", &sy) ||
1084 !gst_structure_get_int (structure, "ex", &ex) ||
1085 !gst_structure_get_int (structure, "ey", &ey)) {
1086 GST_ERROR_OBJECT (dec, "Invalid dvd-spu-highlight event received");
1089 dec->current_button = button;
1093 dec->hl_bottom = ey;
1094 for (i = 0; i < 4; i++) {
1095 dec->menu_alpha[i] = ((guint32) (palette) >> (i * 4)) & 0x0f;
1096 dec->menu_index[i] = ((guint32) (palette) >> (16 + (i * 4))) & 0x0f;
1099 GST_DEBUG_OBJECT (dec, "New button activated highlight=(%d,%d) to (%d,%d) "
1100 "palette 0x%x", sx, sy, ex, ey, palette);
1101 gst_setup_palette (dec);
1103 dec->buf_dirty = TRUE;
1104 } else if (strcmp (event_name, "dvd-spu-clut-change") == 0) {
1105 /* Take a copy of the colour table */
1110 GST_LOG_OBJECT (dec, "New colour table received");
1111 for (i = 0; i < 16; i++) {
1112 g_snprintf (name, sizeof (name), "clut%02d", i);
1113 if (!gst_structure_get_int (structure, name, &value)) {
1114 GST_ERROR_OBJECT (dec, "dvd-spu-clut-change event did not "
1115 "contain %s field", name);
1118 dec->current_clut[i] = (guint32) (value);
1121 gst_setup_palette (dec);
1123 dec->buf_dirty = TRUE;
1124 } else if (strcmp (event_name, "dvd-spu-stream-change") == 0
1125 || strcmp (event_name, "dvd-spu-reset-highlight") == 0) {
1126 /* Turn off forced highlight display */
1127 dec->current_button = 0;
1129 GST_LOG_OBJECT (dec, "Clearing button state");
1130 dec->buf_dirty = TRUE;
1131 } else if (strcmp (event_name, "dvd-spu-still-frame") == 0) {
1132 /* Handle a still frame */
1133 GST_LOG_OBJECT (dec, "Received still frame notification");
1142 /* Ignore all other unknown events */
1143 GST_LOG_OBJECT (dec, "Ignoring other custom event %" GST_PTR_FORMAT,
1150 plugin_init (GstPlugin * plugin)
1152 gboolean ret = FALSE;
1154 ret |= GST_ELEMENT_REGISTER (dvdsubdec, plugin);
1155 ret |= GST_ELEMENT_REGISTER (dvdsubparse, plugin);
1160 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
1163 "DVD subtitle parser and decoder", plugin_init,
1164 VERSION, GST_LICENSE, GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN);